From a1d40eada45d928d3e4a2e8a3f30c1ad27a04f93 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Fri, 1 Dec 2023 03:04:13 +0000 Subject: [PATCH] single quote --- .github/dependabot.yml | 2 +- config.js | 96 +- dev/accountHelper.mjs | 8 +- dev/archiveTest.mjs | 7 +- dev/checkAccounts.mjs | 18 +- dev/createAccounts.mjs | 4 +- dev/findProMatches.mjs | 16 +- dev/generateFakeRatings.mjs | 6 +- dev/keyTest.mjs | 4 +- dev/loginTest.mjs | 12 +- dev/metaParse.mjs | 4 +- dev/rePatch.mjs | 8 +- dev/teamElo.mjs | 6 +- dev/teamMatch.mjs | 6 +- dev/wordcount.mjs | 12 +- docker-compose.ci.yml | 8 +- docker-compose.yml | 8 +- index.js | 12 +- package.json | 3 +- processors/createParsedDataBlob.js | 46 +- processors/performanceOthers.js | 8 +- processors/populate.js | 50 +- processors/processAllPlayers.js | 4 +- processors/processDraftTimings.js | 4 +- processors/processExpand.js | 138 +- processors/processMetadata.js | 2 +- processors/processParsedData.js | 2 +- processors/processTeamfights.js | 24 +- processors/processUploadProps.js | 4 +- routes/api.js | 60 +- routes/generateOperationId.js | 13 +- routes/keyManagement.js | 52 +- routes/requests/importParams.js | 8 +- routes/requests/queryParams/heroParams.js | 8 +- routes/requests/queryParams/leagueParams.js | 8 +- routes/requests/queryParams/matchParams.js | 46 +- routes/requests/queryParams/playerParams.js | 164 +- routes/requests/queryParams/scenarioParams.js | 8 +- routes/requests/queryParams/teamParams.js | 8 +- .../responses/properties/commonProperties.js | 72 +- .../schemas/hero/HeroDurationsResponse.js | 16 +- .../hero/HeroItemPopularityResponse.js | 36 +- .../schemas/hero/HeroMatchupsResponse.js | 14 +- .../schemas/hero/HeroObjectResponse.js | 16 +- .../schemas/hero/HeroStatsResponse.js | 252 +-- .../schemas/importResponseSchemas.js | 8 +- .../schemas/match/MatchObjectResponse.js | 34 +- .../responses/schemas/match/MatchResponse.js | 758 ++++---- .../schemas/match/ParsedMatchesResponse.js | 6 +- .../schemas/match/PublicMatchesResponse.js | 32 +- .../miscellaneous/BenchmarksResponse.js | 94 +- .../miscellaneous/DistributionsResponse.js | 226 +-- .../miscellaneous/LeagueObjectResponse.js | 22 +- .../schemas/miscellaneous/MetadataResponse.js | 8 +- .../schemas/miscellaneous/RankingsResponse.js | 64 +- .../schemas/miscellaneous/RecordsResponse.js | 10 +- .../ScenarioItemTimingsResponse.js | 22 +- .../ScenarioLaneRolesResponse.js | 20 +- .../miscellaneous/ScenarioMiscResponse.js | 22 +- .../schemas/miscellaneous/SchemaResponse.js | 16 +- .../schemas/miscellaneous/SearchResponse.js | 18 +- .../schemas/player/PlayerCountsResponse.js | 28 +- .../schemas/player/PlayerHeroesResponse.js | 36 +- .../schemas/player/PlayerMatchesResponse.js | 44 +- .../schemas/player/PlayerObjectResponse.js | 64 +- .../schemas/player/PlayerPeersResponse.js | 62 +- .../schemas/player/PlayerProsResponse.js | 114 +- .../schemas/player/PlayerRankingsResponse.js | 18 +- .../schemas/player/PlayerRatingsResponse.js | 18 +- .../player/PlayerRecentMatchesResponse.js | 82 +- .../schemas/player/PlayerTotalsResponse.js | 16 +- .../schemas/player/PlayerWardMapResponse.js | 12 +- .../schemas/player/PlayerWinLossResponse.js | 12 +- .../schemas/player/PlayerWordCloudResponse.js | 12 +- .../schemas/player/PlayersByRankResponse.js | 16 +- .../schemas/player/PlayersResponse.js | 78 +- .../schemas/team/TeamHeroesResponse.js | 14 +- .../schemas/team/TeamMatchObjectResponse.js | 32 +- .../schemas/team/TeamObjectResponse.js | 28 +- .../schemas/team/TeamPlayersResponse.js | 18 +- routes/spec.js | 1613 +++++++++-------- scripts/convertSpec.js | 6 +- store/archive.js | 112 +- store/buildMatch.js | 69 +- store/buildSets.js | 20 +- store/buildStatus.js | 82 +- store/cacheFunctions.js | 6 +- store/cassandra.js | 12 +- store/db.js | 10 +- store/elasticsearch.js | 10 +- store/queries.js | 317 ++-- store/queue.js | 18 +- store/redis.js | 8 +- store/search.js | 8 +- store/searchES.js | 12 +- svc/apiadmin.js | 106 +- svc/autofullhistory.js | 8 +- svc/backupscanner.js | 28 +- svc/benchmarks.js | 18 +- svc/buildsets.js | 8 +- svc/cassandraDelete.js | 46 +- svc/cosmetics.js | 20 +- svc/counts.js | 68 +- svc/distributions.js | 22 +- svc/fullhistory.js | 46 +- svc/gcdata.js | 10 +- svc/heroes.js | 16 +- svc/herostats.js | 24 +- svc/items.js | 14 +- svc/leagues.js | 22 +- svc/livegames.js | 22 +- svc/migrater.js | 10 +- svc/mmr.js | 16 +- svc/monitor.js | 34 +- svc/parser.js | 34 +- svc/profiler.js | 12 +- svc/proplayers.js | 12 +- svc/proxy.js | 14 +- svc/repatch.js | 16 +- svc/retriever.js | 247 ++- svc/scanner.js | 42 +- svc/scenarios.js | 30 +- svc/scenariosCleanup.js | 26 +- svc/syncSubs.js | 24 +- svc/teams.js | 20 +- svc/web.js | 222 +-- test/test.js | 308 ++-- util/compute.js | 32 +- util/filter.js | 2 +- util/filterDeps.js | 34 +- util/getGcData.js | 36 +- util/scenariosUtil.js | 46 +- util/utility.js | 142 +- 133 files changed, 3773 insertions(+), 3734 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f36e1c3a8..e464af9bf 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,7 +1,7 @@ version: 2 updates: - package-ecosystem: npm - directory: "/" + directory: '/' schedule: interval: daily open-pull-requests-limit: 10 diff --git a/config.js b/config.js index 01c77adec..f14333af6 100755 --- a/config.js +++ b/config.js @@ -1,47 +1,47 @@ /** * File managing configuration for the application * */ -const dotenv = require("dotenv"); -const fs = require("fs"); +const dotenv = require('dotenv'); +const fs = require('fs'); -if (fs.existsSync(".env")) { +if (fs.existsSync('.env')) { dotenv.config(); } const defaults = { - STEAM_API_KEY: "", // for API reqs, in worker - STEAM_USER: "", // for getting replay salt/profile data, in retriever - STEAM_PASS: "", - ROLE: "", // for specifying the file that should be run when entry point is invoked - GROUP: "", // for specifying the group of apps that should be run when entry point is invoked - START_SEQ_NUM: "", // truthy: use sequence number stored in redis, else: use approximate value from live API - PROVIDER: "", // The cloud provider used by the application (determines how environment data is downloaded) - STEAM_ACCOUNT_DATA: "", // The URL to read Steam account data from - NODE_ENV: "development", - FRONTEND_PORT: "5000", - RETRIEVER_PORT: "5100", - PARSER_PORT: "5200", - PROXY_PORT: "5300", - ROOT_URL: "http://localhost:5000", // base url to redirect to after steam oauth login - RETRIEVER_HOST: "localhost:5100", // Comma separated list of retriever hosts (access to Dota 2 GC data) - GCDATA_RETRIEVER_HOST: "", // Comma separated list of retriever hosts dedicated for gcdata job - PARSER_HOST: "http://localhost:5600", // host of the parse server - UI_HOST: "", // The host of the UI, target of /logout and /return - PROXY_URLS: "", // comma separated list of proxy urls to use - STEAM_API_HOST: "api.steampowered.com", // comma separated list of hosts to fetch Steam API data from - POSTGRES_URL: "postgresql://postgres:postgres@localhost/yasp", // connection string for PostgreSQL - POSTGRES_TEST_URL: "postgresql://postgres:postgres@localhost/yasp_test", - READONLY_POSTGRES_URL: "postgresql://readonly:readonly@localhost/yasp", // readonly connection string for PostgreSQL - REDIS_URL: "redis://127.0.0.1:6379/0", // connection string for Redis - REDIS_TEST_URL: "redis://127.0.0.1:6379/1", - CASSANDRA_URL: "cassandra://localhost/yasp", // connection string for Cassandra - CASSANDRA_TEST_URL: "cassandra://localhost/yasp_test", - ELASTICSEARCH_URL: "localhost:9200", - INIT_POSTGRES_HOST: "localhost", - INIT_CASSANDRA_HOST: "localhost", - RETRIEVER_SECRET: "", // string to use as shared secret with retriever/parser - SESSION_SECRET: "secret to encrypt cookies with", // string to encrypt cookies - COOKIE_DOMAIN: "", // domain to use for the cookie. Use e.g. '.opendota.com' to share cookie across subdomains + STEAM_API_KEY: '', // for API reqs, in worker + STEAM_USER: '', // for getting replay salt/profile data, in retriever + STEAM_PASS: '', + ROLE: '', // for specifying the file that should be run when entry point is invoked + GROUP: '', // for specifying the group of apps that should be run when entry point is invoked + START_SEQ_NUM: '', // truthy: use sequence number stored in redis, else: use approximate value from live API + PROVIDER: '', // The cloud provider used by the application (determines how environment data is downloaded) + STEAM_ACCOUNT_DATA: '', // The URL to read Steam account data from + NODE_ENV: 'development', + FRONTEND_PORT: '5000', + RETRIEVER_PORT: '5100', + PARSER_PORT: '5200', + PROXY_PORT: '5300', + ROOT_URL: 'http://localhost:5000', // base url to redirect to after steam oauth login + RETRIEVER_HOST: 'localhost:5100', // Comma separated list of retriever hosts (access to Dota 2 GC data) + GCDATA_RETRIEVER_HOST: '', // Comma separated list of retriever hosts dedicated for gcdata job + PARSER_HOST: 'http://localhost:5600', // host of the parse server + UI_HOST: '', // The host of the UI, target of /logout and /return + PROXY_URLS: '', // comma separated list of proxy urls to use + STEAM_API_HOST: 'api.steampowered.com', // comma separated list of hosts to fetch Steam API data from + POSTGRES_URL: 'postgresql://postgres:postgres@localhost/yasp', // connection string for PostgreSQL + POSTGRES_TEST_URL: 'postgresql://postgres:postgres@localhost/yasp_test', + READONLY_POSTGRES_URL: 'postgresql://readonly:readonly@localhost/yasp', // readonly connection string for PostgreSQL + REDIS_URL: 'redis://127.0.0.1:6379/0', // connection string for Redis + REDIS_TEST_URL: 'redis://127.0.0.1:6379/1', + CASSANDRA_URL: 'cassandra://localhost/yasp', // connection string for Cassandra + CASSANDRA_TEST_URL: 'cassandra://localhost/yasp_test', + ELASTICSEARCH_URL: 'localhost:9200', + INIT_POSTGRES_HOST: 'localhost', + INIT_CASSANDRA_HOST: 'localhost', + RETRIEVER_SECRET: '', // string to use as shared secret with retriever/parser + SESSION_SECRET: 'secret to encrypt cookies with', // string to encrypt cookies + COOKIE_DOMAIN: '', // domain to use for the cookie. Use e.g. '.opendota.com' to share cookie across subdomains UNTRACK_DAYS: 30, // The number of days a user is tracked for after every visit GOAL: 5, // The cheese goal DEFAULT_DELAY: 1000, // delay between API requests @@ -54,24 +54,24 @@ const defaults = { PUBLIC_SAMPLE_PERCENT: 10, // percent of public matches to sample in DB SCENARIOS_SAMPLE_PERCENT: 100, // percent of parsed matches to sample for scenarios BENCHMARKS_SAMPLE_PERCENT: 100, // percent of parsed matches to sample for benchmarks - ENABLE_MATCH_CACHE: "", // set to enable caching matches in Redis + ENABLE_MATCH_CACHE: '', // set to enable caching matches in Redis ENABLE_PLAYER_CACHE: 1, // enable/disable player aggregation caching - ENABLE_RANDOM_MMR_UPDATE: "", // set to request MMR updates after ranked matches + ENABLE_RANDOM_MMR_UPDATE: '', // set to request MMR updates after ranked matches MAXIMUM_AGE_SCENARIOS_ROWS: 4, // maximum allowed age of scenarios rows in weeks MATCH_CACHE_SECONDS: 60, // number of seconds to cache matches PLAYER_CACHE_SECONDS: 1800, // number of seconds to cache player aggregations SCANNER_PLAYER_PERCENT: 100, // percent of matches from scanner to insert player account IDs for (discover new player account IDs) - ENABLE_RETRIEVER_ADVANCED_AUTH: "", // set to enable retriever two-factor and SteamGuard authentication, - ENABLE_API_LIMIT: "", // if truthy, API calls after exceeding API_FREE_LIMIT are blocked + ENABLE_RETRIEVER_ADVANCED_AUTH: '', // set to enable retriever two-factor and SteamGuard authentication, + ENABLE_API_LIMIT: '', // if truthy, API calls after exceeding API_FREE_LIMIT are blocked API_FREE_LIMIT: 50000, // number of api requests per month before 429 is returned. If using an API key, calls over this are charged. API_BILLING_UNIT: 100, // how many calls is equivalent to a unit of calls e.g. 100 calls per $0.01. API_KEY_PER_MIN_LIMIT: 300, // Rate limit per minute if using an API key NO_API_KEY_PER_MIN_LIMIT: 60, // Rate limit per minute if not using an API key - ADMIN_ACCOUNT_IDS: "", // Whitelisted, comma separated account IDs to access /admin* routes + ADMIN_ACCOUNT_IDS: '', // Whitelisted, comma separated account IDs to access /admin* routes BACKUP_RETRIEVER_PERCENT: 0, // percent of replay salts to fetch from backup data source GCDATA_PARALLELISM: 1, // Number of simultaneous GC match details requests to make (per retriever) - STRIPE_SECRET: "rk_test_gRqwhv4xqv0a1olp8kk8fZ94", // for stripe payment processing (kept on server) - STRIPE_API_PLAN: "plan_CgLthOgwrDgz2K", // plan id for stripe metering + STRIPE_SECRET: 'rk_test_gRqwhv4xqv0a1olp8kk8fZ94', // for stripe payment processing (kept on server) + STRIPE_API_PLAN: 'plan_CgLthOgwrDgz2K', // plan id for stripe metering ES_SEARCH_PERCENT: 0, // % of users to roll out elasticsearch to MATCH_ARCHIVE_S3_KEY_ID: '', // S3-compatible key ID to archive parsed match blobs MATCH_ARCHIVE_S3_KEY_SECRET: '', // S3-compatible key secret to archive parsed match blobs @@ -82,16 +82,16 @@ const defaults = { Object.keys(defaults).forEach((key) => { process.env[key] = key in process.env ? process.env[key] : defaults[key]; }); -if (process.env.NODE_ENV === "development") { +if (process.env.NODE_ENV === 'development') { // force PORT to null in development so we can run multiple web services without conflict - process.env.PORT = ""; + process.env.PORT = ''; } -if (process.env.NODE_ENV === "test") { - process.env.PORT = ""; // use service defaults +if (process.env.NODE_ENV === 'test') { + process.env.PORT = ''; // use service defaults process.env.POSTGRES_URL = process.env.POSTGRES_TEST_URL; process.env.CASSANDRA_URL = process.env.CASSANDRA_TEST_URL; process.env.REDIS_URL = process.env.REDIS_TEST_URL; - process.env.SESSION_SECRET = "testsecretvalue"; + process.env.SESSION_SECRET = 'testsecretvalue'; process.env.ENABLE_MATCH_CACHE = 1; process.env.FRONTEND_PORT = 5001; process.env.PARSER_PORT = 5201; diff --git a/dev/accountHelper.mjs b/dev/accountHelper.mjs index b03d74beb..47deebddf 100644 --- a/dev/accountHelper.mjs +++ b/dev/accountHelper.mjs @@ -1,5 +1,5 @@ -const input = "areallyreallylongemailaddresss@gmail.com"; -const arr = input.split("@"); +const input = 'areallyreallylongemailaddresss@gmail.com'; +const arr = input.split('@'); const user = arr[0]; let count = 0; const limit = 5000; @@ -7,11 +7,11 @@ const limit = 5000; function permute(user, n) { if (n >= user.length || (limit && count > limit)) { arr[0] = user; - console.log(arr.join("@")); + console.log(arr.join('@')); count += 1; return; } - const diff = [user.substr(0, n), user.substr(n)].join("."); + const diff = [user.substr(0, n), user.substr(n)].join('.'); // don't add a period permute(user, n + 1); // add a period diff --git a/dev/archiveTest.mjs b/dev/archiveTest.mjs index ae6e36a58..a35154f4c 100644 --- a/dev/archiveTest.mjs +++ b/dev/archiveTest.mjs @@ -1,10 +1,10 @@ -import { archivePut, archiveGet } from "../store/archive.js"; -import { getMatchData, getPlayerMatchData } from "../store/queries.js"; +import { archivePut, archiveGet } from '../store/archive.js'; +import { getMatchData, getPlayerMatchData } from '../store/queries.js'; // Read some match data const match = await getMatchData(7465883253); const players = await getPlayerMatchData(7465883253); -const blob = Buffer.from(JSON.stringify({...match, players })); +const blob = Buffer.from(JSON.stringify({ ...match, players })); // Archive it await archivePut(match.match_id.toString(), blob); @@ -15,4 +15,3 @@ const readBack = await archiveGet(match.match_id.toString()); console.log(blob.length, readBack.length); // Confirm API returns the same data whether we used the archive or not - diff --git a/dev/checkAccounts.mjs b/dev/checkAccounts.mjs index 63a8a9372..35398f668 100644 --- a/dev/checkAccounts.mjs +++ b/dev/checkAccounts.mjs @@ -1,8 +1,8 @@ import fs from 'fs'; import Steam from 'steam'; import async from 'async'; -const accountData = fs.readFileSync("./STEAM_ACCOUNT_DATA_BAD.txt", "utf8"); -const accountArray = accountData.split(require("os").EOL); +const accountData = fs.readFileSync('./STEAM_ACCOUNT_DATA_BAD.txt', 'utf8'); +const accountArray = accountData.split(require('os').EOL); let index = Number(process.argv[2]) || -1; async.whilst( @@ -11,8 +11,8 @@ async.whilst( index += 1; const random = index; // const random = Math.floor(Math.random() * accountArray.length); - const user = accountArray[random].split("\t")[0]; - const pass = accountArray[random].split("\t")[1]; + const user = accountArray[random].split('\t')[0]; + const pass = accountArray[random].split('\t')[1]; const logOnDetails = { account_name: user, password: pass, @@ -20,16 +20,16 @@ async.whilst( const client = new Steam.SteamClient(); client.steamUser = new Steam.SteamUser(client); client.connect(); - client.on("connected", () => { + client.on('connected', () => { client.steamUser.logOn(logOnDetails); }); - client.on("logOnResponse", (logOnResp) => { + client.on('logOnResponse', (logOnResp) => { if (logOnResp.eresult === Steam.EResult.AccountDisabled) { - console.error(index, user, "failed", logOnResp.eresult); + console.error(index, user, 'failed', logOnResp.eresult); } else if (logOnResp.eresult === Steam.EResult.InvalidPassword) { - console.error(index, user, "failed", logOnResp.eresult); + console.error(index, user, 'failed', logOnResp.eresult); } else { - console.error(index, user, "passed", logOnResp.eresult); + console.error(index, user, 'passed', logOnResp.eresult); } client.disconnect(); setTimeout(cb, 500); diff --git a/dev/createAccounts.mjs b/dev/createAccounts.mjs index d6ec95ef6..25343c081 100644 --- a/dev/createAccounts.mjs +++ b/dev/createAccounts.mjs @@ -8,14 +8,14 @@ async.eachSeries( Array.from(new Array(1000), (v, i) => i), (i, cb) => { steam.logOn(() => {}); - steam.once("loggedOn", () => { + steam.once('loggedOn', () => { const name = `${time}_${i}`; const password = (Math.random() + 1).toString(36).substring(7); const email = `${name}@email.com`; steam.createAccount(name, password, email, (result, steamid) => { console.error(name, password, result, steamid); if (result === Steam.EResult.OK) { - console.log("%s\t%s", name, password); + console.log('%s\t%s', name, password); } steam.logOff(() => {}); setTimeout(cb, 61000); diff --git a/dev/findProMatches.mjs b/dev/findProMatches.mjs index ed181f4ab..b44e46c85 100644 --- a/dev/findProMatches.mjs +++ b/dev/findProMatches.mjs @@ -1,7 +1,7 @@ import async from 'async'; import queries from '../store/queries.js'; import db from '../store/db.js'; -import { generateJob, getData } from '../util/utility.js'; +import { generateJob, getData } from '../util/utility.js'; // const leagueUrl = generateJob('api_leagues', {}).url; @@ -19,7 +19,7 @@ function getPage(url, leagueid, cb) { data.result.matches, (match, cb) => { console.log(match.match_id); - const job = generateJob("api_details", { + const job = generateJob('api_details', { match_id: match.match_id, }); const { url } = job; @@ -51,7 +51,7 @@ function getPage(url, leagueid, cb) { throw err; } if (data.result.results_remaining) { - const url2 = generateJob("api_history", { + const url2 = generateJob('api_history', { leagueid, start_at_match_id: data.result.matches[data.result.matches.length - 1].match_id - 1, @@ -66,10 +66,10 @@ function getPage(url, leagueid, cb) { } // From DB -db.select("leagueid") - .from("leagues") - .where("tier", "professional") - .orWhere("tier", "premium") +db.select('leagueid') + .from('leagues') + .where('tier', 'professional') + .orWhere('tier', 'premium') .asCallback((err, data) => { if (err) { throw err; @@ -78,7 +78,7 @@ db.select("leagueid") async.eachSeries( leagueIds, (leagueid, cb) => { - const { url } = generateJob("api_history", { + const { url } = generateJob('api_history', { leagueid, }); return getPage(url, leagueid, cb); diff --git a/dev/generateFakeRatings.mjs b/dev/generateFakeRatings.mjs index 636056b06..26da1aa2e 100644 --- a/dev/generateFakeRatings.mjs +++ b/dev/generateFakeRatings.mjs @@ -12,13 +12,13 @@ function randByCentralLimitTheorem() { function gaussianRandom(mean, std) { if (mean === undefined || std === undefined) { throw new Error( - "Gaussian random needs 2 arguments (mean, standard deviation)" + 'Gaussian random needs 2 arguments (mean, standard deviation)' ); } return randByCentralLimitTheorem() * std + mean; } -db.from("players").asCallback((err, players) => { +db.from('players').asCallback((err, players) => { async.each( players, (p, cb) => { @@ -30,7 +30,7 @@ db.from("players").asCallback((err, players) => { time: new Date(), }; console.log(fake.account_id, fake.solo_competitive_rank); - db.insert(fake).into("player_ratings").asCallback(cb); + db.insert(fake).into('player_ratings').asCallback(cb); }, (err) => { process.exit(Number(err)); diff --git a/dev/keyTest.mjs b/dev/keyTest.mjs index d147ed43f..8075b8157 100644 --- a/dev/keyTest.mjs +++ b/dev/keyTest.mjs @@ -5,7 +5,7 @@ import config from '../config.js'; const output = []; async.eachSeries( - config.STEAM_API_KEY.split(","), + config.STEAM_API_KEY.split(','), (key, cb) => { setTimeout(() => { request( @@ -23,6 +23,6 @@ async.eachSeries( }, 1000); }, () => { - console.log(output.join(",")); + console.log(output.join(',')); } ); diff --git a/dev/loginTest.mjs b/dev/loginTest.mjs index 882b60671..9b8c2cb73 100644 --- a/dev/loginTest.mjs +++ b/dev/loginTest.mjs @@ -9,16 +9,16 @@ const logOnDetails = { password: pass, }; client.Dota2 = new Dota2.Dota2Client(client, false, false); -client.Dota2.on("ready", () => { - console.log("dota ready"); +client.Dota2.on('ready', () => { + console.log('dota ready'); }); client.steamUser = new Steam.SteamUser(client); client.connect(); -client.on("connected", () => { - console.log("[STEAM] Trying to log on with %s,%s", user, pass); +client.on('connected', () => { + console.log('[STEAM] Trying to log on with %s,%s', user, pass); client.steamUser.logOn(logOnDetails); }); -client.on("logOnResponse", (logOnResp) => { +client.on('logOnResponse', (logOnResp) => { if (logOnResp.eresult !== Steam.EResult.OK) { // try logging on again console.error(logOnResp); @@ -26,7 +26,7 @@ client.on("logOnResponse", (logOnResp) => { return; } if (client && client.steamID) { - console.log("[STEAM] Logged on %s", client.steamID); + console.log('[STEAM] Logged on %s', client.steamID); client.Dota2.launch(); } }); diff --git a/dev/metaParse.mjs b/dev/metaParse.mjs index a8358e006..76f046a65 100644 --- a/dev/metaParse.mjs +++ b/dev/metaParse.mjs @@ -10,9 +10,9 @@ files.forEach((file) => { }); */ -const builder = ProtoBuf.loadProtoFile("./proto/dota_match_metadata.proto"); +const builder = ProtoBuf.loadProtoFile('./proto/dota_match_metadata.proto'); const Message = builder.build(); -const buf = fs.readFileSync("./2750586075_1028519576.meta"); +const buf = fs.readFileSync('./2750586075_1028519576.meta'); const message = Message.CDOTAMatchMetadataFile.decode(buf); message.metadata.teams.forEach((team) => { team.players.forEach((player) => { diff --git a/dev/rePatch.mjs b/dev/rePatch.mjs index 41a4f83f2..b15009b36 100644 --- a/dev/rePatch.mjs +++ b/dev/rePatch.mjs @@ -7,9 +7,9 @@ import db from '../store/db.js'; import queries from '../store/queries.js'; import utility from '../util/utility.js'; -db.select(["match_id", "start_time"]) - .from("matches") - .orderBy("match_id", "desc") +db.select(['match_id', 'start_time']) + .from('matches') + .orderBy('match_id', 'desc') .asCallback((err, matchIds) => { if (err) { throw err; @@ -22,7 +22,7 @@ db.select(["match_id", "start_time"]) console.log(match.match_id, patch); queries.upsert( db, - "match_patch", + 'match_patch', { match_id: match.match_id, patch, diff --git a/dev/teamElo.mjs b/dev/teamElo.mjs index 3e7c55a2c..945fe9c64 100644 --- a/dev/teamElo.mjs +++ b/dev/teamElo.mjs @@ -25,7 +25,7 @@ ORDER BY match_id ASC ) .stream(); stream.pipe(JSONStream.parse()); -stream.on("data", (match) => { +stream.on('data', (match) => { // console.log(JSON.stringify(match)); if (!teams[match.team_id1]) { teams[match.team_id1] = 1000; @@ -64,7 +64,7 @@ stream.on("data", (match) => { losses[match.team_id1] += Number(!win1); losses[match.team_id2] += Number(!win2); }); -stream.on("end", () => { +stream.on('end', () => { console.log(teams, wins, losses, startTimes); // Write the results to table async.eachSeries( @@ -97,6 +97,6 @@ stream.on("end", () => { } ); }); -stream.on("error", (err) => { +stream.on('error', (err) => { throw err; }); diff --git a/dev/teamMatch.mjs b/dev/teamMatch.mjs index f16efd1d4..18a7518d6 100644 --- a/dev/teamMatch.mjs +++ b/dev/teamMatch.mjs @@ -2,8 +2,8 @@ import async from 'async'; import db from '../store/db'; import queries from '../store/queries'; -db.select(["radiant_team_id", "dire_team_id", "match_id"]) - .from("matches") +db.select(['radiant_team_id', 'dire_team_id', 'match_id']) + .from('matches') .asCallback((err, matches) => { if (err) { throw err; @@ -32,7 +32,7 @@ db.select(["radiant_team_id", "dire_team_id", "match_id"]) (tm, cb) => { queries.upsert( db, - "team_match", + 'team_match', tm, { team_id: tm.team_id, diff --git a/dev/wordcount.mjs b/dev/wordcount.mjs index 1873d3ea3..a0b83fb1e 100644 --- a/dev/wordcount.mjs +++ b/dev/wordcount.mjs @@ -6,18 +6,18 @@ import compute from '../util/compute.js'; const args = process.argv.slice(2); const limit = Number(args[0]) || 1; const stream = db - .select("chat") - .from("matches") - .where("version", ">", "0") + .select('chat') + .from('matches') + .where('version', '>', '0') .limit(limit) - .orderBy("match_id", "desc") + .orderBy('match_id', 'desc') .stream(); const counts = {}; -stream.on("end", () => { +stream.on('end', () => { console.log(JSON.stringify(counts)); process.exit(0); }); stream.pipe(JSONStream.parse()); -stream.on("data", (match) => { +stream.on('data', (match) => { utility.mergeObjects(counts, compute.count_words(match)); }); diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index ca851d51b..2c15b222a 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -1,4 +1,4 @@ -version: "2" +version: '2' services: postgres: build: @@ -25,7 +25,7 @@ services: - cluster.name=docker-cluster - bootstrap.memory_lock=true - bootstrap.system_call_filter=false - - "ES_JAVA_OPTS=-Xms256m -Xmx256m" + - 'ES_JAVA_OPTS=-Xms256m -Xmx256m' - discovery.type=single-node ulimits: memlock: @@ -41,8 +41,8 @@ services: image: odota/core entrypoint: sleep infinity ports: - - "5000:5000" - - "5100:5100" + - '5000:5000' + - '5100:5100' environment: PARSER_HOST: http://odota-parser:5600 POSTGRES_URL: postgresql://postgres:postgres@odota-postgres/yasp diff --git a/docker-compose.yml b/docker-compose.yml index e73a75c4b..58cef9416 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ # You don't usually need to edit this file. # If it does not fit your personal use case, docker-compose.override.yml is a preferred way to go. -version: "2" +version: '2' services: postgres: build: @@ -28,7 +28,7 @@ services: - cluster.name=docker-cluster - bootstrap.memory_lock=true - bootstrap.system_call_filter=false - - "ES_JAVA_OPTS=-Xms256m -Xmx256m" + - 'ES_JAVA_OPTS=-Xms256m -Xmx256m' - discovery.type=single-node ulimits: memlock: @@ -45,8 +45,8 @@ services: # Override this command with your script if existing does not fit your needs entrypoint: bash docker/main-launch.sh ports: - - "5000:5000" - - "5100:5100" + - '5000:5000' + - '5100:5100' environment: PARSER_HOST: http://odota-parser:5600 POSTGRES_URL: postgresql://postgres:postgres@odota-postgres/yasp diff --git a/index.js b/index.js index d8e8835b7..90344f04c 100644 --- a/index.js +++ b/index.js @@ -2,17 +2,17 @@ /** * Entry point for the application. * */ -const cp = require("child_process"); -const pm2 = require("pm2"); -const async = require("async"); -const { apps } = require("./manifest.json"); +const cp = require('child_process'); +const pm2 = require('pm2'); +const async = require('async'); +const { apps } = require('./manifest.json'); const args = process.argv.slice(2); const group = args[0] || process.env.GROUP; -if (process.env.PROVIDER === "gce") { +if (process.env.PROVIDER === 'gce') { cp.execSync( - "curl -H \"Metadata-Flavor: Google\" -L http://metadata.google.internal/computeMetadata/v1/project/attributes/env > /usr/src/.env" + 'curl -H "Metadata-Flavor: Google" -L http://metadata.google.internal/computeMetadata/v1/project/attributes/env > /usr/src/.env' ); } if (process.env.ROLE) { diff --git a/package.json b/package.json index 1b05cc46b..db0034f41 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ }, "prettier": { "trailingComma": "es5", - "tabWidth": 2 + "tabWidth": 2, + "singleQuote": true }, "repository": { "type": "git", diff --git a/processors/createParsedDataBlob.js b/processors/createParsedDataBlob.js index 5dce0fb2e..6c5c2e94d 100644 --- a/processors/createParsedDataBlob.js +++ b/processors/createParsedDataBlob.js @@ -1,12 +1,12 @@ -const { Console } = require("console"); -const readline = require("readline"); -const processAllPlayers = require("./processAllPlayers"); -const processTeamfights = require("./processTeamfights"); +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"); +const processParsedData = require('./processParsedData'); +const processMetadata = require('./processMetadata'); +const processExpand = require('./processExpand'); +const processDraftTimings = require('./processDraftTimings'); function getParseSchema() { return { @@ -84,30 +84,30 @@ function getParseSchema() { function createParsedDataBlob(entries, matchId) { const logConsole = new Console(process.stderr); - logConsole.time("metadata"); + logConsole.time('metadata'); const meta = processMetadata(entries); meta.match_id = matchId; - logConsole.timeEnd("metadata"); + logConsole.timeEnd('metadata'); - logConsole.time("expand"); + logConsole.time('expand'); const expanded = processExpand(entries, meta); - logConsole.timeEnd("expand"); + logConsole.timeEnd('expand'); - logConsole.time("populate"); + logConsole.time('populate'); const parsedData = processParsedData(expanded, getParseSchema(), meta); - logConsole.timeEnd("populate"); + logConsole.timeEnd('populate'); - logConsole.time("teamfights"); + logConsole.time('teamfights'); parsedData.teamfights = processTeamfights(expanded, meta); - logConsole.timeEnd("teamfights"); + logConsole.timeEnd('teamfights'); - logConsole.time("draft"); + logConsole.time('draft'); parsedData.draft_timings = processDraftTimings(entries, meta); - logConsole.timeEnd("draft"); + logConsole.timeEnd('draft'); - logConsole.time("processAllPlayers"); + logConsole.time('processAllPlayers'); const ap = processAllPlayers(entries, meta); - logConsole.timeEnd("processAllPlayers"); + logConsole.timeEnd('processAllPlayers'); parsedData.radiant_gold_adv = ap.radiant_gold_adv; parsedData.radiant_xp_adv = ap.radiant_xp_adv; @@ -121,14 +121,14 @@ const matchId = process.argv[2]; const parseStream = readline.createInterface({ input: process.stdin, }); -parseStream.on("line", (e) => { +parseStream.on('line', (e) => { e = JSON.parse(e); entries.push(e); - if (e.type === "epilogue") { + if (e.type === 'epilogue') { complete = true; } }); -parseStream.on("close", () => { +parseStream.on('close', () => { if (complete) { const parsedData = createParsedDataBlob(entries, matchId); process.stdout.write(JSON.stringify(parsedData), null, (err) => { diff --git a/processors/performanceOthers.js b/processors/performanceOthers.js index 7ff424d52..4e1853f58 100644 --- a/processors/performanceOthers.js +++ b/processors/performanceOthers.js @@ -1,6 +1,6 @@ function greevilsGreed(e, container, meta) { - if (e.type === "killed" && "greevils_greed_stack" in e) { - const alchName = "npc_dota_hero_alchemist"; + if (e.type === 'killed' && 'greevils_greed_stack' in e) { + const alchName = 'npc_dota_hero_alchemist'; const alchSlot = meta.hero_to_slot[alchName]; const alchPlayer = container.players[alchSlot]; @@ -18,8 +18,8 @@ function greevilsGreed(e, container, meta) { } function track(e, container, meta) { - if (e.tracked_death && e.type === "killed") { - const bhName = "npc_dota_hero_bountyhunter"; + if (e.tracked_death && e.type === 'killed') { + const bhName = 'npc_dota_hero_bountyhunter'; const trackerSlot = meta.hero_to_slot[e.tracked_sourcename]; const trackerPlayer = container.players[trackerSlot]; diff --git a/processors/populate.js b/processors/populate.js index 0d63ea104..18005b6d4 100644 --- a/processors/populate.js +++ b/processors/populate.js @@ -1,30 +1,30 @@ -const performanceOthers = require("./performanceOthers"); +const performanceOthers = require('./performanceOthers'); function populate(e, container, meta) { let t; switch (e.type) { - case "interval": + case 'interval': break; - case "player_slot": + case 'player_slot': container.players[e.key].player_slot = e.value; break; - case "chat": - case "chatwheel": + case 'chat': + case 'chatwheel': container.chat.push(JSON.parse(JSON.stringify(e))); break; - case "cosmetics": + case 'cosmetics': container.cosmetics = JSON.parse(e.key); break; - case "CHAT_MESSAGE_FIRSTBLOOD": - case "CHAT_MESSAGE_COURIER_LOST": - case "CHAT_MESSAGE_AEGIS": - case "CHAT_MESSAGE_AEGIS_STOLEN": - case "CHAT_MESSAGE_DENIED_AEGIS": - case "CHAT_MESSAGE_ROSHAN_KILL": - case "building_kill": + case 'CHAT_MESSAGE_FIRSTBLOOD': + case 'CHAT_MESSAGE_COURIER_LOST': + case 'CHAT_MESSAGE_AEGIS': + case 'CHAT_MESSAGE_AEGIS_STOLEN': + case 'CHAT_MESSAGE_DENIED_AEGIS': + case 'CHAT_MESSAGE_ROSHAN_KILL': + case 'building_kill': container.objectives.push(JSON.parse(JSON.stringify(e))); break; - case "ability_levels": + case 'ability_levels': meta.ability_levels[e.unit] = { [e.key]: e.level, ...meta.ability_levels[e.unit], @@ -38,7 +38,7 @@ function populate(e, container, meta) { return; } t = container.players[e.slot][e.type]; - if (typeof t === "undefined") { + if (typeof t === 'undefined') { // container.players[0] doesn't have a type for this event // console.log("no field in parsed_data.players for %s", e.type); } else if (e.posData) { @@ -65,23 +65,23 @@ function populate(e, container, meta) { if (e.interval) { arrEntry = e.value; } else if ( - e.type === "purchase_log" || - e.type === "kills_log" || - e.type === "runes_log" + e.type === 'purchase_log' || + e.type === 'kills_log' || + e.type === 'runes_log' ) { arrEntry = { time: e.time, key: e.key, }; - const maxCharges = e.key === "tango" ? 3 : 1; - if (e.type === "purchase_log" && e.charges > maxCharges) { + const maxCharges = e.key === 'tango' ? 3 : 1; + if (e.type === 'purchase_log' && e.charges > maxCharges) { arrEntry = { time: e.time, key: e.key, charges: e.charges, }; } - if (e.type === "kills_log" && e.tracked_death) { + if (e.type === 'kills_log' && e.tracked_death) { arrEntry = { tracked_death: e.tracked_death, tracked_sourcename: e.tracked_sourcename, @@ -92,7 +92,7 @@ function populate(e, container, meta) { arrEntry = JSON.parse(JSON.stringify(e)); } t.push(arrEntry); - } else if (e.type === "ability_targets") { + } else if (e.type === 'ability_targets') { // e.g. { Telekinesis: { Antimage: 1, Bristleback: 2 }, Fade Bolt: { Lion: 4, Timber: 5 }, ... } const ability = e.key[0]; const target = e.key[1]; @@ -104,7 +104,7 @@ function populate(e, container, meta) { t[ability] = {}; t[ability][target] = 1; } - } else if (e.type === "damage_targets") { + } else if (e.type === 'damage_targets') { const ability = e.key[0]; const target = e.key[1]; const damage = e.value; @@ -115,7 +115,7 @@ function populate(e, container, meta) { t[ability][target] = 0; } t[ability][target] += damage; - } else if (typeof t === "object") { + } else if (typeof t === 'object') { // add it to hash of counts e.value = e.value || 1; if (t[e.key]) { @@ -124,7 +124,7 @@ function populate(e, container, meta) { t[e.key] = e.value; } performanceOthers(e, container, meta); - } else if (typeof t === "string") { + } else if (typeof t === 'string') { // string, used for steam id container.players[e.slot][e.type] = e.key; } else { diff --git a/processors/processAllPlayers.js b/processors/processAllPlayers.js index 29ed7f760..5ae6fe31c 100644 --- a/processors/processAllPlayers.js +++ b/processors/processAllPlayers.js @@ -1,4 +1,4 @@ -const utility = require("../util/utility"); +const utility = require('../util/utility'); /** * Compute data requiring all players in a match for storage in match table @@ -12,7 +12,7 @@ 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") { + if (e.time >= 0 && e.time % 60 === 0 && e.type === 'interval') { const g = utility.isRadiant({ player_slot: meta.slot_to_playerslot[e.slot], }) diff --git a/processors/processDraftTimings.js b/processors/processDraftTimings.js index 538eda86c..51e7d3f94 100644 --- a/processors/processDraftTimings.js +++ b/processors/processDraftTimings.js @@ -23,7 +23,7 @@ function processDraftTimings(entries, meta) { for (let i = 0; i < entries.length; i += 1) { const e = entries[i]; const heroId = e.hero_id; - if (e.type === "draft_timings") { + if (e.type === 'draft_timings') { // The active team needs to be downshifted by 1, so ignore the final observation. if (i < entries.length - 1) { sumActiveTeam += e.draft_active_team; @@ -41,7 +41,7 @@ function processDraftTimings(entries, meta) { }; draftTimings.push(JSON.parse(JSON.stringify(currpickban))); previousActiveTeam = e.draft_active_team; - } else if (e.type === "draft_start") { + } else if (e.type === 'draft_start') { draftStart = e.time; } } diff --git a/processors/processExpand.js b/processors/processExpand.js index 1df274e72..c204e5232 100644 --- a/processors/processExpand.js +++ b/processors/processExpand.js @@ -3,10 +3,10 @@ * Does not mutate the original string. * */ function translate(input) { - if (input === "dota_unknown") { + if (input === 'dota_unknown') { return null; } - if (input && input.indexOf("item_") === 0) { + if (input && input.indexOf('item_') === 0) { return input.slice(5); } return input; @@ -15,7 +15,7 @@ function translate(input) { * Prepends illusion_ to string if illusion * */ function computeIllusionString(input, isIllusion) { - return (isIllusion ? "illusion_" : "") + input; + return (isIllusion ? 'illusion_' : '') + input; } /** @@ -28,7 +28,7 @@ function processExpand(entries, meta) { * */ function expand(e) { // set slot and player_slot - const slot = "slot" in e ? e.slot : meta.hero_to_slot[e.unit]; + const slot = 'slot' in e ? e.slot : meta.hero_to_slot[e.unit]; output.push({ ...e, slot, player_slot: meta.slot_to_playerslot[slot] }); } @@ -44,7 +44,7 @@ function processExpand(entries, meta) { const unit = e.sourcename; const key = computeIllusionString(e.targetname, e.targetillusion); const inflictor = translate(e.inflictor); - expand({ ...e, unit, key, type: "damage" }); + expand({ ...e, unit, key, type: 'damage' }); // check if this damage happened to a real hero if (e.targethero && !e.targetillusion) { // reverse @@ -53,13 +53,13 @@ function processExpand(entries, meta) { value: e.value, unit: key, key: unit, - type: "damage_taken", + type: 'damage_taken', }); expand({ value: e.value, unit, key: [inflictor, translate(e.targetname)], - type: "damage_targets", + type: 'damage_targets', }); // count a hit on a real hero with this inflictor expand({ @@ -67,7 +67,7 @@ function processExpand(entries, meta) { value: 1, unit, key: inflictor, - type: "hero_hits", + type: 'hero_hits', }); // don't count self-damage for the following if (key !== unit) { @@ -77,11 +77,11 @@ function processExpand(entries, meta) { value: e.value, unit, key: inflictor, - type: "damage_inflictor", + type: 'damage_inflictor', }); // biggest hit on a hero expand({ - type: "max_hero_hit", + type: 'max_hero_hit', time: e.time, max: true, inflictor, @@ -89,11 +89,11 @@ function processExpand(entries, meta) { key, value: e.value, }); - if (e.sourcename && e.sourcename.includes("npc_dota_hero_")) { + if (e.sourcename && e.sourcename.includes('npc_dota_hero_')) { expand({ time: e.time, value: e.value, - type: "damage_inflictor_received", + type: 'damage_inflictor_received', unit: key, key: inflictor, }); @@ -110,7 +110,7 @@ function processExpand(entries, meta) { { unit: e.sourcename, key: computeIllusionString(e.targetname, e.targetillusion), - type: "healing", + type: 'healing', } ) ); @@ -122,7 +122,7 @@ function processExpand(entries, meta) { // e.targetname // target of buff (possibly illusion) // Aegis expired - if (e.inflictor === "modifier_aegis_regen") { + if (e.inflictor === 'modifier_aegis_regen') { aegisHolder = null; } }, @@ -139,14 +139,14 @@ function processExpand(entries, meta) { // If it is a building kill if ( - e.targetname.indexOf("_tower") > -1 || - e.targetname.indexOf("_rax_") > -1 || - e.targetname.indexOf("_healers") > -1 || - e.targetname.indexOf("_fort") > -1 + e.targetname.indexOf('_tower') > -1 || + e.targetname.indexOf('_rax_') > -1 || + e.targetname.indexOf('_healers') > -1 || + e.targetname.indexOf('_fort') > -1 ) { expand({ time: e.time, - type: "building_kill", + type: 'building_kill', unit, key, }); @@ -183,14 +183,14 @@ function processExpand(entries, meta) { key, tracked_death: e.tracked_death, tracked_sourcename: e.tracked_sourcename, - type: "kills_log", + type: 'kills_log', }); // reverse expand({ time: e.time, unit: key, key: unit, - type: "killed_by", + type: 'killed_by', }); } @@ -198,7 +198,7 @@ function processExpand(entries, meta) { ...e, unit, key, - type: "killed", + type: 'killed', // Dota Plus patch added a value field to this event type, but we want to always treat it as 1 value: 1, }); @@ -210,7 +210,7 @@ function processExpand(entries, meta) { time: e.time, unit: e.attackername, key: translate(e.inflictor), - type: "ability_uses", + type: 'ability_uses', }); // target of ability if (e.targethero && !e.targetillusion) { @@ -218,7 +218,7 @@ function processExpand(entries, meta) { time: e.time, unit: e.attackername, key: [translate(e.inflictor), translate(e.targetname)], - type: "ability_targets", + type: 'ability_targets', }); } }, @@ -229,7 +229,7 @@ function processExpand(entries, meta) { unit: e.targetname, level: e.abilitylevel, key: translate(e.valuename), - type: "ability_levels", + type: 'ability_levels', }); }, DOTA_COMBATLOG_ITEM(e) { @@ -238,7 +238,7 @@ function processExpand(entries, meta) { time: e.time, unit: e.attackername, key: translate(e.inflictor), - type: "item_uses", + type: 'item_uses', }); }, DOTA_COMBATLOG_LOCATION() { @@ -251,7 +251,7 @@ function processExpand(entries, meta) { value: e.value, unit: e.targetname, key: e.gold_reason, - type: "gold_reasons", + type: 'gold_reasons', }); }, DOTA_COMBATLOG_GAME_STATE() { @@ -264,7 +264,7 @@ function processExpand(entries, meta) { value: e.value, unit: e.targetname, key: e.xp_reason, - type: "xp_reasons", + type: 'xp_reasons', }); }, DOTA_COMBATLOG_PURCHASE(e) { @@ -277,17 +277,17 @@ function processExpand(entries, meta) { unit, key, charges: e.charges, - type: "purchase", + type: 'purchase', }); // don't include recipes in purchase logs - if (key.indexOf("recipe_") !== 0) { + if (key.indexOf('recipe_') !== 0) { expand({ time: e.time, value: 1, unit, key, charges: e.charges, - type: "purchase_log", + type: 'purchase_log', }); } }, @@ -296,7 +296,7 @@ function processExpand(entries, meta) { expand({ time: e.time, slot: e.value, - type: "buyback_log", + type: 'buyback_log', }); }, DOTA_COMBATLOG_ABILITY_TRIGGER() { @@ -323,7 +323,7 @@ function processExpand(entries, meta) { value: 1, unit: e.attackername, key: e.value, - type: "multi_kills", + type: 'multi_kills', }); }, DOTA_COMBATLOG_KILLSTREAK(e) { @@ -335,7 +335,7 @@ function processExpand(entries, meta) { value: 1, unit: e.attackername, key: e.value, - type: "kill_streaks", + type: 'kill_streaks', }); }, DOTA_COMBATLOG_TEAM_BUILDING_KILL() { @@ -360,7 +360,7 @@ function processExpand(entries, meta) { // we're not breaking pings into subtypes atm so just set key to 0 for now expand({ time: e.time, - type: "pings", + type: 'pings', slot: e.slot, key: 0, }); @@ -373,7 +373,7 @@ function processExpand(entries, meta) { expand({ time: e.time, value: 1, - type: "runes", + type: 'runes', slot: e.player1, key: String(e.value), }); @@ -381,7 +381,7 @@ function processExpand(entries, meta) { time: e.time, key: e.value, slot: e.player1, - type: "runes_log", + type: 'runes_log', }); }, CHAT_MESSAGE_RUNE_BOTTLE() { @@ -423,17 +423,17 @@ function processExpand(entries, meta) { CHAT_MESSAGE_RECONNECT(e) { expand({ time: e.time, - type: "connection_log", + type: 'connection_log', slot: e.player1, - event: "reconnect", + event: 'reconnect', }); }, CHAT_MESSAGE_DISCONNECT_WAIT_FOR_RECONNECT(e) { expand({ time: e.time, - type: "connection_log", + type: 'connection_log', slot: e.player1, - event: "disconnect", + event: 'disconnect', }); }, CHAT_MESSAGE_FIRSTBLOOD(e) { @@ -522,25 +522,25 @@ function processExpand(entries, meta) { if (e.time >= 0) { expand(e); [ - "stuns", - "life_state", - "obs_placed", - "sen_placed", - "creeps_stacked", - "camps_stacked", - "rune_pickups", - "randomed", - "repicked", - "pred_vict", - "firstblood_claimed", - "teamfight_participation", - "towers_killed", - "roshans_killed", - "observers_placed", + 'stuns', + 'life_state', + 'obs_placed', + 'sen_placed', + 'creeps_stacked', + 'camps_stacked', + 'rune_pickups', + 'randomed', + 'repicked', + 'pred_vict', + 'firstblood_claimed', + 'teamfight_participation', + 'towers_killed', + 'roshans_killed', + 'observers_placed', ].forEach((field) => { let key; let value; - if (field === "life_state") { + if (field === 'life_state') { key = e[field]; value = 1; } else { @@ -561,35 +561,35 @@ function processExpand(entries, meta) { time: e.time, slot: e.slot, interval: true, - type: "times", + type: 'times', value: e.time, }); expand({ time: e.time, slot: e.slot, interval: true, - type: "gold_t", + type: 'gold_t', value: e.gold, }); expand({ time: e.time, slot: e.slot, interval: true, - type: "xp_t", + type: 'xp_t', value: e.xp, }); expand({ time: e.time, slot: e.slot, interval: true, - type: "lh_t", + type: 'lh_t', value: e.lh, }); expand({ time: e.time, slot: e.slot, interval: true, - type: "dn_t", + type: 'dn_t', value: e.denies, }); } @@ -599,25 +599,25 @@ function processExpand(entries, meta) { expand({ time: e.time, slot: e.slot, - type: "lane_pos", + type: 'lane_pos', key: JSON.stringify([e.x, e.y]), posData: true, }); } }, obs(e) { - expand({ ...e, type: "obs", posData: true }); - expand({ ...e, type: "obs_log" }); + expand({ ...e, type: 'obs', posData: true }); + expand({ ...e, type: 'obs_log' }); }, sen(e) { - expand({ ...e, type: "sen", posData: true }); - expand({ ...e, type: "sen_log" }); + expand({ ...e, type: 'sen', posData: true }); + expand({ ...e, type: 'sen_log' }); }, obs_left(e) { - expand({ ...e, type: "obs_left_log" }); + expand({ ...e, type: 'obs_left_log' }); }, sen_left(e) { - expand({ ...e, type: "sen_left_log" }); + expand({ ...e, type: 'sen_left_log' }); }, epilogue(e) { expand(e); diff --git a/processors/processMetadata.js b/processors/processMetadata.js index 7e37574d6..752c6f705 100644 --- a/processors/processMetadata.js +++ b/processors/processMetadata.js @@ -11,7 +11,7 @@ function processMetadata(entries) { // check if hero has been assigned to entity if (e.hero_id) { // grab the end of the name, lowercase it - const ending = e.unit.slice("CDOTA_Unit_Hero_".length); + const ending = e.unit.slice('CDOTA_Unit_Hero_'.length); // the combat log name could involve replacing camelCase with _ or not! // double map it so we can look up both cases const combatLogName = `npc_dota_hero_${ending.toLowerCase()}`; diff --git a/processors/processParsedData.js b/processors/processParsedData.js index 509a707e5..795e14446 100644 --- a/processors/processParsedData.js +++ b/processors/processParsedData.js @@ -1,4 +1,4 @@ -const populate = require("./populate"); +const populate = require('./populate'); function processParsedData(entries, container, meta) { for (let i = 0; i < entries.length; i += 1) { diff --git a/processors/processTeamfights.js b/processors/processTeamfights.js index 261f2bd83..e9de1180c 100644 --- a/processors/processTeamfights.js +++ b/processors/processTeamfights.js @@ -1,4 +1,4 @@ -const populate = require("./populate"); +const populate = require('./populate'); /** * A processor to compute teamfights that occurred given an event stream @@ -11,7 +11,7 @@ function processTeamfights(entries, meta) { const heroToSlot = meta.hero_to_slot; for (let i = 0; i < entries.length; i += 1) { const e = entries[i]; - if (e.type === "killed" && e.targethero && !e.targetillusion) { + if (e.type === 'killed' && e.targethero && !e.targetillusion) { // check teamfight state currTeamfight = currTeamfight || { start: e.time - teamfightCooldown, @@ -35,7 +35,7 @@ function processTeamfights(entries, meta) { // update the last_death time of the current fight currTeamfight.last_death = e.time; currTeamfight.deaths += 1; - } else if (e.type === "interval") { + } else if (e.type === 'interval') { // store hero state at each interval for teamfight lookup if (!intervalState[e.time]) { intervalState[e.time] = {}; @@ -73,7 +73,7 @@ function processTeamfights(entries, meta) { for (let j = 0; j < teamfights.length; j += 1) { const tf = teamfights[j]; if (e.time >= tf.start && e.time <= tf.end) { - if (e.type === "killed" && e.targethero && !e.targetillusion) { + if (e.type === 'killed' && e.targethero && !e.targetillusion) { populate(e, tf); // reverse the kill entry to find killed hero const r = { @@ -87,19 +87,19 @@ function processTeamfights(entries, meta) { // get position from intervalstate const { x, y } = intervalState[r.time][r.slot]; // fill in the copy - r.type = "deaths_pos"; + r.type = 'deaths_pos'; r.key = JSON.stringify([x, y]); r.posData = true; populate(r, tf); // increment death count for this hero tf.players[r.slot].deaths += 1; } - } else if (e.type === "buyback_log") { + } else if (e.type === 'buyback_log') { // bought back if (tf.players[e.slot]) { tf.players[e.slot].buybacks += 1; } - } else if (e.type === "damage") { + } else if (e.type === 'damage') { // sum damage // check if damage dealt to hero and not illusion if (e.targethero && !e.targetillusion) { @@ -108,7 +108,7 @@ function processTeamfights(entries, meta) { tf.players[e.slot].damage += e.value; } } - } else if (e.type === "healing") { + } else if (e.type === 'healing') { // sum healing // check if healing dealt to hero and not illusion if (e.targethero && !e.targetillusion) { @@ -117,16 +117,16 @@ function processTeamfights(entries, meta) { tf.players[e.slot].healing += e.value; } } - } else if (e.type === "gold_reasons" || e.type === "xp_reasons") { + } else if (e.type === 'gold_reasons' || e.type === 'xp_reasons') { // add gold/xp to delta if (tf.players[e.slot]) { const types = { - gold_reasons: "gold_delta", - xp_reasons: "xp_delta", + gold_reasons: 'gold_delta', + xp_reasons: 'xp_delta', }; tf.players[e.slot][types[e.type]] += e.value; } - } else if (e.type === "ability_uses" || e.type === "item_uses") { + } else if (e.type === 'ability_uses' || e.type === 'item_uses') { // count skills, items populate(e, tf); } diff --git a/processors/processUploadProps.js b/processors/processUploadProps.js index 2cbf2b7eb..d59eaa235 100644 --- a/processors/processUploadProps.js +++ b/processors/processUploadProps.js @@ -10,7 +10,7 @@ function processUploadProps(entries) { const e = entries[i]; let dota; switch (e.type) { - case "epilogue": + case 'epilogue': dota = JSON.parse(e.key).gameInfo_.dota_; container.match_id = dota.matchId_; container.game_mode = dota.gameMode_; @@ -19,7 +19,7 @@ function processUploadProps(entries) { // container.picks_bans = dota.picksBans_; // require('fs').writeFileSync('./outputEpilogue.json', JSON.stringify(JSON.parse(e.key))); break; - case "interval": + case 'interval': if (!container.player_map[e.player_slot]) { container.player_map[e.player_slot] = {}; } diff --git a/routes/api.js b/routes/api.js index e7210d071..817b92c06 100644 --- a/routes/api.js +++ b/routes/api.js @@ -1,21 +1,21 @@ -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"); +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'); const api = new express.Router(); const { subkeys } = playerFields; -const admins = config.ADMIN_ACCOUNT_IDS.split(",").map((e) => Number(e)); +const admins = config.ADMIN_ACCOUNT_IDS.split(',').map((e) => Number(e)); // Player caches middleware -api.use("/players/:account_id/:info?", (req, res, cb) => { +api.use('/players/:account_id/:info?', (req, res, cb) => { // Check cache if (!Object.keys(req.query).length && req.params.info) { return cacheFunctions.read( @@ -45,13 +45,13 @@ api.use("/players/:account_id/:info?", (req, res, cb) => { }); // Player endpoints middleware -api.use("/players/:account_id/:info?", (req, res, cb) => { +api.use('/players/:account_id/:info?', (req, res, cb) => { if (Number.isNaN(Number(req.params.account_id))) { - return res.status(400).json({ error: "invalid account id" }); + return res.status(400).json({ error: 'invalid account id' }); } req.originalQuery = JSON.parse(JSON.stringify(req.query)); // Enable significance filter by default, disable it if 0 is passed - if (req.query.significant === "0") { + if (req.query.significant === '0') { delete req.query.significant; } else { req.query.significant = 1; @@ -66,7 +66,7 @@ api.use("/players/:account_id/:info?", (req, res, cb) => { filterCols = filterCols.concat(filterDeps[key] || []); }); req.queryObj = { - project: ["match_id", "player_slot", "radiant_win"] + project: ['match_id', 'player_slot', 'radiant_win'] .concat(filterCols) .concat((req.query.sort || []).filter((f) => subkeys[f])), filter: req.query || {}, @@ -78,34 +78,34 @@ api.use("/players/:account_id/:info?", (req, res, cb) => { return cb(); }); -api.use("/teams/:team_id/:info?", (req, res, cb) => { +api.use('/teams/:team_id/:info?', (req, res, cb) => { if (Number.isNaN(Number(req.params.team_id))) { - return res.status(400).json({ error: "invalid team id" }); + return res.status(400).json({ error: 'invalid team id' }); } return cb(); }); -api.use("/request/:jobId", (req, res, cb) => { +api.use('/request/:jobId', (req, res, cb) => { if (Number.isNaN(Number(req.params.jobId))) { - return res.status(400).json({ error: "invalid job id" }); + return res.status(400).json({ error: 'invalid job id' }); } return cb(); }); // Admin endpoints middleware -api.use("/admin*", (req, res, cb) => { +api.use('/admin*', (req, res, cb) => { if (req.user && admins.includes(req.user.account_id)) { return cb(); } return res.status(403).json({ - error: "Access Denied", + error: 'Access Denied', }); }); -api.get("/admin/apiMetrics", (req, res) => { - const startTime = moment().startOf("month").format("YYYY-MM-DD"); - const endTime = moment().endOf("month").format("YYYY-MM-DD"); +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( { @@ -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); + redis.zrevrange('user_usage_count', 0, 24, 'WITHSCORES', cb); }, numUsersIP: (cb) => { - redis.zcard("user_usage_count", cb); + redis.zcard('user_usage_count', cb); }, }, (err, result) => { @@ -192,7 +192,7 @@ api.get("/admin/apiMetrics", (req, res) => { }); // API spec -api.get("/", (req, res) => { +api.get('/', (req, res) => { res.json(spec); }); @@ -203,9 +203,9 @@ Object.keys(spec.paths).forEach((path) => { // 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() - : path.replace(/{/g, ":").replace(/}/g, ""); + : path.replace(/{/g, ':').replace(/}/g, ''); // Check if the callback function is defined before adding the route.. - if (typeof func === "function") { + if (typeof func === 'function') { api[verb](routePath, func); } else { // If the function is missing, log a warning message with the problematic route path and verb diff --git a/routes/generateOperationId.js b/routes/generateOperationId.js index c094e539a..e995337c0 100644 --- a/routes/generateOperationId.js +++ b/routes/generateOperationId.js @@ -5,30 +5,29 @@ function generateOperationId(method, path) { // Split the path into segments - const pathSegments = path.split("/"); + const pathSegments = path.split('/'); // Remove the first segment if it's empty (because the path starts with a slash) - if (pathSegments[0] === "") { + if (pathSegments[0] === '') { pathSegments.shift(); } // Convert path parameters to the format "by_{parameter}" const segmentsWithParametersReplaced = pathSegments.map((segment) => - segment.replace(/{(.*?)}/g, "by_$1") + segment.replace(/{(.*?)}/g, 'by_$1') ); // If there are 3 elements in the path, prefix the last one with "select_" if (segmentsWithParametersReplaced.length === 3) { - segmentsWithParametersReplaced[2] = - `select_${ segmentsWithParametersReplaced[2]}`; + segmentsWithParametersReplaced[2] = `select_${segmentsWithParametersReplaced[2]}`; } // Join all segments with underscores - const pathWithUnderscores = segmentsWithParametersReplaced.join("_"); + const pathWithUnderscores = segmentsWithParametersReplaced.join('_'); // Convert camelCase to snake_case const snakeCaseBase = pathWithUnderscores - .replace(/([A-Z])/g, "_$1") + .replace(/([A-Z])/g, '_$1') .toLowerCase(); // Return the method and the path joined with underscores diff --git a/routes/keyManagement.js b/routes/keyManagement.js index f90573ced..8bdfdf073 100644 --- a/routes/keyManagement.js +++ b/routes/keyManagement.js @@ -1,12 +1,12 @@ -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 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; @@ -22,7 +22,7 @@ keys.use( keys.use((req, res, next) => { if (!req.user) { return res.status(403).json({ - error: "Authentication required", + error: 'Authentication required', }); } @@ -50,7 +50,7 @@ async function getOpenInvoices(customerId) { const invoices = await stripe.invoices.list({ customer: customerId, limit: 100, - status: "open", + status: 'open', }); return invoices.data; @@ -60,9 +60,9 @@ async function getOpenInvoices(customerId) { * Invariant: A Stripe subscription and an API key is a 1 to 1 mapping. canceled sub = deleted key and vice versa a single user can have multiple subs but only one active at a given time (others have is_canceled = true). */ keys - .route("/") + .route('/') .all(async (req, res, next) => { - const rows = await db.from("api_keys").where({ + const rows = await db.from('api_keys').where({ account_id: req.user.account_id, }); @@ -150,8 +150,8 @@ keys ORDER BY month DESC `, [ - moment().subtract(5, "month").startOf("month"), - moment().endOf("month"), + moment().subtract(5, 'month').startOf('month'), + moment().endOf('month'), req.user.account_id, ] ).asCallback((err, results) => cb(err, err ? null : results.rows)); @@ -180,7 +180,7 @@ keys await stripe.subscriptions.del(subscription_id, { invoice_now: true }); await db - .from("api_keys") + .from('api_keys') .where({ account_id: req.user.account_id, subscription_id, @@ -190,7 +190,7 @@ keys }); // Force the key to be disabled - redis.srem("api_keys", api_key, (err) => { + redis.srem('api_keys', api_key, (err) => { if (err) { throw err; } @@ -203,7 +203,7 @@ keys if (!hasToken(req)) { return res.status(500).json({ - error: "Missing token", + error: 'Missing token', }); } @@ -213,7 +213,7 @@ keys let customer_id; if (hasActiveKey(keyRecord)) { - console.log("Active key exists for", req.user.account_id); + console.log('Active key exists for', req.user.account_id); return res.sendStatus(200); } // returning customer @@ -224,12 +224,12 @@ keys if (invoices.length > 0) { console.log( - "Open invoices exist for", + 'Open invoices exist for', req.user.account_id, - "customer", + 'customer', customer_id ); - return res.status(402).json({ error: "Open invoice" }); + return res.status(402).json({ error: 'Open invoice' }); } try { @@ -264,7 +264,7 @@ keys const sub = await stripe.subscriptions.create({ customer: customer_id, items: [{ plan: stripeAPIPlan }], - billing_cycle_anchor: moment().add(1, "month").startOf("month").unix(), + billing_cycle_anchor: moment().add(1, 'month').startOf('month').unix(), metadata: { apiKey, }, @@ -289,7 +289,7 @@ keys ); // Add the key to Redis so that it works immediately - redis.sadd("api_keys", apiKey, (err) => { + redis.sadd('api_keys', apiKey, (err) => { if (err) { throw err; } @@ -302,14 +302,14 @@ keys if (!hasToken(req)) { return res.status(400).json({ - error: "Missing token", + error: 'Missing token', }); } const { keyRecord } = res.locals; if (!hasActiveKey(keyRecord)) { - throw Error("No record to update."); + throw Error('No record to update.'); } const { customer_id, subscription_id } = keyRecord; diff --git a/routes/requests/importParams.js b/routes/requests/importParams.js index 357229907..29bfd8046 100644 --- a/routes/requests/importParams.js +++ b/routes/requests/importParams.js @@ -1,5 +1,5 @@ -const fs = require("fs"); -const path = require("path"); +const fs = require('fs'); +const path = require('path'); // Recursive function to import all files in a directory and its subdirectories function importAll(directory) { @@ -12,9 +12,9 @@ function importAll(directory) { 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) { + } 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"); + const fileName = path.basename(item, '.js'); files[fileName] = require(itemPath); } }); diff --git a/routes/requests/queryParams/heroParams.js b/routes/requests/queryParams/heroParams.js index 7c866d53f..164d60d3e 100644 --- a/routes/requests/queryParams/heroParams.js +++ b/routes/requests/queryParams/heroParams.js @@ -1,11 +1,11 @@ module.exports = { heroIdPathParam: { - name: "hero_id", - in: "path", - description: "Hero ID", + name: 'hero_id', + in: 'path', + description: 'Hero ID', required: true, schema: { - type: "integer", + type: 'integer', }, }, }; diff --git a/routes/requests/queryParams/leagueParams.js b/routes/requests/queryParams/leagueParams.js index ef6c81821..91c3b4a0f 100644 --- a/routes/requests/queryParams/leagueParams.js +++ b/routes/requests/queryParams/leagueParams.js @@ -1,11 +1,11 @@ module.exports = { leagueIdPathParam: { - name: "league_id", - in: "path", - description: "League ID", + name: 'league_id', + in: 'path', + description: 'League ID', required: true, schema: { - type: "integer", + type: 'integer', }, }, }; diff --git a/routes/requests/queryParams/matchParams.js b/routes/requests/queryParams/matchParams.js index 212c36a8b..ee338039d 100644 --- a/routes/requests/queryParams/matchParams.js +++ b/routes/requests/queryParams/matchParams.js @@ -1,58 +1,58 @@ module.exports = { matchIdParam: { - name: "match_id", - in: "path", + name: 'match_id', + in: 'path', required: true, schema: { - type: "integer", + type: 'integer', }, }, // for /publicMatches: lessThanMatchIdParam: { - name: "less_than_match_id", - in: "query", - description: "Get matches with a match ID lower than this value", + name: 'less_than_match_id', + in: 'query', + description: 'Get matches with a match ID lower than this value', required: false, schema: { - type: "integer", + type: 'integer', }, }, minRankParam: { - name: "min_rank", - in: "query", + 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.", + '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", + type: 'integer', }, }, maxRankParam: { - name: "max_rank", - in: "query", + 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.", + '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", + type: 'integer', }, }, mmrAscendingParam: { - name: "mmr_ascending", - in: "query", - description: "Order by MMR ascending", + name: 'mmr_ascending', + in: 'query', + description: 'Order by MMR ascending', required: false, schema: { - type: "integer", + type: 'integer', }, }, mmrDescendingParam: { - name: "mmr_descending", - in: "query", - description: "Order by MMR descending", + name: 'mmr_descending', + in: 'query', + description: 'Order by MMR descending', required: false, schema: { - type: "integer", + type: 'integer', }, }, }; diff --git a/routes/requests/queryParams/playerParams.js b/routes/requests/queryParams/playerParams.js index 79c99d075..9de4389c0 100644 --- a/routes/requests/queryParams/playerParams.js +++ b/routes/requests/queryParams/playerParams.js @@ -1,195 +1,195 @@ module.exports = { accountIdParam: { - name: "account_id", - in: "path", - description: "Steam32 account ID", + name: 'account_id', + in: 'path', + description: 'Steam32 account ID', required: true, schema: { - type: "integer", + type: 'integer', }, }, // for /players/{account_id}/histograms/{field}: fieldParam: { - name: "field", - in: "path", - description: "Field to aggregate on", + name: 'field', + in: 'path', + description: 'Field to aggregate on', required: true, schema: { - type: "string", + type: 'string', }, }, // for /players/{account_id}/matches projectParam: { - name: "project", - in: "query", - description: "Fields to project (array)", + name: 'project', + in: 'query', + description: 'Fields to project (array)', required: false, schema: { - type: "string", + type: 'string', }, }, // playerParamNames: limitParam: { - name: "limit", - in: "query", - description: "Number of matches to limit to", + name: 'limit', + in: 'query', + description: 'Number of matches to limit to', required: false, schema: { - type: "integer", + type: 'integer', }, }, offsetParam: { - name: "offset", - in: "query", - description: "Number of matches to offset start by", + name: 'offset', + in: 'query', + description: 'Number of matches to offset start by', required: false, schema: { - type: "integer", + type: 'integer', }, }, winParam: { - name: "win", - in: "query", - description: "Whether the player won", + name: 'win', + in: 'query', + description: 'Whether the player won', required: false, schema: { - type: "integer", + type: 'integer', }, }, patchParam: { - name: "patch", - in: "query", - description: "Patch ID", + name: 'patch', + in: 'query', + description: 'Patch ID', required: false, schema: { - type: "integer", + type: 'integer', }, }, gameModeParam: { - name: "game_mode", - in: "query", - description: "Game Mode ID", + name: 'game_mode', + in: 'query', + description: 'Game Mode ID', required: false, schema: { - type: "integer", + type: 'integer', }, }, lobbyTypeParam: { - name: "lobby_type", - in: "query", - description: "Lobby type ID", + name: 'lobby_type', + in: 'query', + description: 'Lobby type ID', required: false, schema: { - type: "integer", + type: 'integer', }, }, regionParam: { - name: "region", - in: "query", - description: "Region ID", + name: 'region', + in: 'query', + description: 'Region ID', required: false, schema: { - type: "integer", + type: 'integer', }, }, dateParam: { - name: "date", - in: "query", - description: "Days previous", + name: 'date', + in: 'query', + description: 'Days previous', required: false, schema: { - type: "integer", + type: 'integer', }, }, laneRoleParam: { - name: "lane_role", - in: "query", - description: "Lane Role ID", + name: 'lane_role', + in: 'query', + description: 'Lane Role ID', required: false, schema: { - type: "integer", + type: 'integer', }, }, heroIdParam: { - name: "hero_id", - in: "query", - description: "Hero ID", + name: 'hero_id', + in: 'query', + description: 'Hero ID', required: false, schema: { - type: "integer", + type: 'integer', }, }, isRadiantParam: { - name: "is_radiant", - in: "query", - description: "Whether the player was radiant", + name: 'is_radiant', + in: 'query', + description: 'Whether the player was radiant', required: false, schema: { - type: "integer", + type: 'integer', }, }, includedAccountIdParam: { - name: "included_account_id", - in: "query", - description: "Account IDs in the match (array)", + name: 'included_account_id', + in: 'query', + description: 'Account IDs in the match (array)', required: false, schema: { - type: "integer", + type: 'integer', }, }, excludedAccountIdParam: { - name: "excluded_account_id", - in: "query", - description: "Account IDs not in the match (array)", + name: 'excluded_account_id', + in: 'query', + description: 'Account IDs not in the match (array)', required: false, schema: { - type: "integer", + type: 'integer', }, }, withHeroIdParam: { - name: "with_hero_id", - in: "query", + name: 'with_hero_id', + in: 'query', description: "Hero IDs on the player's team (array)", required: false, schema: { - type: "integer", + type: 'integer', }, }, againstHeroIdParam: { - name: "against_hero_id", - in: "query", + name: 'against_hero_id', + in: 'query', description: "Hero IDs against the player's team (array)", required: false, schema: { - type: "integer", + type: 'integer', }, }, significantParam: { - name: "significant", - in: "query", + 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.", + '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: { - name: "having", - in: "query", - description: "The minimum number of games played, for filtering hero stats", + name: 'having', + in: 'query', + description: 'The minimum number of games played, for filtering hero stats', required: false, schema: { - type: "integer", + type: 'integer', }, }, sortParam: { - name: "sort", - in: "query", - description: "The field to return matches sorted by in descending order", + 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/scenarioParams.js b/routes/requests/queryParams/scenarioParams.js index 470f490be..5e23eabeb 100644 --- a/routes/requests/queryParams/scenarioParams.js +++ b/routes/requests/queryParams/scenarioParams.js @@ -1,13 +1,13 @@ -const su = require("../../../util/scenariosUtil"); +const su = require('../../../util/scenariosUtil'); module.exports = { scenarioParam: { - name: "scenario", - in: "query", + name: 'scenario', + in: 'query', description: su.teamScenariosQueryParams.toString(), required: false, schema: { - type: "string", + type: 'string', }, }, }; diff --git a/routes/requests/queryParams/teamParams.js b/routes/requests/queryParams/teamParams.js index 1f67058c5..1adbdce54 100644 --- a/routes/requests/queryParams/teamParams.js +++ b/routes/requests/queryParams/teamParams.js @@ -1,11 +1,11 @@ module.exports = { teamIdPathParam: { - name: "team_id", - in: "path", - description: "Team ID", + name: 'team_id', + in: 'path', + description: 'Team ID', required: true, schema: { - type: "integer", + type: 'integer', }, }, }; diff --git a/routes/responses/properties/commonProperties.js b/routes/responses/properties/commonProperties.js index ccbd1722d..d4702ccd5 100644 --- a/routes/responses/properties/commonProperties.js +++ b/routes/responses/properties/commonProperties.js @@ -1,78 +1,78 @@ module.exports = { hero_id: { - description: "The ID value of the hero played", - type: "integer", + description: 'The ID value of the hero played', + type: 'integer', }, match_id: { - description: "The ID number of the match assigned by Valve", - type: "integer", + description: 'The ID number of the match assigned by Valve', + type: 'integer', example: 3703866531, }, radiant_win: { - description: "Boolean indicating whether Radiant won the match", - type: "boolean", + description: 'Boolean indicating whether Radiant won the match', + type: 'boolean', nullable: true, }, player_slot: { description: - "Which slot the player is in. 0-127 are Radiant, 128-255 are Dire", - type: "integer", + 'Which slot the player is in. 0-127 are Radiant, 128-255 are Dire', + type: 'integer', nullable: true, }, duration: { - description: "Duration of the game in seconds", - type: "integer", + description: 'Duration of the game in seconds', + type: 'integer', }, start_time: { - description: "The Unix timestamp at which the game started", - type: "integer", + description: 'The Unix timestamp at which the game started', + type: 'integer', }, dire_score: { - description: "Number of kills the Dire team had when the match ended", - type: "integer", + description: 'Number of kills the Dire team had when the match ended', + type: 'integer', }, radiant_score: { - description: "Number of kills the Radiant team had when the match ended", - type: "integer", + description: 'Number of kills the Radiant team had when the match ended', + type: 'integer', }, account_id: { - description: "The player account ID", - type: "integer", + description: 'The player account ID', + type: 'integer', }, hero_name: { - description: "Hero name", - type: "string", - example: "Anti-Mage", + description: 'Hero name', + type: 'string', + example: 'Anti-Mage', }, hero_command_name: { - description: "Dota hero command name", - type: "string", - example: "npc_dota_hero_antimage", + description: 'Dota hero command name', + type: 'string', + example: 'npc_dota_hero_antimage', }, persona_name: { description: "Player's Steam name", - type: "string", + type: 'string', nullable: true, - example: "420 booty wizard", + example: '420 booty wizard', }, team_name: { - description: "Team name", - type: "string", - example: "Newbee", + description: 'Team name', + type: 'string', + example: 'Newbee', nullable: true, }, league_name: { - description: "League name", - type: "string", - example: "ASUS ROG DreamLeague Season 4", + description: 'League name', + type: 'string', + example: 'ASUS ROG DreamLeague Season 4', }, general_name: { - description: "name", - type: "string", + description: 'name', + type: 'string', nullable: true, }, field_name: { - description: "Field name", - type: "string", + description: 'Field name', + type: 'string', }, }; diff --git a/routes/responses/schemas/hero/HeroDurationsResponse.js b/routes/responses/schemas/hero/HeroDurationsResponse.js index f5a97baa2..68ea142e6 100644 --- a/routes/responses/schemas/hero/HeroDurationsResponse.js +++ b/routes/responses/schemas/hero/HeroDurationsResponse.js @@ -1,19 +1,19 @@ module.exports = { HeroDurationsResponse: { - title: "HeroDurationsResponse", - type: "object", + title: 'HeroDurationsResponse', + type: 'object', properties: { duration_bin: { - description: "Lower bound of number of seconds the match lasted", - type: "string", + description: 'Lower bound of number of seconds the match lasted', + type: 'string', }, games_played: { - description: "Number of games played", - type: "integer", + description: 'Number of games played', + type: 'integer', }, wins: { - description: "Number of wins", - type: "integer", + description: 'Number of wins', + type: 'integer', }, }, }, diff --git a/routes/responses/schemas/hero/HeroItemPopularityResponse.js b/routes/responses/schemas/hero/HeroItemPopularityResponse.js index 3283dbeb3..a5d64dd73 100644 --- a/routes/responses/schemas/hero/HeroItemPopularityResponse.js +++ b/routes/responses/schemas/hero/HeroItemPopularityResponse.js @@ -1,48 +1,48 @@ module.exports = { HeroItemPopularityResponse: { - title: "HeroItemPopularityResponse", - type: "object", + title: 'HeroItemPopularityResponse', + type: 'object', properties: { start_game_items: { - description: "Items bought before game started", - type: "object", + description: 'Items bought before game started', + type: 'object', properties: { item: { - description: "Number of item bought", - type: "integer", + 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", + '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", + 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", + '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", + 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", + '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", + description: 'Number of item bought', + type: 'integer', }, }, }, diff --git a/routes/responses/schemas/hero/HeroMatchupsResponse.js b/routes/responses/schemas/hero/HeroMatchupsResponse.js index e4f794cdf..fce077872 100644 --- a/routes/responses/schemas/hero/HeroMatchupsResponse.js +++ b/routes/responses/schemas/hero/HeroMatchupsResponse.js @@ -1,18 +1,18 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { HeroMatchupsResponse: { - title: "HeroMatchupsResponse", - type: "object", + title: 'HeroMatchupsResponse', + type: 'object', properties: { hero_id: commonProperties.hero_id, games_played: { - description: "Number of games played", - type: "integer", + description: 'Number of games played', + type: 'integer', }, wins: { - description: "Number of games won", - type: "integer", + description: 'Number of games won', + type: 'integer', }, }, }, diff --git a/routes/responses/schemas/hero/HeroObjectResponse.js b/routes/responses/schemas/hero/HeroObjectResponse.js index f5ac06505..695177ae9 100644 --- a/routes/responses/schemas/hero/HeroObjectResponse.js +++ b/routes/responses/schemas/hero/HeroObjectResponse.js @@ -1,29 +1,29 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { HeroObjectResponse: { - title: "HeroObjectResponse", - type: "object", + 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", + type: 'string', }, attack_type: { description: "Hero attack type, either 'Melee' or 'Ranged'", - type: "string", + type: 'string', }, roles: { - type: "array", + type: 'array', items: { description: "A hero's role in the game", - type: "string", + type: 'string', }, }, }, - required: ["id"], + required: ['id'], }, }; diff --git a/routes/responses/schemas/hero/HeroStatsResponse.js b/routes/responses/schemas/hero/HeroStatsResponse.js index ec6facb1f..4f1a18cb6 100644 --- a/routes/responses/schemas/hero/HeroStatsResponse.js +++ b/routes/responses/schemas/hero/HeroStatsResponse.js @@ -1,229 +1,229 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { HeroStatsResponse: { - title: "HeroStatsResponse", - type: "object", + 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", + description: 'primary_attr', + type: 'string', }, attack_type: { - description: "attack_type", - type: "string", + description: 'attack_type', + type: 'string', }, roles: { - description: "roles", - type: "array", + description: 'roles', + type: 'array', items: { - type: "string", + type: 'string', }, }, img: { - description: "img", - type: "string", + description: 'img', + type: 'string', }, icon: { - description: "icon", - type: "string", + description: 'icon', + type: 'string', }, base_health: { - description: "base_health", - type: "integer", + description: 'base_health', + type: 'integer', }, base_health_regen: { - description: "base_health_regen", - type: "number", + description: 'base_health_regen', + type: 'number', }, base_mana: { - description: "base_mana", - type: "integer", + description: 'base_mana', + type: 'integer', }, base_mana_regen: { - description: "base_mana_regen", - type: "integer", + description: 'base_mana_regen', + type: 'integer', }, base_armor: { - description: "base_armor", - type: "integer", + description: 'base_armor', + type: 'integer', }, base_mr: { - description: "base_mr", - type: "integer", + description: 'base_mr', + type: 'integer', }, base_attack_min: { - description: "base_attack_min", - type: "integer", + description: 'base_attack_min', + type: 'integer', }, base_attack_max: { - description: "base_attack_max", - type: "integer", + description: 'base_attack_max', + type: 'integer', }, base_str: { - description: "base_str", - type: "integer", + description: 'base_str', + type: 'integer', }, base_agi: { - description: "base_agi", - type: "integer", + description: 'base_agi', + type: 'integer', }, base_int: { - description: "base_int", - type: "integer", + description: 'base_int', + type: 'integer', }, str_gain: { - description: "str_gain", - type: "number", + description: 'str_gain', + type: 'number', }, agi_gain: { - description: "agi_gain", - type: "number", + description: 'agi_gain', + type: 'number', }, int_gain: { - description: "int_gain", - type: "number", + description: 'int_gain', + type: 'number', }, attack_range: { - description: "attack_range", - type: "integer", + description: 'attack_range', + type: 'integer', }, projectile_speed: { - description: "projectile_speed", - type: "integer", + description: 'projectile_speed', + type: 'integer', }, attack_rate: { - description: "attack_rate", - type: "number", + description: 'attack_rate', + type: 'number', }, base_attack_time: { - description: "base_attack_time", - type: "integer", + description: 'base_attack_time', + type: 'integer', }, attack_point: { - description: "attack_point", - type: "number", + description: 'attack_point', + type: 'number', }, move_speed: { - description: "move_speed", - type: "integer", + description: 'move_speed', + type: 'integer', }, turn_rate: { - description: "turn_rate", - type: "number", + description: 'turn_rate', + type: 'number', }, cm_enabled: { - description: "cm_enabled", - type: "boolean", + description: 'cm_enabled', + type: 'boolean', }, legs: { - description: "legs", - type: "integer", + description: 'legs', + type: 'integer', }, day_vision: { - description: "day_vision", - type: "integer", + description: 'day_vision', + type: 'integer', }, night_vision: { - description: "night_vision", - type: "integer", + description: 'night_vision', + type: 'integer', }, hero_id: commonProperties.hero_id, // TODO: Duplicate turbo_picks: { - description: "Picks in Turbo mode this month", - type: "integer", + description: 'Picks in Turbo mode this month', + type: 'integer', }, turbo_wins: { - description: "Wins in Turbo mode this month", - type: "integer", + description: 'Wins in Turbo mode this month', + type: 'integer', }, pro_ban: { - description: "pro_ban", - type: "integer", + description: 'pro_ban', + type: 'integer', }, pro_win: { - description: "pro_win", - type: "integer", + description: 'pro_win', + type: 'integer', }, pro_pick: { - description: "pro_pick", - type: "integer", + description: 'pro_pick', + type: 'integer', }, - "1_pick": { - description: "Herald picks", - type: "integer", + '1_pick': { + description: 'Herald picks', + type: 'integer', }, - "1_win": { - description: "Herald wins", - type: "integer", + '1_win': { + description: 'Herald wins', + type: 'integer', }, - "2_pick": { - description: "Guardian picks", - type: "integer", + '2_pick': { + description: 'Guardian picks', + type: 'integer', }, - "2_win": { - description: "Guardian wins", - type: "integer", + '2_win': { + description: 'Guardian wins', + type: 'integer', }, - "3_pick": { - description: "Crusader picks", - type: "integer", + '3_pick': { + description: 'Crusader picks', + type: 'integer', }, - "3_win": { - description: "Crusader wins", - type: "integer", + '3_win': { + description: 'Crusader wins', + type: 'integer', }, - "4_pick": { - description: "Archon picks", - type: "integer", + '4_pick': { + description: 'Archon picks', + type: 'integer', }, - "4_win": { - description: "Archon wins", - type: "integer", + '4_win': { + description: 'Archon wins', + type: 'integer', }, - "5_pick": { - description: "Legend picks", - type: "integer", + '5_pick': { + description: 'Legend picks', + type: 'integer', }, - "5_win": { - description: "Legend wins", - type: "integer", + '5_win': { + description: 'Legend wins', + type: 'integer', }, - "6_pick": { - description: "Ancient picks", - type: "integer", + '6_pick': { + description: 'Ancient picks', + type: 'integer', }, - "6_win": { - description: "Ancient wins", - type: "integer", + '6_win': { + description: 'Ancient wins', + type: 'integer', }, - "7_pick": { - description: "Divine picks", - type: "integer", + '7_pick': { + description: 'Divine picks', + type: 'integer', }, - "7_win": { - description: "Divine wins", - type: "integer", + '7_win': { + description: 'Divine wins', + type: 'integer', }, - "8_pick": { - description: "Immortal picks", - type: "integer", + '8_pick': { + description: 'Immortal picks', + type: 'integer', }, - "8_win": { - description: "Immortal wins", - type: "integer", + '8_win': { + description: 'Immortal wins', + type: 'integer', }, // TODO: Should the following remain in the response? null_pick: { - description: "null_pick", - type: "integer", + description: 'null_pick', + type: 'integer', }, null_win: { - description: "null_win", - type: "integer", + description: 'null_win', + type: 'integer', }, }, }, diff --git a/routes/responses/schemas/importResponseSchemas.js b/routes/responses/schemas/importResponseSchemas.js index c59878afa..55426d86a 100644 --- a/routes/responses/schemas/importResponseSchemas.js +++ b/routes/responses/schemas/importResponseSchemas.js @@ -1,5 +1,5 @@ -const fs = require("fs"); -const path = require("path"); +const fs = require('fs'); +const path = require('path'); // Recursive function to import all files in a directory and its subdirectories function importAll(directory) { @@ -12,9 +12,9 @@ function importAll(directory) { 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) { + } 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"); + const fileName = path.basename(item, '.js'); files[fileName] = require(itemPath); } }); diff --git a/routes/responses/schemas/match/MatchObjectResponse.js b/routes/responses/schemas/match/MatchObjectResponse.js index bdc07aa45..845b47f2c 100644 --- a/routes/responses/schemas/match/MatchObjectResponse.js +++ b/routes/responses/schemas/match/MatchObjectResponse.js @@ -1,51 +1,51 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { MatchObjectResponse: { - title: "MatchObjectResponse", - type: "object", + 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", + type: 'integer', }, radiant_name: { description: "The Radiant's team name", - type: "string", + type: 'string', }, dire_team_id: { description: "The Dire's team_id", - type: "integer", + type: 'integer', }, dire_name: { description: "The Dire's team name", - type: "string", + type: 'string', }, leagueid: { - description: "Identifier for the league the match took place in", - type: "integer", + 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", + description: 'Name of league the match took place in', + type: 'string', }, series_id: { - description: "Identifier for the series of the match", - type: "integer", + description: 'Identifier for the series of the match', + type: 'integer', }, series_type: { - description: "Type of series the match was", - type: "integer", + 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", + 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 index 330b73cf7..c44a77907 100644 --- a/routes/responses/schemas/match/MatchResponse.js +++ b/routes/responses/schemas/match/MatchResponse.js @@ -1,905 +1,905 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { MatchResponse: { - title: "MatchResponse", - type: "object", + 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", + '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", + '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", + description: 'Array containing information on the chat of the game', + type: 'array', items: { - type: "object", + type: 'object', properties: { time: { - description: "Time in seconds at which the message was said", - type: "integer", + description: 'Time in seconds at which the message was said', + type: 'integer', }, unit: { - description: "Name of the player who sent the message", - type: "string", + description: 'Name of the player who sent the message', + type: 'string', }, key: { - description: "The message the player sent", - type: "string", + description: 'The message the player sent', + type: 'string', }, slot: { - description: "slot", - type: "integer", + description: 'slot', + type: 'integer', }, player_slot: commonProperties.player_slot, }, }, }, cluster: { - description: "cluster", - type: "integer", + description: 'cluster', + type: 'integer', }, cosmetics: { - description: "cosmetics", - type: "object", + description: 'cosmetics', + type: 'object', additionalProperties: { - type: "integer", + type: 'integer', }, }, dire_score: commonProperties.dire_score, draft_timings: { - description: "draft_timings", - type: "array", + description: 'draft_timings', + type: 'array', items: { - description: "draft_stage", - type: "object", + description: 'draft_stage', + type: 'object', properties: { order: { - description: "order", - type: "integer", + description: 'order', + type: 'integer', }, pick: { - description: "pick", - type: "boolean", + description: 'pick', + type: 'boolean', }, active_team: { - description: "active_team", - type: "integer", + description: 'active_team', + type: 'integer', }, hero_id: commonProperties.hero_id, player_slot: commonProperties.player_slot, extra_time: { - description: "extra_time", - type: "integer", + description: 'extra_time', + type: 'integer', }, total_time_taken: { - description: "total_time_taken", - type: "integer", + description: 'total_time_taken', + type: 'integer', }, }, }, }, duration: commonProperties.duration, engine: { - description: "engine", - type: "integer", + description: 'engine', + type: 'integer', }, first_blood_time: { - description: "Time in seconds at which first blood occurred", - type: "integer", + 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", + '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", + description: 'Number of human players in the game', + type: 'integer', }, leagueid: { - description: "leagueid", - type: "integer", + 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", + '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", + description: 'match_seq_num', + type: 'integer', }, negative_votes: { description: - "Number of negative votes the replay received in the in-game client", - type: "integer", + 'Number of negative votes the replay received in the in-game client', + type: 'integer', }, objectives: { - description: "objectives", - type: "array", + description: 'objectives', + type: 'array', items: { - type: "object", + 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", + '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", + type: 'object', properties: { is_pick: { description: - "Boolean indicating whether the choice is a pick or a ban", - type: "boolean", + '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", + description: 'The team that picked or banned the hero', + type: 'integer', }, order: { - description: "The order of the pick or ban", - type: "integer", + 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", + '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", + '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", + 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", + '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", + type: 'number', }, }, start_time: commonProperties.start_time, teamfights: { - description: "teamfights", - type: "array", + description: 'teamfights', + type: 'array', items: { - type: "object", + type: 'object', }, nullable: true, }, tower_status_dire: { description: - "Bitmask. An integer that represents a binary of which Dire towers are still standing.", - type: "integer", + '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", + '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", + description: 'Parse version, used internally by OpenDota', + type: 'integer', }, replay_salt: { - description: "replay_salt", - type: "integer", + description: 'replay_salt', + type: 'integer', }, series_id: { - description: "series_id", - type: "integer", + description: 'series_id', + type: 'integer', }, series_type: { - description: "series_type", - type: "integer", + description: 'series_type', + type: 'integer', }, radiant_team: { - description: "radiant_team", - type: "object", + description: 'radiant_team', + type: 'object', }, dire_team: { - description: "dire_team", - type: "object", + description: 'dire_team', + type: 'object', }, league: { - description: "league", - type: "object", + description: 'league', + type: 'object', }, skill: { description: - "Skill bracket assigned by Valve (Normal, High, Very High)", - type: "integer", + 'Skill bracket assigned by Valve (Normal, High, Very High)', + type: 'integer', nullable: true, }, players: { - description: "Array of information on individual players", - type: "array", + description: 'Array of information on individual players', + type: 'array', items: { - description: "player", - type: "object", + 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", + description: 'An array describing how abilities were upgraded', + type: 'array', items: { - type: "integer", + type: 'integer', }, }, ability_uses: { description: - "Object containing information on how many times the played used their abilities", - type: "object", + '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", + '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", + '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", + '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", + 'Object containing information on additional units the player had under their control', + type: 'array', items: { - type: "object", + type: 'object', }, nullable: true, }, assists: { - description: "Number of assists the player had", - type: "integer", + description: 'Number of assists the player had', + type: 'integer', }, backpack_0: { - description: "Item in backpack slot 0", - type: "integer", + description: 'Item in backpack slot 0', + type: 'integer', }, backpack_1: { - description: "Item in backpack slot 1", - type: "integer", + description: 'Item in backpack slot 1', + type: 'integer', }, backpack_2: { - description: "Item in backpack slot 2", - type: "integer", + description: 'Item in backpack slot 2', + type: 'integer', }, buyback_log: { - description: "Array containing information about buybacks", - type: "array", + description: 'Array containing information about buybacks', + type: 'array', items: { - type: "object", + type: 'object', properties: { time: { - description: "Time in seconds the buyback occurred", - type: "integer", + description: 'Time in seconds the buyback occurred', + type: 'integer', }, slot: { - description: "slot", - type: "integer", + description: 'slot', + type: 'integer', }, player_slot: commonProperties.player_slot, }, }, }, camps_stacked: { - description: "Number of camps stacked", - type: "integer", + description: 'Number of camps stacked', + type: 'integer', }, connection_log: { description: "Array containing information about the player's disconnections and reconnections", - type: "array", + type: 'array', items: { - type: "object", + type: 'object', properties: { time: { - description: "Game time in seconds the event ocurred", - type: "integer", + description: 'Game time in seconds the event ocurred', + type: 'integer', }, event: { - description: "Event that occurred", - type: "string", + description: 'Event that occurred', + type: 'string', }, player_slot: commonProperties.player_slot, }, }, }, creeps_stacked: { - description: "Number of creeps stacked", - type: "integer", + description: 'Number of creeps stacked', + type: 'integer', }, damage: { description: - "Object containing information about damage dealt by the player to different units", - type: "object", + '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", + type: 'object', }, damage_inflictor_received: { description: - "Object containing information about the sources of damage received by this player from heroes", - type: "object", + '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", + 'Object containing information about from whom the player took damage', + type: 'object', }, deaths: { - description: "Number of deaths", - type: "integer", + description: 'Number of deaths', + type: 'integer', }, denies: { - description: "Number of denies", - type: "integer", + description: 'Number of denies', + type: 'integer', }, dn_t: { description: - "Array containing number of denies at different times of the match", - type: "array", + 'Array containing number of denies at different times of the match', + type: 'array', items: { - type: "integer", + type: 'integer', }, }, gold: { - description: "Gold at the end of the game", - type: "integer", + description: 'Gold at the end of the game', + type: 'integer', }, gold_per_min: { - description: "Gold Per Minute obtained by this player", - type: "integer", + 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", + '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", + description: 'How much gold the player spent', + type: 'integer', }, gold_t: { description: - "Array containing total gold at different times of the match", - type: "array", + 'Array containing total gold at different times of the match', + type: 'array', items: { - type: "integer", + type: 'integer', }, }, hero_damage: { - description: "Hero Damage Dealt", - type: "integer", + description: 'Hero Damage Dealt', + type: 'integer', }, hero_healing: { - description: "Hero Healing Done", - type: "integer", + 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", + '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", + type: 'integer', }, item_1: { description: "Item in the player's second slot", - type: "integer", + type: 'integer', }, item_2: { description: "Item in the player's third slot", - type: "integer", + type: 'integer', }, item_3: { description: "Item in the player's fourth slot", - type: "integer", + type: 'integer', }, item_4: { description: "Item in the player's fifth slot", - type: "integer", + type: 'integer', }, item_5: { description: "Item in the player's sixth slot", - type: "integer", + type: 'integer', }, item_uses: { description: - "Object containing information about how many times a player used items", - type: "object", + '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", + type: 'object', }, killed: { description: - "Object containing information about what units the player killed", - type: "object", + 'Object containing information about what units the player killed', + type: 'object', }, killed_by: { description: - "Object containing information about who killed the player", - type: "object", + 'Object containing information about who killed the player', + type: 'object', }, kills: { - description: "Number of kills", - type: "integer", + description: 'Number of kills', + type: 'integer', }, kills_log: { description: - "Array containing information on which hero the player killed at what time", - type: "array", + 'Array containing information on which hero the player killed at what time', + type: 'array', items: { - type: "object", + type: 'object', properties: { time: { - description: "Time in seconds the player killed the hero", - type: "integer", + description: 'Time in seconds the player killed the hero', + type: 'integer', }, key: { - description: "Hero killed", - type: "string", + description: 'Hero killed', + type: 'string', }, }, }, }, lane_pos: { - description: "Object containing information on lane position", - type: "object", + description: 'Object containing information on lane position', + type: 'object', }, last_hits: { - description: "Number of last hits", - type: "integer", + 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", + type: 'integer', }, level: { - description: "Level at the end of the game", - type: "integer", + 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", + 'Array describing last hits at each minute in the game', + type: 'array', items: { - type: "integer", + type: 'integer', }, }, life_state: { - description: "life_state", - type: "object", + description: 'life_state', + type: 'object', }, max_hero_hit: { description: - "Object with information on the highest damage instance the player inflicted", - type: "object", + '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", + '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", + '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", + description: 'obs_left_log', + type: 'array', items: { - type: "object", + type: 'object', }, }, obs_log: { description: - "Object containing information on when and where the player placed observer wards", - type: "array", + 'Object containing information on when and where the player placed observer wards', + type: 'array', items: { - type: "object", + type: 'object', }, }, obs_placed: { - description: "Total number of observer wards placed", - type: "integer", + description: 'Total number of observer wards placed', + type: 'integer', }, party_id: { - description: "party_id", - type: "integer", + 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", + '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", + type: 'object', }, }, pings: { - description: "Total number of pings", - type: "integer", + description: 'Total number of pings', + type: 'integer', }, purchase: { description: - "Object containing information on the items the player purchased", - type: "object", + 'Object containing information on the items the player purchased', + type: 'object', }, purchase_log: { description: - "Object containing information on when items were purchased", - type: "array", + 'Object containing information on when items were purchased', + type: 'array', items: { - type: "object", + type: 'object', properties: { time: { - description: "Time in seconds the item was bought", - type: "integer", + description: 'Time in seconds the item was bought', + type: 'integer', }, key: { - description: "String item ID", - type: "string", + description: 'String item ID', + type: 'string', }, charges: { - description: "Integer amount of charges", - type: "integer", + description: 'Integer amount of charges', + type: 'integer', }, }, }, }, rune_pickups: { - description: "Number of runes picked up", - type: "integer", + description: 'Number of runes picked up', + type: 'integer', }, runes: { description: - "Object with information about which runes the player picked up", - type: "object", + 'Object with information about which runes the player picked up', + type: 'object', additionalProperties: { - type: "integer", + type: 'integer', }, }, runes_log: { description: - "Array with information on when runes were picked up", - type: "array", + 'Array with information on when runes were picked up', + type: 'array', items: { - type: "object", + type: 'object', properties: { time: { - description: "Time in seconds rune picked up", - type: "integer", + description: 'Time in seconds rune picked up', + type: 'integer', }, key: { - description: "key", - type: "integer", + 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", + '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", + 'Array containing information on when and where the player placed sentries', + type: 'array', items: { - type: "object", + type: 'object', }, }, sen_log: { description: - "Array with information on when and where sentries were placed by the player", - type: "array", + 'Array with information on when and where sentries were placed by the player', + type: 'array', items: { - type: "object", + type: 'object', }, }, sen_placed: { - description: "How many sentries were placed by the player", - type: "integer", + description: 'How many sentries were placed by the player', + type: 'integer', }, stuns: { - description: "Total stun duration of all stuns by the player", - type: "number", + 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", + 'Time in seconds corresponding to the time of entries of other arrays in the match.', + type: 'array', items: { - type: "integer", + type: 'integer', }, }, tower_damage: { - description: "Total tower damage done by the player", - type: "integer", + description: 'Total tower damage done by the player', + type: 'integer', }, xp_per_min: { - description: "Experience Per Minute obtained by the player", - type: "integer", + 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", + type: 'object', }, xp_t: { - description: "Experience at each minute of the game", - type: "array", + description: 'Experience at each minute of the game', + type: 'array', items: { - type: "integer", + type: 'integer', }, }, personaname: commonProperties.persona_name, name: commonProperties.general_name, last_login: { description: "Time of player's last login", - type: "string", - format: "date-time", + 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", + 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", + '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", + '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", + '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", + '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", + '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", + 'Binary integer representing whether or not the player won', + type: 'integer', }, lose: { description: - "Binary integer representing whether or not the player lost", - type: "integer", + 'Binary integer representing whether or not the player lost', + type: 'integer', }, total_gold: { - description: "Total gold at the end of the game", - type: "integer", + description: 'Total gold at the end of the game', + type: 'integer', }, total_xp: { - description: "Total experience at the end of the game", - type: "integer", + description: 'Total experience at the end of the game', + type: 'integer', }, kills_per_min: { - description: "Number of kills per minute", - type: "number", + description: 'Number of kills per minute', + type: 'number', }, kda: { - description: "kda", - type: "number", + description: 'kda', + type: 'number', }, abandons: { - description: "abandons", - type: "integer", + description: 'abandons', + type: 'integer', }, neutral_kills: { - description: "Total number of neutral creeps killed", - type: "integer", + description: 'Total number of neutral creeps killed', + type: 'integer', }, tower_kills: { - description: "Total number of tower kills the player had", - type: "integer", + description: 'Total number of tower kills the player had', + type: 'integer', }, courier_kills: { - description: "Total number of courier kills the player had", - type: "integer", + 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", + 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", + 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", + '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", + 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", + '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", + '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", + 'Total number of Ancient creeps killed by the player', + type: 'integer', }, buyback_count: { - description: "Total number of buyback the player used", - type: "integer", + description: 'Total number of buyback the player used', + type: 'integer', }, observer_uses: { - description: "Number of observer wards used", - type: "integer", + description: 'Number of observer wards used', + type: 'integer', }, sentry_uses: { - description: "Number of sentry wards used", - type: "integer", + description: 'Number of sentry wards used', + type: 'integer', }, lane_efficiency: { - description: "lane_efficiency", - type: "number", + description: 'lane_efficiency', + type: 'number', }, lane_efficiency_pct: { - description: "lane_efficiency_pct", - type: "number", + description: 'lane_efficiency_pct', + type: 'number', }, lane: { - description: "Integer referring to which lane the hero laned in", - type: "integer", + description: 'Integer referring to which lane the hero laned in', + type: 'integer', nullable: true, }, lane_role: { - description: "lane_role", - type: "integer", + description: 'lane_role', + type: 'integer', nullable: true, }, is_roaming: { description: - "Boolean referring to whether or not the player roamed", - type: "boolean", + '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", + '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", + '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", + '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", + '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", + description: 'Total number of TP scrolls purchased by the player', + type: 'integer', }, actions_per_min: { - description: "Actions per minute", - type: "integer", + description: 'Actions per minute', + type: 'integer', }, life_state_dead: { - description: "life_state_dead", - type: "integer", + 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", + 'The rank tier of the player. Tens place indicates rank, ones place indicates stars.', + type: 'integer', }, cosmetics: { - description: "cosmetics", - type: "array", + description: 'cosmetics', + type: 'array', items: { - type: "object", + type: 'object', properties: { item_id: { - type: "integer", + type: 'integer', }, name: commonProperties.general_name, prefab: { - type: "string", + type: 'string', }, creation_date: { - type: "string", - format: "date-time", + type: 'string', + format: 'date-time', nullable: true, }, image_inventory: { - type: "string", + type: 'string', nullable: true, }, image_path: { - type: "string", + type: 'string', nullable: true, }, item_description: { - type: "string", + type: 'string', nullable: true, }, item_name: { - type: "string", + type: 'string', }, item_rarity: { - type: "string", + type: 'string', nullable: true, }, item_type_name: { - type: "string", + type: 'string', nullable: true, }, used_by_heroes: { - type: "string", + type: 'string', nullable: true, }, }, @@ -907,53 +907,53 @@ module.exports = { }, benchmarks: { description: - "Object containing information on certain benchmarks like GPM, XPM, KDA, tower damage, etc", - type: "object", + '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", + 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", + '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", + type: 'object', }, my_word_counts: { description: "Word counts of the player's all chat messages", - type: "object", + type: 'object', }, throw: { description: "Maximum gold advantage of the player's team if they lost the match", - type: "integer", + type: 'integer', }, comeback: { description: "Maximum gold disadvantage of the player's team if they won the match", - type: "integer", + type: 'integer', }, loss: { description: "Maximum gold disadvantage of the player's team if they lost the match", - type: "integer", + type: 'integer', }, win: { description: "Maximum gold advantage of the player's team if they won the match", - type: "integer", + type: 'integer', }, replay_url: { - description: "replay_url", - type: "string", + description: 'replay_url', + type: 'string', }, }, }, diff --git a/routes/responses/schemas/match/ParsedMatchesResponse.js b/routes/responses/schemas/match/ParsedMatchesResponse.js index 7f6c57ce0..a6f362c5d 100644 --- a/routes/responses/schemas/match/ParsedMatchesResponse.js +++ b/routes/responses/schemas/match/ParsedMatchesResponse.js @@ -1,9 +1,9 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { ParsedMatchesResponse: { - title: "ParsedMatchesResponse", - type: "object", + 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 index 9326bb20a..524dd4112 100644 --- a/routes/responses/schemas/match/PublicMatchesResponse.js +++ b/routes/responses/schemas/match/PublicMatchesResponse.js @@ -1,46 +1,46 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { PublicMatchesResponse: { - title: "PublicMatchesResponse", - type: "object", + title: 'PublicMatchesResponse', + type: 'object', properties: { match_id: commonProperties.match_id, match_seq_num: { - description: "match_seq_num", - type: "integer", + description: 'match_seq_num', + type: 'integer', }, radiant_win: commonProperties.radiant_win, start_time: commonProperties.start_time, duration: commonProperties.duration, avg_mmr: { - type: "integer", + type: 'integer', }, num_mmr: { - type: "integer", + type: 'integer', }, lobby_type: { - type: "integer", + type: 'integer', }, game_mode: { - type: "integer", + type: 'integer', }, avg_rank_tier: { - type: "integer", + type: 'integer', }, num_rank_tier: { - type: "integer", + type: 'integer', }, cluster: { - type: "integer", + type: 'integer', }, radiant_team: { - description: "radiant_team", - type: "string", + description: 'radiant_team', + type: 'string', }, dire_team: { - description: "dire_team", - type: "string", + description: 'dire_team', + type: 'string', }, }, }, diff --git a/routes/responses/schemas/miscellaneous/BenchmarksResponse.js b/routes/responses/schemas/miscellaneous/BenchmarksResponse.js index 69c07e7d2..94a007760 100644 --- a/routes/responses/schemas/miscellaneous/BenchmarksResponse.js +++ b/routes/responses/schemas/miscellaneous/BenchmarksResponse.js @@ -1,123 +1,123 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { BenchmarksResponse: { - title: "BenchmarksResponse", - type: "object", + title: 'BenchmarksResponse', + type: 'object', properties: { hero_id: commonProperties.hero_id, result: { - description: "result", - type: "object", + description: 'result', + type: 'object', properties: { gold_per_min: { - type: "array", + type: 'array', items: { - type: "object", + type: 'object', properties: { percentile: { - description: "percentile", - type: "number", + description: 'percentile', + type: 'number', }, value: { - description: "value", - type: "number", + description: 'value', + type: 'number', }, }, }, }, xp_per_min: { - type: "array", + type: 'array', items: { - type: "object", + type: 'object', properties: { percentile: { - description: "percentile", - type: "number", + description: 'percentile', + type: 'number', }, value: { - description: "value", - type: "number", + description: 'value', + type: 'number', }, }, }, }, kills_per_min: { - type: "array", + type: 'array', items: { - type: "object", + type: 'object', properties: { percentile: { - description: "percentile", - type: "number", + description: 'percentile', + type: 'number', }, value: { - description: "value", - type: "number", + description: 'value', + type: 'number', }, }, }, }, last_hits_per_min: { - type: "array", + type: 'array', items: { - type: "object", + type: 'object', properties: { percentile: { - description: "percentile", - type: "number", + description: 'percentile', + type: 'number', }, value: { - description: "value", - type: "number", + description: 'value', + type: 'number', }, }, }, }, hero_damage_per_min: { - type: "array", + type: 'array', items: { - type: "object", + type: 'object', properties: { percentile: { - description: "percentile", - type: "number", + description: 'percentile', + type: 'number', }, value: { - description: "value", - type: "number", + description: 'value', + type: 'number', }, }, }, }, hero_healing_per_min: { - type: "array", + type: 'array', items: { - type: "object", + type: 'object', properties: { percentile: { - description: "percentile", - type: "number", + description: 'percentile', + type: 'number', }, value: { - description: "value", - type: "number", + description: 'value', + type: 'number', }, }, }, }, tower_damage: { - type: "array", + type: 'array', items: { - type: "object", + type: 'object', properties: { percentile: { - description: "percentile", - type: "number", + description: 'percentile', + type: 'number', }, value: { - description: "value", - type: "integer", + description: 'value', + type: 'integer', }, }, }, diff --git a/routes/responses/schemas/miscellaneous/DistributionsResponse.js b/routes/responses/schemas/miscellaneous/DistributionsResponse.js index 70c1c3b29..6fef7a5d0 100644 --- a/routes/responses/schemas/miscellaneous/DistributionsResponse.js +++ b/routes/responses/schemas/miscellaneous/DistributionsResponse.js @@ -1,259 +1,259 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { DistributionsResponse: { - title: "DistributionsResponse", - type: "object", + title: 'DistributionsResponse', + type: 'object', properties: { ranks: { - description: "ranks", - type: "object", + description: 'ranks', + type: 'object', properties: { commmand: { - description: "command", - type: "string", + description: 'command', + type: 'string', }, rowCount: { - description: "rowCount", - type: "integer", + description: 'rowCount', + type: 'integer', }, rows: { - description: "rows", - type: "array", + description: 'rows', + type: 'array', items: { - type: "object", + type: 'object', properties: { bin: { - description: "bin", - type: "integer", + description: 'bin', + type: 'integer', }, bin_name: { - description: "bin_name", - type: "integer", + description: 'bin_name', + type: 'integer', }, count: { - description: "count", - type: "integer", + description: 'count', + type: 'integer', }, cumulative_sum: { - description: "cumulative_sum", - type: "integer", + description: 'cumulative_sum', + type: 'integer', }, }, }, }, fields: { - description: "fields", - type: "array", + description: 'fields', + type: 'array', items: { - type: "object", + type: 'object', properties: { name: commonProperties.field_name, tableID: { - description: "tableID", - type: "integer", + description: 'tableID', + type: 'integer', }, columnID: { - description: "columnID", - type: "integer", + description: 'columnID', + type: 'integer', }, dataTypeID: { - description: "dataTypeID", - type: "integer", + description: 'dataTypeID', + type: 'integer', }, dataTypeSize: { - description: "dataTypeSize", - type: "integer", + description: 'dataTypeSize', + type: 'integer', }, dataTypeModifier: { - description: "dataTypeModifier", - type: "integer", + description: 'dataTypeModifier', + type: 'integer', }, format: { - description: "format", - type: "string", + description: 'format', + type: 'string', }, }, }, }, rowAsArray: { - description: "rowAsArray", - type: "boolean", + description: 'rowAsArray', + type: 'boolean', }, sum: { - description: "sum", - type: "object", + description: 'sum', + type: 'object', properties: { count: { - description: "count", - type: "integer", + description: 'count', + type: 'integer', }, }, }, }, }, mmr: { - description: "mmr", - type: "object", + description: 'mmr', + type: 'object', properties: { commmand: { - description: "command", - type: "string", + description: 'command', + type: 'string', }, rowCount: { - description: "rowCount", - type: "integer", + description: 'rowCount', + type: 'integer', }, rows: { - description: "rows", - type: "array", + description: 'rows', + type: 'array', items: { - type: "object", + type: 'object', properties: { bin: { - description: "bin", - type: "integer", + description: 'bin', + type: 'integer', }, bin_name: { - description: "bin_name", - type: "integer", + description: 'bin_name', + type: 'integer', }, count: { - description: "count", - type: "integer", + description: 'count', + type: 'integer', }, cumulative_sum: { - description: "cumulative_sum", - type: "integer", + description: 'cumulative_sum', + type: 'integer', }, }, }, }, fields: { - description: "fields", - type: "array", + description: 'fields', + type: 'array', items: { - type: "object", + type: 'object', properties: { name: commonProperties.field_name, tableID: { - description: "tableID", - type: "integer", + description: 'tableID', + type: 'integer', }, columnID: { - description: "columnID", - type: "integer", + description: 'columnID', + type: 'integer', }, dataTypeID: { - description: "dataTypeID", - type: "integer", + description: 'dataTypeID', + type: 'integer', }, dataTypeSize: { - description: "dataTypeSize", - type: "integer", + description: 'dataTypeSize', + type: 'integer', }, dataTypeModifier: { - description: "dataTypeModifier", - type: "integer", + description: 'dataTypeModifier', + type: 'integer', }, format: { - description: "format", - type: "string", + description: 'format', + type: 'string', }, }, }, }, rowAsArray: { - description: "rowAsArray", - type: "boolean", + description: 'rowAsArray', + type: 'boolean', }, sum: { - description: "sum", - type: "object", + description: 'sum', + type: 'object', properties: { count: { - description: "count", - type: "integer", + description: 'count', + type: 'integer', }, }, }, }, }, country_mmr: { - description: "country_mmr", - type: "object", + description: 'country_mmr', + type: 'object', properties: { commmand: { - description: "command", - type: "string", + description: 'command', + type: 'string', }, rowCount: { - description: "rowCount", - type: "integer", + description: 'rowCount', + type: 'integer', }, rows: { - description: "rows", - type: "array", + description: 'rows', + type: 'array', items: { - type: "object", + type: 'object', properties: { loccountrycode: { - description: "loccountrycode", - type: "string", + description: 'loccountrycode', + type: 'string', nullable: true, }, count: { - description: "count", - type: "integer", + description: 'count', + type: 'integer', }, avg: { - description: "avg", - type: "string", + description: 'avg', + type: 'string', }, common: { - description: "common", - type: "string", + description: 'common', + type: 'string', }, }, }, }, fields: { - description: "fields", - type: "array", + description: 'fields', + type: 'array', items: { - type: "object", + type: 'object', properties: { name: commonProperties.field_name, tableID: { - description: "tableID", - type: "integer", + description: 'tableID', + type: 'integer', }, columnID: { - description: "columnID", - type: "integer", + description: 'columnID', + type: 'integer', }, dataTypeID: { - description: "dataTypeID", - type: "integer", + description: 'dataTypeID', + type: 'integer', }, dataTypeSize: { - description: "dataTypeSize", - type: "integer", + description: 'dataTypeSize', + type: 'integer', }, dataTypeModifier: { - description: "dataTypeModifier", - type: "integer", + description: 'dataTypeModifier', + type: 'integer', }, format: { - description: "format", - type: "string", + description: 'format', + type: 'string', }, }, }, }, rowAsArray: { - description: "rowAsArray", - type: "boolean", + description: 'rowAsArray', + type: 'boolean', }, }, }, diff --git a/routes/responses/schemas/miscellaneous/LeagueObjectResponse.js b/routes/responses/schemas/miscellaneous/LeagueObjectResponse.js index bfd05ec90..b38953c3e 100644 --- a/routes/responses/schemas/miscellaneous/LeagueObjectResponse.js +++ b/routes/responses/schemas/miscellaneous/LeagueObjectResponse.js @@ -1,25 +1,25 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { LeagueObjectResponse: { - title: "LeagueObjectResponse", - type: "object", + title: 'LeagueObjectResponse', + type: 'object', properties: { leagueid: { - description: "leagueid", - type: "integer", + description: 'leagueid', + type: 'integer', }, ticket: { - description: "ticket", - type: "string", + description: 'ticket', + type: 'string', }, banner: { - description: "banner", - type: "string", + description: 'banner', + type: 'string', }, tier: { - description: "tier", - type: "string", + description: 'tier', + type: 'string', }, name: commonProperties.league_name, }, diff --git a/routes/responses/schemas/miscellaneous/MetadataResponse.js b/routes/responses/schemas/miscellaneous/MetadataResponse.js index 02708dbe3..fdba9b630 100644 --- a/routes/responses/schemas/miscellaneous/MetadataResponse.js +++ b/routes/responses/schemas/miscellaneous/MetadataResponse.js @@ -1,11 +1,11 @@ module.exports = { MetadataResponse: { - title: "MetadataResponse", - type: "object", + title: 'MetadataResponse', + type: 'object', properties: { banner: { - description: "banner", - type: "object", + description: 'banner', + type: 'object', nullable: true, }, }, diff --git a/routes/responses/schemas/miscellaneous/RankingsResponse.js b/routes/responses/schemas/miscellaneous/RankingsResponse.js index aec83119f..07c7ce58b 100644 --- a/routes/responses/schemas/miscellaneous/RankingsResponse.js +++ b/routes/responses/schemas/miscellaneous/RankingsResponse.js @@ -1,77 +1,77 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { RankingsResponse: { - title: "RankingsResponse", - type: "object", + title: 'RankingsResponse', + type: 'object', properties: { hero_id: commonProperties.hero_id, rankings: { - description: "rankings", - type: "array", + description: 'rankings', + type: 'array', items: { - type: "object", + type: 'object', properties: { account_id: commonProperties.account_id, score: { - description: "Score", - type: "number", + description: 'Score', + type: 'number', }, steamid: { - description: "steamid", - type: "string", + description: 'steamid', + type: 'string', nullable: true, }, avatar: { - description: "avatar", - type: "string", + description: 'avatar', + type: 'string', nullable: true, }, avatarmedium: { - description: "avatarmedium", - type: "string", + description: 'avatarmedium', + type: 'string', nullable: true, }, avatarfull: { - description: "avatarfull", - type: "string", + description: 'avatarfull', + type: 'string', nullable: true, }, profileurl: { - description: "profileurl", - type: "string", + description: 'profileurl', + type: 'string', nullable: true, }, personaname: commonProperties.persona_name, last_login: { - description: "last_login", - type: "string", - format: "date-time", + description: 'last_login', + type: 'string', + format: 'date-time', nullable: true, }, full_history_time: { - description: "full_history_time", - type: "string", - format: "date-time", + description: 'full_history_time', + type: 'string', + format: 'date-time', }, cheese: { - description: "cheese", - type: "integer", + description: 'cheese', + type: 'integer', nullable: true, }, fh_unavailable: { - description: "fh_unavailable", - type: "boolean", + description: 'fh_unavailable', + type: 'boolean', nullable: true, }, loccountrycode: { - description: "loccountrycode", - type: "string", + description: 'loccountrycode', + type: 'string', nullable: true, }, rank_tier: { - description: "rank_tier", - type: "integer", + description: 'rank_tier', + type: 'integer', nullable: true, }, }, diff --git a/routes/responses/schemas/miscellaneous/RecordsResponse.js b/routes/responses/schemas/miscellaneous/RecordsResponse.js index b54a6c7f4..ff708bcdb 100644 --- a/routes/responses/schemas/miscellaneous/RecordsResponse.js +++ b/routes/responses/schemas/miscellaneous/RecordsResponse.js @@ -1,16 +1,16 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { RecordsResponse: { - title: "RecordsResponse", - type: "object", + 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", + description: 'Record score', + type: 'integer', }, }, }, diff --git a/routes/responses/schemas/miscellaneous/ScenarioItemTimingsResponse.js b/routes/responses/schemas/miscellaneous/ScenarioItemTimingsResponse.js index 51a0306e1..3114d25f3 100644 --- a/routes/responses/schemas/miscellaneous/ScenarioItemTimingsResponse.js +++ b/routes/responses/schemas/miscellaneous/ScenarioItemTimingsResponse.js @@ -1,28 +1,28 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { ScenarioItemTimingsResponse: { - title: "ScenarioItemTimingsResponse", - type: "object", + title: 'ScenarioItemTimingsResponse', + type: 'object', properties: { hero_id: commonProperties.hero_id, item: { - description: "Purchased item", - type: "string", + description: 'Purchased item', + type: 'string', }, time: { - description: "Ingame time in seconds before the item was purchased", - type: "integer", + 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", + '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", + '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 index 967757369..39fd96fa1 100644 --- a/routes/responses/schemas/miscellaneous/ScenarioLaneRolesResponse.js +++ b/routes/responses/schemas/miscellaneous/ScenarioLaneRolesResponse.js @@ -1,28 +1,28 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { ScenarioLaneRolesResponse: { - title: "ScenarioLaneRolesResponse", - type: "object", + title: 'ScenarioLaneRolesResponse', + type: 'object', properties: { hero_id: commonProperties.hero_id, lane_role: { description: "The hero's lane role", - type: "integer", + type: 'integer', }, time: { - description: "Maximum game length in seconds", - type: "integer", + description: 'Maximum game length in seconds', + type: 'integer', }, games: { description: - "The number of games where the hero played in this lane role", - type: "string", + '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", + '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 index 9b0eaa0ac..27fef039e 100644 --- a/routes/responses/schemas/miscellaneous/ScenarioMiscResponse.js +++ b/routes/responses/schemas/miscellaneous/ScenarioMiscResponse.js @@ -1,28 +1,28 @@ module.exports = { ScenarioMiscResponse: { - title: "ScenarioMiscResponse", - type: "object", + title: 'ScenarioMiscResponse', + type: 'object', properties: { scenario: { description: "The scenario's name or description", - type: "string", + type: 'string', }, is_radiant: { description: - "Boolean indicating whether Radiant executed this scenario", - type: "boolean", + 'Boolean indicating whether Radiant executed this scenario', + type: 'boolean', }, region: { - description: "Region the game was played in", - type: "integer", + description: 'Region the game was played in', + type: 'integer', }, games: { - description: "The number of games where this scenario occurred", - type: "string", + description: 'The number of games where this scenario occurred', + type: 'string', }, wins: { - description: "The number of games won where this scenario occured", - type: "string", + 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 index 2195a3fda..143017293 100644 --- a/routes/responses/schemas/miscellaneous/SchemaResponse.js +++ b/routes/responses/schemas/miscellaneous/SchemaResponse.js @@ -1,19 +1,19 @@ module.exports = { SchemaResponse: { - title: "SchemaResponse", - type: "object", + title: 'SchemaResponse', + type: 'object', properties: { table_name: { - description: "table_name", - type: "string", + description: 'table_name', + type: 'string', }, column_name: { - description: "column_name", - type: "string", + description: 'column_name', + type: 'string', }, data_type: { - description: "data_type", - type: "string", + description: 'data_type', + type: 'string', }, }, }, diff --git a/routes/responses/schemas/miscellaneous/SearchResponse.js b/routes/responses/schemas/miscellaneous/SearchResponse.js index 3ae466282..32bb38292 100644 --- a/routes/responses/schemas/miscellaneous/SearchResponse.js +++ b/routes/responses/schemas/miscellaneous/SearchResponse.js @@ -1,24 +1,24 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { SearchResponse: { - title: "SearchResponse", - type: "object", + title: 'SearchResponse', + type: 'object', properties: { account_id: commonProperties.account_id, avatarfull: { - description: "avatarfull", - type: "string", + 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", + description: 'last_match_time. May not be present or null.', + type: 'string', }, similarity: { - description: "similarity", - type: "number", + description: 'similarity', + type: 'number', }, }, }, diff --git a/routes/responses/schemas/player/PlayerCountsResponse.js b/routes/responses/schemas/player/PlayerCountsResponse.js index 7fab8cde0..48432649d 100644 --- a/routes/responses/schemas/player/PlayerCountsResponse.js +++ b/routes/responses/schemas/player/PlayerCountsResponse.js @@ -1,37 +1,37 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { PlayerCountsResponse: { - title: "PlayerCountsResponse", - type: "object", + 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", + 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", + '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", + '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", + description: 'lane_role', + type: 'object', }, region: { description: - "Integer corresponding to the region the game was played on", - type: "object", + 'Integer corresponding to the region the game was played on', + type: 'object', }, patch: { - description: "patch", - type: "object", + description: 'patch', + type: 'object', }, }, }, diff --git a/routes/responses/schemas/player/PlayerHeroesResponse.js b/routes/responses/schemas/player/PlayerHeroesResponse.js index ddb541f8a..34a136a79 100644 --- a/routes/responses/schemas/player/PlayerHeroesResponse.js +++ b/routes/responses/schemas/player/PlayerHeroesResponse.js @@ -1,39 +1,39 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { PlayerHeroesResponse: { - title: "PlayerHeroesResponse", - description: "hero", - type: "object", + title: 'PlayerHeroesResponse', + description: 'hero', + type: 'object', properties: { hero_id: commonProperties.hero_id, last_played: { - description: "last_played", - type: "integer", + description: 'last_played', + type: 'integer', }, games: { - description: "games", - type: "integer", + description: 'games', + type: 'integer', }, win: { - description: "win", - type: "integer", + description: 'win', + type: 'integer', }, with_games: { - description: "with_games", - type: "integer", + description: 'with_games', + type: 'integer', }, with_win: { - description: "with_win", - type: "integer", + description: 'with_win', + type: 'integer', }, against_games: { - description: "against_games", - type: "integer", + description: 'against_games', + type: 'integer', }, against_win: { - description: "against_win", - type: "integer", + description: 'against_win', + type: 'integer', }, }, }, diff --git a/routes/responses/schemas/player/PlayerMatchesResponse.js b/routes/responses/schemas/player/PlayerMatchesResponse.js index ded5deb22..54e803515 100644 --- a/routes/responses/schemas/player/PlayerMatchesResponse.js +++ b/routes/responses/schemas/player/PlayerMatchesResponse.js @@ -1,10 +1,10 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { PlayerMatchesResponse: { - title: "PlayerMatchesResponse", - description: "Object containing information on the match", - type: "object", + title: 'PlayerMatchesResponse', + description: 'Object containing information on the match', + type: 'object', properties: { match_id: commonProperties.match_id, player_slot: commonProperties.player_slot, @@ -12,52 +12,52 @@ module.exports = { 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", + '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", + '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", + description: 'version', + type: 'integer', nullable: true, }, kills: { - description: "Total kills the player had at the end of the game", - type: "integer", + 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", + 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", + 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", + '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", + 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", + type: 'integer', }, party_size: { description: "Size of the player's party", - type: "integer", + type: 'integer', nullable: true, }, }, diff --git a/routes/responses/schemas/player/PlayerObjectResponse.js b/routes/responses/schemas/player/PlayerObjectResponse.js index 93feeed1f..db2acd977 100644 --- a/routes/responses/schemas/player/PlayerObjectResponse.js +++ b/routes/responses/schemas/player/PlayerObjectResponse.js @@ -1,87 +1,87 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { PlayerObjectResponse: { - title: "PlayerObjectResponse", - type: "object", + title: 'PlayerObjectResponse', + type: 'object', properties: { account_id: commonProperties.account_id, steamid: { description: "Player's steam identifier", - type: "string", + type: 'string', }, avatar: { - description: "Steam picture URL (small picture)", - type: "string", + description: 'Steam picture URL (small picture)', + type: 'string', }, avatarmedium: { - description: "Steam picture URL (medium picture)", - type: "string", + description: 'Steam picture URL (medium picture)', + type: 'string', }, avatarfull: { - description: "Steam picture URL (full picture)", - type: "string", + description: 'Steam picture URL (full picture)', + type: 'string', }, profileurl: { - description: "Steam profile URL", - type: "string", + 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", + 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", + type: 'string', + format: 'date-time', }, cheese: { - description: "Amount of dollars the player has donated to OpenDota", - type: "integer", + 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", + type: 'boolean', }, loccountrycode: { description: "Player's country identifier, e.g. US", - type: "string", + type: 'string', }, name: { description: "Verified player name, e.g. 'Miracle-'", - type: "string", + type: 'string', }, country_code: { description: "Player's country code", - type: "string", + type: 'string', }, fantasy_role: { description: "Player's ingame role (core: 1 or support: 2)", - type: "integer", + type: 'integer', }, team_id: { description: "Player's team identifier", - type: "integer", + type: 'integer', }, team_name: commonProperties.team_name, team_tag: { description: "Player's team shorthand tag, e.g. 'EG'", - type: "string", + type: 'string', }, is_locked: { - description: "Whether the roster lock is active", - type: "boolean", + description: 'Whether the roster lock is active', + type: 'boolean', }, is_pro: { - description: "Whether the player is professional or not", - type: "boolean", + description: 'Whether the player is professional or not', + type: 'boolean', }, locked_until: { - description: "When the roster lock will end", - type: "integer", + 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 index a2fee880b..c4faab8a0 100644 --- a/routes/responses/schemas/player/PlayerPeersResponse.js +++ b/routes/responses/schemas/player/PlayerPeersResponse.js @@ -1,70 +1,70 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { PlayerPeersResponse: { - title: "PlayerPeersResponse", - type: "object", + title: 'PlayerPeersResponse', + type: 'object', properties: { account_id: commonProperties.account_id, last_played: { - description: "last_played", - type: "integer", + description: 'last_played', + type: 'integer', }, win: { - description: "win", - type: "integer", + description: 'win', + type: 'integer', }, games: { - description: "games", - type: "integer", + description: 'games', + type: 'integer', }, with_win: { - description: "with_win", - type: "integer", + description: 'with_win', + type: 'integer', }, with_games: { - description: "with_games", - type: "integer", + description: 'with_games', + type: 'integer', }, against_win: { - description: "against_win", - type: "integer", + description: 'against_win', + type: 'integer', }, against_games: { - description: "against_games", - type: "integer", + description: 'against_games', + type: 'integer', }, with_gpm_sum: { - description: "with_gpm_sum", - type: "integer", + description: 'with_gpm_sum', + type: 'integer', }, with_xpm_sum: { - description: "with_xpm_sum", - type: "integer", + description: 'with_xpm_sum', + type: 'integer', }, personaname: commonProperties.persona_name, name: commonProperties.general_name, is_contributor: { - description: "is_contributor", - type: "boolean", + description: 'is_contributor', + type: 'boolean', }, is_subscriber: { - description: "is_subscriber", - type: "boolean", + description: 'is_subscriber', + type: 'boolean', }, last_login: { - description: "last_login", - type: "string", + description: 'last_login', + type: 'string', nullable: true, }, avatar: { - description: "avatar", - type: "string", + description: 'avatar', + type: 'string', nullable: true, }, avatarfull: { - description: "avatarfull", - type: "string", + description: 'avatarfull', + type: 'string', nullable: true, }, }, diff --git a/routes/responses/schemas/player/PlayerProsResponse.js b/routes/responses/schemas/player/PlayerProsResponse.js index 35fdeeea3..55afff2de 100644 --- a/routes/responses/schemas/player/PlayerProsResponse.js +++ b/routes/responses/schemas/player/PlayerProsResponse.js @@ -1,132 +1,132 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { PlayerProsResponse: { - title: "PlayerProsResponse", - type: "object", + title: 'PlayerProsResponse', + type: 'object', properties: { account_id: commonProperties.account_id, name: commonProperties.general_name, country_code: { - description: "country_code", - type: "string", + description: 'country_code', + type: 'string', }, fantasy_role: { - description: "fantasy_role", - type: "integer", + description: 'fantasy_role', + type: 'integer', }, team_id: { - description: "team_id", - type: "integer", + description: 'team_id', + type: 'integer', }, team_name: commonProperties.team_name, team_tag: { - description: "team_tag", - type: "string", + description: 'team_tag', + type: 'string', nullable: true, }, is_locked: { - description: "is_locked", - type: "boolean", + description: 'is_locked', + type: 'boolean', }, is_pro: { - description: "is_pro", - type: "boolean", + description: 'is_pro', + type: 'boolean', }, locked_until: { - description: "locked_until", - type: "integer", + description: 'locked_until', + type: 'integer', nullable: true, }, steamid: { - description: "steamid", - type: "string", + description: 'steamid', + type: 'string', nullable: true, }, avatar: { - description: "avatar", - type: "string", + description: 'avatar', + type: 'string', nullable: true, }, avatarmedium: { - description: "avatarmedium", - type: "string", + description: 'avatarmedium', + type: 'string', nullable: true, }, avatarfull: { - description: "avatarfull", - type: "string", + description: 'avatarfull', + type: 'string', nullable: true, }, profileurl: { - description: "profileurl", - type: "string", + description: 'profileurl', + type: 'string', nullable: true, }, last_login: { - description: "last_login", - type: "string", - format: "date-time", + description: 'last_login', + type: 'string', + format: 'date-time', nullable: true, }, full_history_time: { - description: "full_history_time", - type: "string", - format: "date-time", + description: 'full_history_time', + type: 'string', + format: 'date-time', nullable: true, }, cheese: { - description: "cheese", - type: "integer", + description: 'cheese', + type: 'integer', nullable: true, }, fh_unavailable: { - description: "fh_unavailable", - type: "boolean", + description: 'fh_unavailable', + type: 'boolean', nullable: true, }, loccountrycode: { - description: "loccountrycode", - type: "string", + description: 'loccountrycode', + type: 'string', nullable: true, }, last_played: { - description: "last_played", - type: "integer", + description: 'last_played', + type: 'integer', nullable: true, }, win: { - description: "win", - type: "integer", + description: 'win', + type: 'integer', }, games: { - description: "games", - type: "integer", + description: 'games', + type: 'integer', }, with_win: { - description: "with_win", - type: "integer", + description: 'with_win', + type: 'integer', }, with_games: { - description: "with_games", - type: "integer", + description: 'with_games', + type: 'integer', }, against_win: { - description: "against_win", - type: "integer", + description: 'against_win', + type: 'integer', }, against_games: { - description: "against_games", - type: "integer", + description: 'against_games', + type: 'integer', }, with_gpm_sum: { - description: "with_gpm_sum", - type: "integer", + description: 'with_gpm_sum', + type: 'integer', nullable: true, }, with_xpm_sum: { - description: "with_xpm_sum", - type: "integer", + description: 'with_xpm_sum', + type: 'integer', nullable: true, }, }, diff --git a/routes/responses/schemas/player/PlayerRankingsResponse.js b/routes/responses/schemas/player/PlayerRankingsResponse.js index 8f747754e..dfed8dff4 100644 --- a/routes/responses/schemas/player/PlayerRankingsResponse.js +++ b/routes/responses/schemas/player/PlayerRankingsResponse.js @@ -1,22 +1,22 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { PlayerRankingsResponse: { - title: "PlayerRankingsResponse", - type: "object", + title: 'PlayerRankingsResponse', + type: 'object', properties: { hero_id: commonProperties.hero_id, score: { - description: "Hero score", - type: "number", + description: 'Hero score', + type: 'number', }, percent_rank: { - description: "percent_rank", - type: "number", + description: 'percent_rank', + type: 'number', }, card: { - description: "numeric_rank", - type: "integer", + description: 'numeric_rank', + type: 'integer', }, }, }, diff --git a/routes/responses/schemas/player/PlayerRatingsResponse.js b/routes/responses/schemas/player/PlayerRatingsResponse.js index 5ec529ad0..37d3c46b6 100644 --- a/routes/responses/schemas/player/PlayerRatingsResponse.js +++ b/routes/responses/schemas/player/PlayerRatingsResponse.js @@ -1,24 +1,24 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { PlayerRatingsResponse: { - title: "PlayerRatingsResponse", - type: "object", + title: 'PlayerRatingsResponse', + type: 'object', properties: { account_id: commonProperties.account_id, match_id: commonProperties.match_id, solo_competitive_rank: { - description: "solo_competitive_rank", - type: "integer", + description: 'solo_competitive_rank', + type: 'integer', nullable: true, }, competitive_rank: { - description: "competitive_rank", - type: "integer", + description: 'competitive_rank', + type: 'integer', }, time: { - description: "time", - type: "integer", + description: 'time', + type: 'integer', }, }, }, diff --git a/routes/responses/schemas/player/PlayerRecentMatchesResponse.js b/routes/responses/schemas/player/PlayerRecentMatchesResponse.js index 640a05536..c51b31fcb 100644 --- a/routes/responses/schemas/player/PlayerRecentMatchesResponse.js +++ b/routes/responses/schemas/player/PlayerRecentMatchesResponse.js @@ -1,10 +1,10 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { PlayerRecentMatchesResponse: { - title: "PlayerRecentMatchesResponse", - description: "match", - type: "object", + title: 'PlayerRecentMatchesResponse', + description: 'match', + type: 'object', properties: { match_id: commonProperties.match_id, player_slot: commonProperties.player_slot, @@ -12,93 +12,93 @@ module.exports = { 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", + '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", + '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", + description: 'version', + type: 'integer', nullable: true, }, kills: { - description: "Total kills the player had at the end of the match", - type: "integer", + 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", + 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", + 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", + '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", + 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", + description: 'Experience Per Minute obtained by the player', + type: 'integer', }, gold_per_min: { - description: "Average gold per minute of the player", - type: "integer", + description: 'Average gold per minute of the player', + type: 'integer', }, hero_damage: { - description: "Total hero damage to enemy heroes", - type: "integer", + description: 'Total hero damage to enemy heroes', + type: 'integer', }, hero_healing: { - description: "Total healing of ally heroes", - type: "integer", + 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", + 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", + 'Integer corresponding to which lane the player laned in for the match', + type: 'integer', nullable: true, }, lane_role: { - description: "lane_role", - type: "integer", + description: 'lane_role', + type: 'integer', nullable: true, }, is_roaming: { - description: "Boolean describing whether or not the player roamed", - type: "boolean", + description: 'Boolean describing whether or not the player roamed', + type: 'boolean', nullable: true, }, cluster: { - description: "cluster", - type: "integer", + 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", + type: 'integer', }, party_size: { description: - "Size of the players party. If not in a party, will return 1.", - type: "integer", + '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 index 611ca299f..8d9f8b26a 100644 --- a/routes/responses/schemas/player/PlayerTotalsResponse.js +++ b/routes/responses/schemas/player/PlayerTotalsResponse.js @@ -1,19 +1,19 @@ module.exports = { PlayerTotalsResponse: { - title: "PlayerTotalsResponse", - type: "object", + title: 'PlayerTotalsResponse', + type: 'object', properties: { field: { - description: "field", - type: "string", + description: 'field', + type: 'string', }, n: { - description: "number", - type: "integer", + description: 'number', + type: 'integer', }, sum: { - description: "sum", - type: "number", + description: 'sum', + type: 'number', }, }, }, diff --git a/routes/responses/schemas/player/PlayerWardMapResponse.js b/routes/responses/schemas/player/PlayerWardMapResponse.js index e1977ec85..eb2bd72c5 100644 --- a/routes/responses/schemas/player/PlayerWardMapResponse.js +++ b/routes/responses/schemas/player/PlayerWardMapResponse.js @@ -1,15 +1,15 @@ module.exports = { PlayerWardMapResponse: { - title: "PlayerWardMapResponse", - type: "object", + title: 'PlayerWardMapResponse', + type: 'object', properties: { obs: { - description: "obs", - type: "object", + description: 'obs', + type: 'object', }, sen: { - description: "sen", - type: "object", + description: 'sen', + type: 'object', }, }, }, diff --git a/routes/responses/schemas/player/PlayerWinLossResponse.js b/routes/responses/schemas/player/PlayerWinLossResponse.js index 9da1ffb93..f83dbb67b 100644 --- a/routes/responses/schemas/player/PlayerWinLossResponse.js +++ b/routes/responses/schemas/player/PlayerWinLossResponse.js @@ -1,15 +1,15 @@ module.exports = { PlayerWinLossResponse: { - title: "PlayerWinLossResponse", - type: "object", + title: 'PlayerWinLossResponse', + type: 'object', properties: { win: { - description: "Number of wins", - type: "integer", + description: 'Number of wins', + type: 'integer', }, lose: { - description: "Number of loses", - type: "integer", + description: 'Number of loses', + type: 'integer', }, }, }, diff --git a/routes/responses/schemas/player/PlayerWordCloudResponse.js b/routes/responses/schemas/player/PlayerWordCloudResponse.js index 7c31ca67e..d29572891 100644 --- a/routes/responses/schemas/player/PlayerWordCloudResponse.js +++ b/routes/responses/schemas/player/PlayerWordCloudResponse.js @@ -1,15 +1,15 @@ module.exports = { PlayerWordCloudResponse: { - title: "PlayerWordCloudResponse", - type: "object", + title: 'PlayerWordCloudResponse', + type: 'object', properties: { my_word_counts: { - description: "my_word_counts", - type: "object", + description: 'my_word_counts', + type: 'object', }, all_word_counts: { - description: "all_word_counts", - type: "object", + description: 'all_word_counts', + type: 'object', }, }, }, diff --git a/routes/responses/schemas/player/PlayersByRankResponse.js b/routes/responses/schemas/player/PlayersByRankResponse.js index 9fdad2514..4cc2b4e39 100644 --- a/routes/responses/schemas/player/PlayersByRankResponse.js +++ b/routes/responses/schemas/player/PlayersByRankResponse.js @@ -1,21 +1,21 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { PlayersByRankResponse: { - title: "PlayersByRankResponse", - type: "array", + title: 'PlayersByRankResponse', + type: 'array', items: { - type: "object", + type: 'object', properties: { account_id: commonProperties.account_id, rank_tier: { - description: "Integer indicating the rank/medal of the player", - type: "number", + 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", + '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 index 68f1ebbd4..3943ac08b 100644 --- a/routes/responses/schemas/player/PlayersResponse.js +++ b/routes/responses/schemas/player/PlayersResponse.js @@ -1,103 +1,103 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { PlayersResponse: { - title: "PlayerResponse", - type: "object", + title: 'PlayerResponse', + type: 'object', properties: { solo_competitive_rank: { - description: "solo_competitive_rank", - type: "integer", + description: 'solo_competitive_rank', + type: 'integer', nullable: true, }, competitive_rank: { - description: "competitive_rank", - type: "integer", + description: 'competitive_rank', + type: 'integer', nullable: true, }, rank_tier: { - description: "rank_tier", - type: "number", + description: 'rank_tier', + type: 'number', nullable: true, }, leaderboard_rank: { - description: "leaderboard_rank", - type: "number", + description: 'leaderboard_rank', + type: 'number', nullable: true, }, mmr_estimate: { - description: "mmr_estimate", - type: "object", + description: 'mmr_estimate', + type: 'object', properties: { estimate: { - description: "estimate", - type: "number", + description: 'estimate', + type: 'number', nullable: true, }, }, }, profile: { - description: "profile", - type: "object", + 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", + 'Boolean indicating status of current Dota Plus subscription', + type: 'boolean', }, cheese: { - description: "cheese", - type: "integer", + description: 'cheese', + type: 'integer', nullable: true, }, steamid: { - description: "steamid", - type: "string", + description: 'steamid', + type: 'string', nullable: true, }, avatar: { - description: "avatar", - type: "string", + description: 'avatar', + type: 'string', nullable: true, }, avatarmedium: { - description: "avatarmedium", - type: "string", + description: 'avatarmedium', + type: 'string', nullable: true, }, avatarfull: { - description: "avatarfull", - type: "string", + description: 'avatarfull', + type: 'string', nullable: true, }, profileurl: { - description: "profileurl", - type: "string", + description: 'profileurl', + type: 'string', nullable: true, }, last_login: { - description: "last_login", - type: "string", + description: 'last_login', + type: 'string', nullable: true, }, loccountrycode: { - description: "loccountrycode", - type: "string", + description: 'loccountrycode', + type: 'string', nullable: true, }, is_contributor: { description: - "Boolean indicating if the user contributed to the development of OpenDota", - type: "boolean", + '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", + '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 index 01095fe57..4ad5e2859 100644 --- a/routes/responses/schemas/team/TeamHeroesResponse.js +++ b/routes/responses/schemas/team/TeamHeroesResponse.js @@ -1,19 +1,19 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { TeamHeroesResponse: { - title: "TeamHeroesResponse", - type: "object", + title: 'TeamHeroesResponse', + type: 'object', properties: { hero_id: commonProperties.hero_id, name: commonProperties.hero_name, games_played: { - description: "Number of games played", - type: "integer", + description: 'Number of games played', + type: 'integer', }, wins: { - description: "Number of wins", - type: "integer", + description: 'Number of wins', + type: 'integer', }, }, }, diff --git a/routes/responses/schemas/team/TeamMatchObjectResponse.js b/routes/responses/schemas/team/TeamMatchObjectResponse.js index c17b28642..682fd0e64 100644 --- a/routes/responses/schemas/team/TeamMatchObjectResponse.js +++ b/routes/responses/schemas/team/TeamMatchObjectResponse.js @@ -1,14 +1,14 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { TeamMatchObjectResponse: { - title: "TeamMatchObjectResponse", - type: "object", + title: 'TeamMatchObjectResponse', + type: 'object', properties: { match_id: commonProperties.match_id, radiant: { - description: "Whether the team/player/hero was on Radiant", - type: "boolean", + description: 'Whether the team/player/hero was on Radiant', + type: 'boolean', }, radiant_win: commonProperties.radiant_win, radiant_score: commonProperties.radiant_score, @@ -16,29 +16,29 @@ module.exports = { duration: commonProperties.duration, start_time: commonProperties.start_time, leagueid: { - description: "Identifier for the league the match took place in", - type: "integer", + 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", + description: 'Name of league the match took place in', + type: 'string', }, cluster: { - description: "cluster", - type: "integer", + description: 'cluster', + type: 'integer', }, opposing_team_id: { - description: "Opposing team identifier", - type: "integer", + description: 'Opposing team identifier', + type: 'integer', }, opposing_team_name: { description: "Opposing team name, e.g. 'Evil Geniuses'", - type: "string", + type: 'string', nullable: true, }, opposing_team_logo: { - description: "Opposing team logo url", - type: "string", + description: 'Opposing team logo url', + type: 'string', }, }, }, diff --git a/routes/responses/schemas/team/TeamObjectResponse.js b/routes/responses/schemas/team/TeamObjectResponse.js index 7aac2e233..6a9001091 100644 --- a/routes/responses/schemas/team/TeamObjectResponse.js +++ b/routes/responses/schemas/team/TeamObjectResponse.js @@ -1,34 +1,34 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { TeamObjectResponse: { - title: "TeamObjectResponse", - type: "object", + title: 'TeamObjectResponse', + type: 'object', properties: { team_id: { description: "Team's identifier", - type: "integer", + type: 'integer', }, rating: { - description: "The Elo rating of the team", - type: "number", + description: 'The Elo rating of the team', + type: 'number', }, wins: { - description: "The number of games won by this team", - type: "integer", + description: 'The number of games won by this team', + type: 'integer', }, losses: { - description: "The number of losses by this team", - type: "integer", + 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", + 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", + description: 'The team tag/abbreviation', + type: 'string', }, }, }, diff --git a/routes/responses/schemas/team/TeamPlayersResponse.js b/routes/responses/schemas/team/TeamPlayersResponse.js index 9dd68bbbe..7924c35e5 100644 --- a/routes/responses/schemas/team/TeamPlayersResponse.js +++ b/routes/responses/schemas/team/TeamPlayersResponse.js @@ -1,23 +1,23 @@ -const commonProperties = require("../../properties/commonProperties"); +const commonProperties = require('../../properties/commonProperties'); module.exports = { TeamPlayersResponse: { - title: "TeamPlayersResponse", - type: "object", + title: 'TeamPlayersResponse', + type: 'object', properties: { account_id: commonProperties.account_id, name: commonProperties.general_name, games_played: { - description: "Number of games played", - type: "integer", + description: 'Number of games played', + type: 'integer', }, wins: { - description: "Number of wins", - type: "integer", + description: 'Number of wins', + type: 'integer', }, is_current_team_member: { - description: "If this player is on the current roster", - type: "boolean", + description: 'If this player is on the current roster', + type: 'boolean', }, }, }, diff --git a/routes/spec.js b/routes/spec.js index 0d09584a6..087755eeb 100644 --- a/routes/spec.js +++ b/routes/spec.js @@ -1,25 +1,25 @@ -const async = require("async"); -const constants = require("dotaconstants"); -const moment = require("moment"); -const { Client } = require("pg"); -const config = require("../config"); +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 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 { redisCount, countPeers, isContributor, matchupToString } = utility; const { subkeys, countCats } = playerFields; @@ -30,25 +30,25 @@ const parameters = Object.values(params).reduce( ); const playerParamNames = [ - "accountIdParam", - "limitParam", - "offsetParam", - "winParam", - "patchParam", - "gameModeParam", - "lobbyTypeParam", - "regionParam", - "dateParam", - "laneRoleParam", - "heroIdParam", - "isRadiantParam", - "includedAccountIdParam", - "excludedAccountIdParam", - "withHeroIdParam", - "againstHeroIdParam", - "significantParam", - "havingParam", - "sortParam", + 'accountIdParam', + 'limitParam', + 'offsetParam', + 'winParam', + 'patchParam', + 'gameModeParam', + 'lobbyTypeParam', + 'regionParam', + 'dateParam', + 'laneRoleParam', + 'heroIdParam', + 'isRadiantParam', + 'includedAccountIdParam', + 'excludedAccountIdParam', + 'withHeroIdParam', + 'againstHeroIdParam', + 'significantParam', + 'havingParam', + 'sortParam', ]; const playerParams = playerParamNames.map((paramName) => ({ @@ -62,21 +62,21 @@ const schemas = Object.values(responses).reduce( const securitySchemes = { api_key: { - type: "apiKey", - name: "api_key", + type: 'apiKey', + name: 'api_key', description: `Use an API key to remove monthly call limits and to receive higher rate limits. [Learn more and get your API key](https://www.opendota.com/api-keys). Usage example: https://api.opendota.com/api/matches/271145478?api_key=YOUR-API-KEY API key can also be sent using the authorization header "Authorization: Bearer YOUR-API-KEY" `, - in: "query", + in: 'query', }, }; const spec = { - openapi: "3.0.3", + openapi: '3.0.3', info: { - title: "OpenDota API", + title: 'OpenDota API', description: `# Introduction The OpenDota API provides Dota 2 related data including advanced match data extracted from match replays. @@ -88,7 +88,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, servers: [ { - url: "https://api.opendota.com/api", + url: 'https://api.opendota.com/api', }, ], components: { @@ -97,26 +97,26 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque parameters, }, paths: { - "/matches/{match_id}": { + '/matches/{match_id}': { get: { - operationId: generateOperationId("get", "/matches/{match_id}"), - summary: "GET /matches/{match_id}", - description: "Match data", - tags: ["matches"], - parameters: [{ $ref: "#/components/parameters/matchIdParam" }], + operationId: generateOperationId('get', '/matches/{match_id}'), + summary: 'GET /matches/{match_id}', + description: 'Match data', + tags: ['matches'], + parameters: [{ $ref: '#/components/parameters/matchIdParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - $ref: "#/components/schemas/MatchResponse", + $ref: '#/components/schemas/MatchResponse', }, }, }, }, }, - route: () => "/matches/:match_id/:info?", + route: () => '/matches/:match_id/:info?', func: async (req, res, cb) => { try { const match = await buildMatch(req.params.match_id, req.query); @@ -130,26 +130,26 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/playersByRank": { + '/playersByRank': { get: { - operationId: generateOperationId("get", "/playersByRank"), - summary: "GET /playersByRank", - description: "Players ordered by rank/medal tier", - tags: ["playersByRank"], + operationId: generateOperationId('get', '/playersByRank'), + summary: 'GET /playersByRank', + description: 'Players ordered by rank/medal tier', + tags: ['playersByRank'], parameters: [], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - $ref: "#/components/schemas/PlayersByRankResponse", + $ref: '#/components/schemas/PlayersByRankResponse', }, }, }, }, }, - route: () => "/playersByRank", + route: () => '/playersByRank', func: (req, res, cb) => { db.raw( ` @@ -170,26 +170,26 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/players/{account_id}": { + '/players/{account_id}': { get: { - operationId: generateOperationId("get", "/players/{account_id}"), - summary: "GET /players/{account_id}", - description: "Player data", - tags: ["players"], - parameters: [{ $ref: "#/components/parameters/accountIdParam" }], + operationId: generateOperationId('get', '/players/{account_id}'), + summary: 'GET /players/{account_id}', + description: 'Player data', + tags: ['players'], + parameters: [{ $ref: '#/components/parameters/accountIdParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - $ref: "#/components/schemas/PlayersResponse", + $ref: '#/components/schemas/PlayersResponse', }, }, }, }, }, - route: () => "/players/:account_id", + route: () => '/players/:account_id', func: (req, res, cb) => { const accountId = Number(req.params.account_id); async.parallel( @@ -205,7 +205,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, solo_competitive_rank(cb) { db.first() - .from("solo_competitive_rank") + .from('solo_competitive_rank') .where({ account_id: accountId }) .asCallback((err, row) => { cb(err, row ? row.rating : null); @@ -213,7 +213,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, competitive_rank(cb) { db.first() - .from("competitive_rank") + .from('competitive_rank') .where({ account_id: accountId }) .asCallback((err, row) => { cb(err, row ? row.rating : null); @@ -221,7 +221,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, rank_tier(cb) { db.first() - .from("rank_tier") + .from('rank_tier') .where({ account_id: accountId }) .asCallback((err, row) => { cb(err, row ? row.rating : null); @@ -229,7 +229,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, leaderboard_rank(cb) { db.first() - .from("leaderboard_rank") + .from('leaderboard_rank') .where({ account_id: accountId }) .asCallback((err, row) => { cb(err, row ? row.rating : null); @@ -251,34 +251,34 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/players/{account_id}/wl": { + '/players/{account_id}/wl': { get: { - operationId: generateOperationId("get", "/players/{account_id}/wl"), - summary: "GET /players/{account_id}/wl", - description: "Win/Loss count", - tags: ["players"], + operationId: generateOperationId('get', '/players/{account_id}/wl'), + summary: 'GET /players/{account_id}/wl', + description: 'Win/Loss count', + tags: ['players'], parameters: playerParams, responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - $ref: "#/components/schemas/PlayerWinLossResponse", + $ref: '#/components/schemas/PlayerWinLossResponse', }, }, }, }, }, - route: () => "/players/:account_id/wl", + route: () => '/players/:account_id/wl', func: (req, res, cb) => { const result = { win: 0, lose: 0, }; req.queryObj.project = req.queryObj.project.concat( - "player_slot", - "radiant_win" + 'player_slot', + 'radiant_win' ); queries.getPlayerMatches( req.params.account_id, @@ -294,33 +294,33 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque result.lose += 1; } }); - return cacheFunctions.sendDataWithCache(req, res, result, "wl"); + return cacheFunctions.sendDataWithCache(req, res, result, 'wl'); } ); }, }, }, - "/players/{account_id}/recentMatches": { + '/players/{account_id}/recentMatches': { get: { operationId: generateOperationId( - "get", - "/players/{account_id}/recentMatches" + 'get', + '/players/{account_id}/recentMatches' ), - summary: "GET /players/{account_id}/recentMatches", - description: "Recent matches played", - tags: ["players"], - parameters: [{ $ref: "#/components/parameters/accountIdParam" }], + summary: 'GET /players/{account_id}/recentMatches', + description: 'Recent matches played', + tags: ['players'], + parameters: [{ $ref: '#/components/parameters/accountIdParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - type: "object", + type: 'object', items: { - $ref: "#/components/schemas/PlayerRecentMatchesResponse", + $ref: '#/components/schemas/PlayerRecentMatchesResponse', }, }, }, @@ -328,37 +328,37 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - route: () => "/players/:account_id/recentMatches", + route: () => '/players/:account_id/recentMatches', func: (req, res, cb) => { queries.getPlayerMatches( req.params.account_id, { project: req.queryObj.project.concat([ - "hero_id", - "start_time", - "duration", - "player_slot", - "radiant_win", - "game_mode", - "lobby_type", - "version", - "kills", - "deaths", - "assists", - "skill", - "average_rank", - "xp_per_min", - "gold_per_min", - "hero_damage", - "tower_damage", - "hero_healing", - "last_hits", - "lane", - "lane_role", - "is_roaming", - "cluster", - "leaver_status", - "party_size", + 'hero_id', + 'start_time', + 'duration', + 'player_slot', + 'radiant_win', + 'game_mode', + 'lobby_type', + 'version', + 'kills', + 'deaths', + 'assists', + 'skill', + 'average_rank', + 'xp_per_min', + 'gold_per_min', + 'hero_damage', + 'tower_damage', + 'hero_healing', + 'last_hits', + 'lane', + 'lane_role', + 'is_roaming', + 'cluster', + 'leaver_status', + 'party_size', ]), dbLimit: 20, }, @@ -372,55 +372,55 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/players/{account_id}/matches": { + '/players/{account_id}/matches': { get: { operationId: generateOperationId( - "get", - "/players/{account_id}/matches" + 'get', + '/players/{account_id}/matches' ), - summary: "GET /players/{account_id}/matches", - description: "Matches played", - tags: ["players"], + summary: 'GET /players/{account_id}/matches', + description: 'Matches played', + tags: ['players'], parameters: [ ...playerParams, { - $ref: "#/components/parameters/projectParam", + $ref: '#/components/parameters/projectParam', }, ], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/PlayerMatchesResponse", + $ref: '#/components/schemas/PlayerMatchesResponse', }, }, }, }, }, }, - route: () => "/players/:account_id/matches", + route: () => '/players/:account_id/matches', func: (req, res, cb) => { // Use passed fields as additional fields, if available const additionalFields = req.query.project || [ - "hero_id", - "start_time", - "duration", - "player_slot", - "radiant_win", - "game_mode", - "lobby_type", - "version", - "kills", - "deaths", - "assists", - "skill", - "average_rank", - "leaver_status", - "party_size", + 'hero_id', + 'start_time', + 'duration', + 'player_slot', + 'radiant_win', + 'game_mode', + 'lobby_type', + 'version', + 'kills', + 'deaths', + 'assists', + 'skill', + 'average_rank', + 'leaver_status', + 'party_size', ]; req.queryObj.project = req.queryObj.project.concat(additionalFields); queries.getPlayerMatches( @@ -436,29 +436,29 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/players/{account_id}/heroes": { + '/players/{account_id}/heroes': { get: { - operationId: generateOperationId("get", "/players/{account_id}/heroes"), - summary: "GET /players/{account_id}/heroes", - description: "Heroes played", - tags: ["players"], + operationId: generateOperationId('get', '/players/{account_id}/heroes'), + summary: 'GET /players/{account_id}/heroes', + description: 'Heroes played', + tags: ['players'], parameters: playerParams, responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/PlayerHeroesResponse", + $ref: '#/components/schemas/PlayerHeroesResponse', }, }, }, }, }, }, - route: () => "/players/:account_id/heroes", + route: () => '/players/:account_id/heroes', func: (req, res, cb) => { const heroes = {}; // prefill heroes with every hero @@ -477,11 +477,11 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque heroes[hero_id_int] = hero; }); req.queryObj.project = req.queryObj.project.concat( - "heroes", - "account_id", - "start_time", - "player_slot", - "radiant_win" + 'heroes', + 'account_id', + 'start_time', + 'player_slot', + 'radiant_win' ); queries.getPlayerMatches( req.params.account_id, @@ -529,44 +529,44 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque req, res, result, - "heroes" + 'heroes' ); } ); }, }, }, - "/players/{account_id}/peers": { + '/players/{account_id}/peers': { get: { - operationId: generateOperationId("get", "/players/{account_id}/peers"), - summary: "GET /players/{account_id}/peers", - description: "Players played with", - tags: ["players"], + operationId: generateOperationId('get', '/players/{account_id}/peers'), + summary: 'GET /players/{account_id}/peers', + description: 'Players played with', + tags: ['players'], parameters: playerParams, responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/PlayerPeersResponse", + $ref: '#/components/schemas/PlayerPeersResponse', }, }, }, }, }, }, - route: () => "/players/:account_id/peers", + route: () => '/players/:account_id/peers', func: (req, res, cb) => { req.queryObj.project = req.queryObj.project.concat( - "heroes", - "start_time", - "player_slot", - "radiant_win", - "gold_per_min", - "xp_per_min" + 'heroes', + 'start_time', + 'player_slot', + 'radiant_win', + 'gold_per_min', + 'xp_per_min' ); queries.getPlayerMatches( req.params.account_id, @@ -590,7 +590,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque req, res, result, - "peers" + 'peers' ); } ); @@ -599,35 +599,35 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/players/{account_id}/pros": { + '/players/{account_id}/pros': { get: { - operationId: generateOperationId("get", "/players/{account_id}/pros"), - summary: "GET /players/{account_id}/pros", - description: "Pro players played with", - tags: ["players"], + operationId: generateOperationId('get', '/players/{account_id}/pros'), + summary: 'GET /players/{account_id}/pros', + description: 'Pro players played with', + tags: ['players'], parameters: playerParams, responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/PlayerProsResponse", + $ref: '#/components/schemas/PlayerProsResponse', }, }, }, }, }, }, - route: () => "/players/:account_id/pros", + route: () => '/players/:account_id/pros', func: (req, res, cb) => { req.queryObj.project = req.queryObj.project.concat( - "heroes", - "start_time", - "player_slot", - "radiant_win" + 'heroes', + 'start_time', + 'player_slot', + 'radiant_win' ); queries.getPlayerMatches( req.params.account_id, @@ -655,29 +655,29 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/players/{account_id}/totals": { + '/players/{account_id}/totals': { get: { - operationId: generateOperationId("get", "/players/{account_id}/totals"), - summary: "GET /players/{account_id}/totals", - description: "Totals in stats", - tags: ["players"], + operationId: generateOperationId('get', '/players/{account_id}/totals'), + summary: 'GET /players/{account_id}/totals', + description: 'Totals in stats', + tags: ['players'], parameters: playerParams, responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/PlayerTotalsResponse", + $ref: '#/components/schemas/PlayerTotalsResponse', }, }, }, }, }, }, - route: () => "/players/:account_id/totals", + route: () => '/players/:account_id/totals', func: (req, res, cb) => { const result = {}; Object.keys(subkeys).forEach((key) => { @@ -711,26 +711,26 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/players/{account_id}/counts": { + '/players/{account_id}/counts': { get: { - operationId: generateOperationId("get", "/players/{account_id}/counts"), - summary: "GET /players/{account_id}/counts", - description: "Counts in categories", - tags: ["players"], + operationId: generateOperationId('get', '/players/{account_id}/counts'), + summary: 'GET /players/{account_id}/counts', + description: 'Counts in categories', + tags: ['players'], parameters: playerParams, responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - $ref: "#/components/schemas/PlayerCountsResponse", + $ref: '#/components/schemas/PlayerCountsResponse', }, }, }, }, }, - route: () => "/players/:account_id/counts", + route: () => '/players/:account_id/counts', func: (req, res, cb) => { const result = {}; Object.keys(countCats).forEach((key) => { @@ -766,42 +766,42 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/players/{account_id}/histograms/{field}": { + '/players/{account_id}/histograms/{field}': { get: { operationId: generateOperationId( - "get", - "/players/{account_id}/histograms/{field}" + 'get', + '/players/{account_id}/histograms/{field}' ), - summary: "GET /players/{account_id}/histograms", - description: "Distribution of matches in a single stat", - tags: ["players"], + summary: 'GET /players/{account_id}/histograms', + description: 'Distribution of matches in a single stat', + tags: ['players'], parameters: [ ...playerParams, { - $ref: "#/components/parameters/fieldParam", + $ref: '#/components/parameters/fieldParam', }, ], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - title: "PlayerHistogramsResponse", - type: "object", + title: 'PlayerHistogramsResponse', + type: 'object', }, }, }, }, }, }, - route: () => "/players/:account_id/histograms/:field", + route: () => '/players/:account_id/histograms/:field', func: (req, res, cb) => { const { field } = req.params; req.queryObj.project = req.queryObj.project - .concat("radiant_win", "player_slot") + .concat('radiant_win', 'player_slot') .concat([field].filter((f) => subkeys[f])); queries.getPlayerMatches( req.params.account_id, @@ -841,29 +841,29 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/players/{account_id}/wardmap": { + '/players/{account_id}/wardmap': { get: { operationId: generateOperationId( - "get", - "/players/{account_id}/wardmap" + 'get', + '/players/{account_id}/wardmap' ), - summary: "GET /players/{account_id}/wardmap", - description: "Wards placed in matches played", - tags: ["players"], + summary: 'GET /players/{account_id}/wardmap', + description: 'Wards placed in matches played', + tags: ['players'], parameters: playerParams, responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - $ref: "#/components/schemas/PlayerWardMapResponse", + $ref: '#/components/schemas/PlayerWardMapResponse', }, }, }, }, }, - route: () => "/players/:account_id/wardmap", + route: () => '/players/:account_id/wardmap', func: (req, res, cb) => { const result = { obs: {}, @@ -890,29 +890,29 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/players/{account_id}/wordcloud": { + '/players/{account_id}/wordcloud': { get: { operationId: generateOperationId( - "get", - "/players/{account_id}/wordcloud" + 'get', + '/players/{account_id}/wordcloud' ), - summary: "GET /players/{account_id}/wordcloud", - description: "Words said/read in matches played", - tags: ["players"], + summary: 'GET /players/{account_id}/wordcloud', + description: 'Words said/read in matches played', + tags: ['players'], parameters: playerParams, responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - $ref: "#/components/schemas/PlayerWordCloudResponse", + $ref: '#/components/schemas/PlayerWordCloudResponse', }, }, }, }, }, - route: () => "/players/:account_id/wordcloud", + route: () => '/players/:account_id/wordcloud', func: (req, res, cb) => { const result = { my_word_counts: {}, @@ -939,32 +939,32 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/players/{account_id}/ratings": { + '/players/{account_id}/ratings': { get: { operationId: generateOperationId( - "get", - "/players/{account_id}/ratings" + 'get', + '/players/{account_id}/ratings' ), - summary: "GET /players/{account_id}/ratings", - description: "Player rating history", - tags: ["players"], - parameters: [{ $ref: "#/components/parameters/accountIdParam" }], + summary: 'GET /players/{account_id}/ratings', + description: 'Player rating history', + tags: ['players'], + parameters: [{ $ref: '#/components/parameters/accountIdParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/PlayerRatingsResponse", + $ref: '#/components/schemas/PlayerRatingsResponse', }, }, }, }, }, }, - route: () => "/players/:account_id/ratings", + route: () => '/players/:account_id/ratings', func: (req, res, cb) => { queries.getPlayerRatings(db, req.params.account_id, (err, result) => { if (err) { @@ -975,32 +975,32 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/players/{account_id}/rankings": { + '/players/{account_id}/rankings': { get: { operationId: generateOperationId( - "get", - "/players/{account_id}/rankings" + 'get', + '/players/{account_id}/rankings' ), - summary: "GET /players/{account_id}/rankings", - description: "Player hero rankings", - tags: ["players"], - parameters: [{ $ref: "#/components/parameters/accountIdParam" }], + summary: 'GET /players/{account_id}/rankings', + description: 'Player hero rankings', + tags: ['players'], + parameters: [{ $ref: '#/components/parameters/accountIdParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/PlayerRankingsResponse", + $ref: '#/components/schemas/PlayerRankingsResponse', }, }, }, }, }, }, - route: () => "/players/:account_id/rankings", + route: () => '/players/:account_id/rankings', func: (req, res, cb) => { queries.getPlayerHeroRankings( req.params.account_id, @@ -1014,31 +1014,31 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/players/{account_id}/refresh": { + '/players/{account_id}/refresh': { post: { - summary: "POST /players/{account_id}/refresh", - description: "Refresh player match history", - tags: ["players"], - parameters: [{ $ref: "#/components/parameters/accountIdParam" }], + summary: 'POST /players/{account_id}/refresh', + description: 'Refresh player match history', + tags: ['players'], + parameters: [{ $ref: '#/components/parameters/accountIdParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - title: "PlayerRefreshResponse", - type: "object", + title: 'PlayerRefreshResponse', + type: 'object', }, }, }, }, }, - route: () => "/players/:account_id/refresh", + route: () => '/players/:account_id/refresh', func: (req, res, cb) => { redis.rpush( - "fhQueue", + 'fhQueue', JSON.stringify({ - account_id: req.params.account_id || "1", + account_id: req.params.account_id || '1', }), (err, length) => { if (err) { @@ -1052,37 +1052,37 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/proPlayers": { + '/proPlayers': { get: { - operationId: generateOperationId("get", "/proPlayers"), - summary: "GET /proPlayers", - description: "Get list of pro players", - tags: ["pro players"], + operationId: generateOperationId('get', '/proPlayers'), + summary: 'GET /proPlayers', + description: 'Get list of pro players', + tags: ['pro players'], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/PlayerObjectResponse", + $ref: '#/components/schemas/PlayerObjectResponse', }, }, }, }, }, }, - route: () => "/proPlayers", + route: () => '/proPlayers', func: (req, res, cb) => { db.select() - .from("players") + .from('players') .rightJoin( - "notable_players", - "players.account_id", - "notable_players.account_id" + 'notable_players', + 'players.account_id', + 'notable_players.account_id' ) - .orderBy("notable_players.account_id", "asc") + .orderBy('notable_players.account_id', 'asc') .asCallback((err, result) => { if (err) { return cb(err); @@ -1092,29 +1092,29 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/proMatches": { + '/proMatches': { get: { - operationId: generateOperationId("get", "/proMatches"), - summary: "GET /proMatches", - description: "Get list of pro matches", - tags: ["pro matches"], - parameters: [{ $ref: "#/components/parameters/lessThanMatchIdParam" }], + operationId: generateOperationId('get', '/proMatches'), + summary: 'GET /proMatches', + description: 'Get list of pro matches', + tags: ['pro matches'], + parameters: [{ $ref: '#/components/parameters/lessThanMatchIdParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/MatchObjectResponse", + $ref: '#/components/schemas/MatchObjectResponse', }, }, }, }, }, }, - route: () => "/proMatches", + route: () => '/proMatches', func: (req, res, cb) => { db.raw( ` @@ -1145,55 +1145,55 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/publicMatches": { + '/publicMatches': { get: { - operationId: generateOperationId("get", "/publicMatches"), - summary: "GET /publicMatches", - description: "Get list of randomly sampled public matches", - tags: ["public matches"], + operationId: generateOperationId('get', '/publicMatches'), + summary: 'GET /publicMatches', + description: 'Get list of randomly sampled public matches', + tags: ['public matches'], parameters: [ - { $ref: "#/components/parameters/lessThanMatchIdParam" }, - { $ref: "#/components/parameters/minRankParam" }, - { $ref: "#/components/parameters/maxRankParam" }, - { $ref: "#/components/parameters/mmrAscendingParam" }, - { $ref: "#/components/parameters/mmrDescendingParam" }, + { $ref: '#/components/parameters/lessThanMatchIdParam' }, + { $ref: '#/components/parameters/minRankParam' }, + { $ref: '#/components/parameters/maxRankParam' }, + { $ref: '#/components/parameters/mmrAscendingParam' }, + { $ref: '#/components/parameters/mmrDescendingParam' }, ], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/PublicMatchesResponse", + $ref: '#/components/schemas/PublicMatchesResponse', }, }, }, }, }, }, - route: () => "/publicMatches", + route: () => '/publicMatches', func: async (req, res, cb) => { const currMax = - (await db("public_matches").max("match_id").first()).max || 0; + (await db('public_matches').max('match_id').first()).max || 0; const lessThan = Number(req.query.less_than_match_id) || currMax; let moreThan = lessThan - 1000000; - let order = ""; + let order = ''; if (req.query.mmr_ascending) { - order = "ORDER BY avg_rank_tier ASC NULLS LAST"; + order = 'ORDER BY avg_rank_tier ASC NULLS LAST'; } else if (req.query.mmr_descending) { - order = "ORDER BY avg_rank_tier DESC NULLS LAST"; + order = 'ORDER BY avg_rank_tier DESC NULLS LAST'; } else { - order = "ORDER BY match_id DESC"; + order = 'ORDER BY match_id DESC'; moreThan = 0; } const minRank = req.query.min_rank ? `AND avg_rank_tier >= ${req.query.min_rank}` - : ""; + : ''; const maxRank = req.query.max_rank ? `AND avg_rank_tier <= ${req.query.max_rank}` - : ""; + : ''; db.raw( ` @@ -1226,29 +1226,29 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/parsedMatches": { + '/parsedMatches': { get: { - operationId: generateOperationId("get", "/parsedMatches"), - summary: "GET /parsedMatches", - description: "Get list of parsed match IDs", - tags: ["parsed matches"], - parameters: [{ $ref: "#/components/parameters/lessThanMatchIdParam" }], + operationId: generateOperationId('get', '/parsedMatches'), + summary: 'GET /parsedMatches', + description: 'Get list of parsed match IDs', + tags: ['parsed matches'], + parameters: [{ $ref: '#/components/parameters/lessThanMatchIdParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/ParsedMatchesResponse", + $ref: '#/components/schemas/ParsedMatchesResponse', }, }, }, }, }, }, - route: () => "/parsedMatches", + route: () => '/parsedMatches', func: (req, res, cb) => { const lessThan = req.query.less_than_match_id || Number.MAX_SAFE_INTEGER; @@ -1270,37 +1270,37 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/explorer": { + '/explorer': { get: { - operationId: generateOperationId("get", "/explorer"), - summary: "GET /explorer", - description: "Submit arbitrary SQL queries to the database", - tags: ["explorer"], + operationId: generateOperationId('get', '/explorer'), + summary: 'GET /explorer', + description: 'Submit arbitrary SQL queries to the database', + tags: ['explorer'], parameters: [ { - name: "sql", - in: "query", - description: "The PostgreSQL query as percent-encoded string.", + name: 'sql', + in: 'query', + description: 'The PostgreSQL query as percent-encoded string.', required: false, schema: { - type: "string", + type: 'string', }, }, ], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - title: "ExplorerResponse", - type: "object", + title: 'ExplorerResponse', + type: 'object', }, }, }, }, }, - route: () => "/explorer", + route: () => '/explorer', func: async (req, res) => { const input = req.query.sql; const client = new Client({ @@ -1316,30 +1316,30 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque err = e; } client.end(); - const final = { ...result, err: err && err.toString(),}; + const final = { ...result, err: err && err.toString() }; return res.status(err ? 400 : 200).json(final); }, }, }, - "/metadata": { + '/metadata': { get: { - operationId: generateOperationId("get", "/metadata"), - summary: "GET /metadata", - description: "Site metadata", - tags: ["metadata"], + operationId: generateOperationId('get', '/metadata'), + summary: 'GET /metadata', + description: 'Site metadata', + tags: ['metadata'], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - $ref: "#/components/schemas/MetadataResponse", + $ref: '#/components/schemas/MetadataResponse', }, }, }, }, }, - route: () => "/metadata", + route: () => '/metadata', func: (req, res, cb) => { queries.getMetadata(req, (err, result) => { if (err) { @@ -1350,25 +1350,25 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/distributions": { + '/distributions': { get: { - operationId: generateOperationId("get", "/distributions"), - summary: "GET /distributions", - description: "Distributions of MMR data by bracket and country", - tags: ["distributions"], + operationId: generateOperationId('get', '/distributions'), + summary: 'GET /distributions', + description: 'Distributions of MMR data by bracket and country', + tags: ['distributions'], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - $ref: "#/components/schemas/DistributionsResponse", + $ref: '#/components/schemas/DistributionsResponse', }, }, }, }, }, - route: () => "/distributions", + route: () => '/distributions', func: (req, res, cb) => { queries.getDistributions(redis, (err, result) => { if (err) { @@ -1379,39 +1379,39 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/search": { + '/search': { get: { - operationId: generateOperationId("get", "/search"), - summary: "GET /search", - description: "Search players by personaname.", - tags: ["search"], + operationId: generateOperationId('get', '/search'), + summary: 'GET /search', + description: 'Search players by personaname.', + tags: ['search'], parameters: [ { - name: "q", - in: "query", - description: "Search string", + name: 'q', + in: 'query', + description: 'Search string', required: true, schema: { - type: "string", + type: 'string', }, }, ], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/SearchResponse", + $ref: '#/components/schemas/SearchResponse', }, }, }, }, }, }, - route: () => "/search", + route: () => '/search', func: (req, res, cb) => { if (!req.query.q) { return res.status(400).json([]); @@ -1437,36 +1437,36 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/rankings": { + '/rankings': { get: { - operationId: generateOperationId("get", "/rankings"), - summary: "GET /rankings", - description: "Top players by hero", - tags: ["rankings"], + operationId: generateOperationId('get', '/rankings'), + summary: 'GET /rankings', + description: 'Top players by hero', + tags: ['rankings'], parameters: [ { - name: "hero_id", - in: "query", - description: "Hero ID", + name: 'hero_id', + in: 'query', + description: 'Hero ID', required: true, schema: { - type: "string", // todo: String for hero id? + type: 'string', // todo: String for hero id? }, }, ], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - $ref: "#/components/schemas/RankingsResponse", + $ref: '#/components/schemas/RankingsResponse', }, }, }, }, }, - route: () => "/rankings", + route: () => '/rankings', func: (req, res, cb) => { queries.getHeroRankings( db, @@ -1483,36 +1483,36 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/benchmarks": { + '/benchmarks': { get: { - operationId: generateOperationId("get", "/benchmarks"), - summary: "GET /benchmarks", - description: "Benchmarks of average stat values for a hero", - tags: ["benchmarks"], + operationId: generateOperationId('get', '/benchmarks'), + summary: 'GET /benchmarks', + description: 'Benchmarks of average stat values for a hero', + tags: ['benchmarks'], parameters: [ { - name: "hero_id", - in: "query", - description: "Hero ID", + name: 'hero_id', + in: 'query', + description: 'Hero ID', required: true, schema: { - type: "string", // todo: String for hero id? + type: 'string', // todo: String for hero id? }, }, ], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - $ref: "#/components/schemas/BenchmarksResponse", + $ref: '#/components/schemas/BenchmarksResponse', }, }, }, }, }, - route: () => "/benchmarks", + route: () => '/benchmarks', func: (req, res, cb) => { queries.getHeroBenchmarks( db, @@ -1530,26 +1530,26 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/status": { + '/status': { get: { - operationId: generateOperationId("get", "/status"), - summary: "GET /status", - description: "Get current service statistics", - tags: ["status"], + operationId: generateOperationId('get', '/status'), + summary: 'GET /status', + description: 'Get current service statistics', + tags: ['status'], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - title: "StatusResponse", - type: "object", + title: 'StatusResponse', + type: 'object', }, }, }, }, }, - route: () => "/status", + route: () => '/status', func: (req, res, cb) => { buildStatus(db, redis, (err, status) => { if (err) { @@ -1560,28 +1560,28 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/health": { + '/health': { get: { - operationId: generateOperationId("get", "/health"), - summary: "GET /health", - description: "Get service health data", - tags: ["health"], + operationId: generateOperationId('get', '/health'), + summary: 'GET /health', + description: 'Get service health data', + tags: ['health'], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - title: "HealthResponse", - type: "object", + title: 'HealthResponse', + type: 'object', }, }, }, }, }, - route: () => "/health/:metric?", + route: () => '/health/:metric?', func: (req, res, cb) => { - redis.hgetall("health", (err, result) => { + redis.hgetall('health', (err, result) => { if (err) { return cb(err); } @@ -1599,72 +1599,70 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/request/{jobId}": { + '/request/{jobId}': { get: { - operationId: generateOperationId("get", "/request/{jobId}"), - summary: "GET /request/{jobId}", - description: "Get parse request state", - tags: ["request"], + operationId: generateOperationId('get', '/request/{jobId}'), + summary: 'GET /request/{jobId}', + description: 'Get parse request state', + tags: ['request'], parameters: [ { - name: "jobId", - in: "path", - description: "The job ID to query.", + name: 'jobId', + in: 'path', + description: 'The job ID to query.', required: true, schema: { - type: "string", + type: 'string', }, }, ], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - title: "RequestJobResponse", - type: "object", + title: 'RequestJobResponse', + type: 'object', }, }, }, }, }, - route: () => "/request/:jobId", + route: () => '/request/:jobId', func: (req, res, cb) => { queue.getJob(req.params.jobId, (err, job) => { if (err) { return cb(err); } if (job) { - return res.json( - { ...job, jobId: job.id,} - ); + return res.json({ ...job, jobId: job.id }); } return res.json(null); }); }, }, }, - "/request/{match_id}": { + '/request/{match_id}': { post: { - summary: "POST /request/{match_id}", - description: "Submit a new parse request", - tags: ["request"], - parameters: [{ $ref: "#/components/parameters/matchIdParam" }], + summary: 'POST /request/{match_id}', + description: 'Submit a new parse request', + tags: ['request'], + parameters: [{ $ref: '#/components/parameters/matchIdParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - title: "RequestMatchResponse", - type: "object", + title: 'RequestMatchResponse', + type: 'object', }, }, }, }, }, - route: () => "/request/:match_id", + route: () => '/request/:match_id', func: (req, res) => { const matchId = req.params.match_id; const match = { @@ -1683,25 +1681,32 @@ 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, + utility.generateJob('api_details', match).url, async (err, body) => { if (err) { // couldn't get data from api, non-retryable return exitWithJob(JSON.stringify(err)); } // Count this request - redisCount(redis, "request"); + redisCount(redis, 'request'); if (req.query.api_key) { - redisCount(redis, "request_api_key"); + redisCount(redis, 'request_api_key'); } // match details response const match = body.result; // Check if match is already parsed - const isAlreadyParsed = Boolean((await db.raw('select match_id from parsed_matches where match_id = ?', [match.match_id])).rows[0]); + const isAlreadyParsed = Boolean( + ( + await db.raw( + 'select match_id from parsed_matches where match_id = ?', + [match.match_id] + ) + ).rows[0] + ); return queries.insertMatch( match, { - type: "api", + type: 'api', attempts: 1, priority: req.query.api_key ? 2 : 1, // Reduce load: only actually reprocess the replay for league matches @@ -1712,63 +1717,63 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque } ); } - return exitWithJob("invalid input"); + return exitWithJob('invalid input'); }, }, }, - "/findMatches": { + '/findMatches': { get: { - operationId: generateOperationId("get", "/findMatches"), - summary: "GET /", - description: "Finds recent matches by heroes played", - tags: ["findMatches"], + operationId: generateOperationId('get', '/findMatches'), + summary: 'GET /', + description: 'Finds recent matches by heroes played', + tags: ['findMatches'], parameters: [ { - name: "teamA", - in: "query", - description: "Hero IDs on first team (array)", + name: 'teamA', + in: 'query', + description: 'Hero IDs on first team (array)', required: false, - style: "form", + style: 'form', explode: false, schema: { - type: "array", + type: 'array', items: { - type: "integer", + type: 'integer', }, }, }, { - name: "teamB", - in: "query", - description: "Hero IDs on second team (array)", + name: 'teamB', + in: 'query', + description: 'Hero IDs on second team (array)', required: false, - style: "form", + style: 'form', explode: false, schema: { - type: "array", + type: 'array', items: { - type: "integer", + type: 'integer', }, }, }, ], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - title: "FindMatchesResponse", - type: "array", + title: 'FindMatchesResponse', + type: 'array', items: { - type: "object", + type: 'object', }, }, }, }, }, }, - route: () => "/findMatches", + route: () => '/findMatches', func: (req, res, cb) => { // accept as input two arrays of up to 5 const t0 = [].concat(req.query.teamA || []).slice(0, 5); @@ -1794,7 +1799,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque return db .raw( - "select * from hero_search where (teamA @> ? AND teamB @> ?) OR (teamA @> ? AND teamB @> ?) order by match_id desc limit 10", + 'select * from hero_search where (teamA @> ? AND teamB @> ?) OR (teamA @> ? AND teamB @> ?) order by match_id desc limit 10', [teamA, teamB, teamB, teamA] ) .asCallback((err, result) => { @@ -1808,32 +1813,32 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/heroes": { + '/heroes': { get: { - operationId: generateOperationId("get", "/heroes"), - summary: "GET /heroes", - description: "Get hero data", - tags: ["heroes"], + operationId: generateOperationId('get', '/heroes'), + summary: 'GET /heroes', + description: 'Get hero data', + tags: ['heroes'], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/HeroObjectResponse", + $ref: '#/components/schemas/HeroObjectResponse', }, }, }, }, }, }, - route: () => "/heroes", + route: () => '/heroes', func: (req, res, cb) => { db.select() - .from("heroes") - .orderBy("id", "asc") + .from('heroes') + .orderBy('id', 'asc') .asCallback((err, result) => { if (err) { return cb(err); @@ -1843,32 +1848,32 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/heroStats": { + '/heroStats': { get: { - operationId: generateOperationId("get", "/heroStats"), - summary: "GET /heroStats", - description: "Get stats about hero performance in recent matches", - tags: ["hero stats"], + operationId: generateOperationId('get', '/heroStats'), + summary: 'GET /heroStats', + description: 'Get stats about hero performance in recent matches', + tags: ['hero stats'], parameters: [], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/HeroStatsResponse", + $ref: '#/components/schemas/HeroStatsResponse', }, }, }, }, }, }, - route: () => "/heroStats", + route: () => '/heroStats', func: (req, res, cb) => { // fetch from cached redis value - redis.get("heroStats", (err, result) => { + redis.get('heroStats', (err, result) => { if (err) { return cb(err); } @@ -1877,29 +1882,29 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/heroes/{hero_id}/matches": { + '/heroes/{hero_id}/matches': { get: { - operationId: generateOperationId("get", "/heroes/{hero_id}/matches"), - summary: "GET /heroes/{hero_id}/matches", - description: "Get recent matches with a hero", - tags: ["heroes"], - parameters: [{ $ref: "#/components/parameters/heroIdPathParam" }], + operationId: generateOperationId('get', '/heroes/{hero_id}/matches'), + summary: 'GET /heroes/{hero_id}/matches', + description: 'Get recent matches with a hero', + tags: ['heroes'], + parameters: [{ $ref: '#/components/parameters/heroIdPathParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/MatchObjectResponse", + $ref: '#/components/schemas/MatchObjectResponse', }, }, }, }, }, }, - route: () => "/heroes/:hero_id/matches", + route: () => '/heroes/:hero_id/matches', func: (req, res, cb) => { const heroId = req.params.hero_id; db.raw( @@ -1933,29 +1938,29 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/heroes/{hero_id}/matchups": { + '/heroes/{hero_id}/matchups': { get: { - operationId: generateOperationId("get", "/heroes/{hero_id}/matchups"), - summary: "GET /heroes/{hero_id}/matchups", - description: "Get results against other heroes for a hero", - tags: ["heroes"], - parameters: [{ $ref: "#/components/parameters/heroIdPathParam" }], + operationId: generateOperationId('get', '/heroes/{hero_id}/matchups'), + summary: 'GET /heroes/{hero_id}/matchups', + description: 'Get results against other heroes for a hero', + tags: ['heroes'], + parameters: [{ $ref: '#/components/parameters/heroIdPathParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/HeroMatchupsResponse", + $ref: '#/components/schemas/HeroMatchupsResponse', }, }, }, }, }, }, - route: () => "/heroes/:hero_id/matchups", + route: () => '/heroes/:hero_id/matchups', func: (req, res, cb) => { const heroId = req.params.hero_id; db.raw( @@ -1970,7 +1975,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque AND matches.start_time > ? GROUP BY pm2.hero_id ORDER BY games_played DESC`, - [heroId, moment().subtract(1, "year").format("X")] + [heroId, moment().subtract(1, 'year').format('X')] ).asCallback((err, result) => { if (err) { return cb(err); @@ -1980,29 +1985,29 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/heroes/{hero_id}/durations": { + '/heroes/{hero_id}/durations': { get: { - operationId: generateOperationId("get", "/heroes/{hero_id}/durations"), - summary: "GET /heroes/{hero_id}/durations", - description: "Get hero performance over a range of match durations", - tags: ["heroes"], - parameters: [{ $ref: "#/components/parameters/heroIdPathParam" }], + operationId: generateOperationId('get', '/heroes/{hero_id}/durations'), + summary: 'GET /heroes/{hero_id}/durations', + description: 'Get hero performance over a range of match durations', + tags: ['heroes'], + parameters: [{ $ref: '#/components/parameters/heroIdPathParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/HeroDurationsResponse", + $ref: '#/components/schemas/HeroDurationsResponse', }, }, }, }, }, }, - route: () => "/heroes/:hero_id/durations", + route: () => '/heroes/:hero_id/durations', func: (req, res, cb) => { const heroId = req.params.hero_id; db.raw( @@ -2024,24 +2029,24 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/heroes/{hero_id}/players": { + '/heroes/{hero_id}/players': { get: { - operationId: generateOperationId("get", "/heroes/{hero_id}/players"), - summary: "GET /heroes/{hero_id}/players", - description: "Get players who have played this hero", - tags: ["heroes"], - parameters: [{ $ref: "#/components/parameters/heroIdPathParam" }], + operationId: generateOperationId('get', '/heroes/{hero_id}/players'), + summary: 'GET /heroes/{hero_id}/players', + description: 'Get players who have played this hero', + tags: ['heroes'], + parameters: [{ $ref: '#/components/parameters/heroIdPathParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - type: "array", // todo: Why double array? + type: 'array', // todo: Why double array? items: { - $ref: "#/components/schemas/PlayerObjectResponse", + $ref: '#/components/schemas/PlayerObjectResponse', }, }, }, @@ -2049,7 +2054,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - route: () => "/heroes/:hero_id/players", + route: () => '/heroes/:hero_id/players', func: (req, res, cb) => { const heroId = req.params.hero_id; db.raw( @@ -2072,30 +2077,30 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/heroes/{hero_id}/itemPopularity": { + '/heroes/{hero_id}/itemPopularity': { get: { operationId: generateOperationId( - "get", - "/heroes/{hero_id}/itemPopularity" + 'get', + '/heroes/{hero_id}/itemPopularity' ), - summary: "GET /heroes/{hero_id}/itemPopularity", + summary: 'GET /heroes/{hero_id}/itemPopularity', description: - "Get item popularity of hero categoried by start, early, mid and late game, analyzed from professional games", - tags: ["heroes"], - parameters: [{ $ref: "#/components/parameters/heroIdPathParam" }], + 'Get item popularity of hero categoried by start, early, mid and late game, analyzed from professional games', + tags: ['heroes'], + parameters: [{ $ref: '#/components/parameters/heroIdPathParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - $ref: "#/components/schemas/HeroItemPopularityResponse", + $ref: '#/components/schemas/HeroItemPopularityResponse', }, }, }, }, }, - route: () => "/heroes/:hero_id/itemPopularity", + route: () => '/heroes/:hero_id/itemPopularity', func: (req, res, cb) => { const heroId = req.params.hero_id; queries.getHeroItemPopularity( @@ -2113,31 +2118,31 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/leagues": { + '/leagues': { get: { - operationId: generateOperationId("get", "/leagues"), - summary: "GET /leagues", - description: "Get league data", - tags: ["leagues"], + operationId: generateOperationId('get', '/leagues'), + summary: 'GET /leagues', + description: 'Get league data', + tags: ['leagues'], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/LeagueObjectResponse", + $ref: '#/components/schemas/LeagueObjectResponse', }, }, }, }, }, }, - route: () => "/leagues", + route: () => '/leagues', func: (req, res, cb) => { db.select() - .from("leagues") + .from('leagues') .asCallback((err, result) => { if (err) { return cb(err); @@ -2147,29 +2152,29 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/leagues/{league_id}": { + '/leagues/{league_id}': { get: { - operationId: generateOperationId("get", "/leagues/{league_id}"), - summary: "GET /leagues/{league_id}", - description: "Get data for a league", - tags: ["leagues"], - parameters: [{ $ref: "#/components/parameters/leagueIdPathParam" }], + operationId: generateOperationId('get', '/leagues/{league_id}'), + summary: 'GET /leagues/{league_id}', + description: 'Get data for a league', + tags: ['leagues'], + parameters: [{ $ref: '#/components/parameters/leagueIdPathParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/LeagueObjectResponse", + $ref: '#/components/schemas/LeagueObjectResponse', }, }, }, }, }, }, - route: () => "/leagues/:league_id", + route: () => '/leagues/:league_id', func: (req, res, cb) => { db.raw( `SELECT leagues.* @@ -2185,26 +2190,26 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/leagues/{league_id}/matches": { + '/leagues/{league_id}/matches': { get: { - operationId: generateOperationId("get", "/leagues/{league_id}/matches"), - summary: "GET /leagues/{league_id}/matches", - description: "Get matches for a team", - tags: ["leagues"], - parameters: [{ $ref: "#/components/parameters/leagueIdPathParam" }], + operationId: generateOperationId('get', '/leagues/{league_id}/matches'), + summary: 'GET /leagues/{league_id}/matches', + description: 'Get matches for a team', + tags: ['leagues'], + parameters: [{ $ref: '#/components/parameters/leagueIdPathParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - $ref: "#/components/schemas/MatchObjectResponse", + $ref: '#/components/schemas/MatchObjectResponse', }, }, }, }, }, - route: () => "/leagues/:league_id/matches", + route: () => '/leagues/:league_id/matches', func: (req, res, cb) => { db.raw( `SELECT matches.* @@ -2220,26 +2225,26 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/leagues/{league_id}/teams": { + '/leagues/{league_id}/teams': { get: { - operationId: generateOperationId("get", "/leagues/{league_id}/teams"), - summary: "GET /leagues/{league_id}/teams", - description: "Get teams for a league", - tags: ["leagues"], - parameters: [{ $ref: "#/components/parameters/leagueIdPathParam" }], + operationId: generateOperationId('get', '/leagues/{league_id}/teams'), + summary: 'GET /leagues/{league_id}/teams', + description: 'Get teams for a league', + tags: ['leagues'], + parameters: [{ $ref: '#/components/parameters/leagueIdPathParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - $ref: "#/components/schemas/TeamObjectResponse", + $ref: '#/components/schemas/TeamObjectResponse', }, }, }, }, }, - route: () => "/leagues/:league_id/teams", + route: () => '/leagues/:league_id/teams', func: (req, res, cb) => { db.raw( `SELECT team_rating.*, teams.* @@ -2259,40 +2264,40 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/teams": { + '/teams': { get: { - operationId: generateOperationId("get", "/teams"), - summary: "GET /teams", - description: "Get team data", - tags: ["teams"], + operationId: generateOperationId('get', '/teams'), + summary: 'GET /teams', + description: 'Get team data', + tags: ['teams'], parameters: [ { - name: "page", - in: "query", + name: 'page', + in: 'query', description: - "Page number, zero indexed. Each page returns up to 1000 entries.", + 'Page number, zero indexed. Each page returns up to 1000 entries.', required: false, schema: { - type: "integer", + type: 'integer', }, }, ], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/TeamObjectResponse", + $ref: '#/components/schemas/TeamObjectResponse', }, }, }, }, }, }, - route: () => "/teams", + route: () => '/teams', func: (req, res, cb) => { db.raw( `SELECT team_rating.*, teams.* @@ -2311,26 +2316,26 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/teams/{team_id}": { + '/teams/{team_id}': { get: { - operationId: generateOperationId("get", "/teams/{team_id}"), - summary: "GET /teams/{team_id}", - description: "Get data for a team", - tags: ["teams"], - parameters: [{ $ref: "#/components/parameters/teamIdPathParam" }], + operationId: generateOperationId('get', '/teams/{team_id}'), + summary: 'GET /teams/{team_id}', + description: 'Get data for a team', + tags: ['teams'], + parameters: [{ $ref: '#/components/parameters/teamIdPathParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - $ref: "#/components/schemas/TeamObjectResponse", + $ref: '#/components/schemas/TeamObjectResponse', }, }, }, }, }, - route: () => "/teams/:team_id", + route: () => '/teams/:team_id', func: (req, res, cb) => { db.raw( `SELECT team_rating.*, teams.* @@ -2347,26 +2352,26 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/teams/{team_id}/matches": { + '/teams/{team_id}/matches': { get: { - operationId: generateOperationId("get", "/teams/{team_id}/matches"), - summary: "GET /teams/{team_id}/matches", - description: "Get matches for a team", - tags: ["teams"], - parameters: [{ $ref: "#/components/parameters/teamIdPathParam" }], + operationId: generateOperationId('get', '/teams/{team_id}/matches'), + summary: 'GET /teams/{team_id}/matches', + description: 'Get matches for a team', + tags: ['teams'], + parameters: [{ $ref: '#/components/parameters/teamIdPathParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - $ref: "#/components/schemas/TeamMatchObjectResponse", + $ref: '#/components/schemas/TeamMatchObjectResponse', }, }, }, }, }, - route: () => "/teams/:team_id/matches", + route: () => '/teams/:team_id/matches', func: (req, res, cb) => { db.raw( ` @@ -2389,26 +2394,26 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/teams/{team_id}/players": { + '/teams/{team_id}/players': { get: { - operationId: generateOperationId("get", "/teams/{team_id}/players"), - summary: "GET /teams/{team_id}/players", - description: "Get players who have played for a team", - tags: ["teams"], - parameters: [{ $ref: "#/components/parameters/teamIdPathParam" }], + operationId: generateOperationId('get', '/teams/{team_id}/players'), + summary: 'GET /teams/{team_id}/players', + description: 'Get players who have played for a team', + tags: ['teams'], + parameters: [{ $ref: '#/components/parameters/teamIdPathParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - $ref: "#/components/schemas/TeamPlayersResponse", + $ref: '#/components/schemas/TeamPlayersResponse', }, }, }, }, }, - route: () => "/teams/:team_id/players", + route: () => '/teams/:team_id/players', func: (req, res, cb) => { db.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 @@ -2430,26 +2435,26 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/teams/{team_id}/heroes": { + '/teams/{team_id}/heroes': { get: { - operationId: generateOperationId("get", "/teams/{team_id}/heroes"), - summary: "GET /teams/{team_id}/heroes", - description: "Get heroes for a team", - tags: ["teams"], - parameters: [{ $ref: "#/components/parameters/teamIdPathParam" }], + operationId: generateOperationId('get', '/teams/{team_id}/heroes'), + summary: 'GET /teams/{team_id}/heroes', + description: 'Get heroes for a team', + tags: ['teams'], + parameters: [{ $ref: '#/components/parameters/teamIdPathParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - $ref: "#/components/schemas/TeamHeroesResponse", + $ref: '#/components/schemas/TeamHeroesResponse', }, }, }, }, }, - route: () => "/teams/:team_id/heroes", + route: () => '/teams/:team_id/heroes', func: (req, res, cb) => { db.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 @@ -2471,34 +2476,34 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/replays": { + '/replays': { get: { - operationId: generateOperationId("get", "/replays"), - summary: "GET /replays", - description: "Get data to construct a replay URL with", - tags: ["replays"], - parameters: [{ $ref: "#/components/parameters/matchIdParam" }], + operationId: generateOperationId('get', '/replays'), + summary: 'GET /replays', + description: 'Get data to construct a replay URL with', + tags: ['replays'], + parameters: [{ $ref: '#/components/parameters/matchIdParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/ReplaysResponse", + $ref: '#/components/schemas/ReplaysResponse', }, }, }, }, }, }, - route: () => "/replays", + route: () => '/replays', func: (req, res, cb) => { - db.select(["match_id", "cluster", "replay_salt"]) - .from("match_gcdata") + db.select(['match_id', 'cluster', 'replay_salt']) + .from('match_gcdata') .whereIn( - "match_id", + 'match_id', [].concat(req.query.match_id || []).slice(0, 5) ) .asCallback((err, result) => { @@ -2510,54 +2515,54 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/records/{field}": { + '/records/{field}': { get: { - operationId: generateOperationId("get", "/records/{field}"), - summary: "GET /records/{field}", - description: "Get top performances in a stat", - tags: ["records"], + operationId: generateOperationId('get', '/records/{field}'), + summary: 'GET /records/{field}', + description: 'Get top performances in a stat', + tags: ['records'], parameters: [ { - name: "field", - in: "path", - description: "Field name to query", + name: 'field', + in: 'path', + description: 'Field name to query', required: true, schema: { - type: "string", + type: 'string', }, }, ], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/RecordsResponse", + $ref: '#/components/schemas/RecordsResponse', }, }, }, }, }, }, - route: () => "/records/:field", + route: () => '/records/:field', func: (req, res, cb) => { redis.zrevrange( `records:${req.params.field}`, 0, 99, - "WITHSCORES", + 'WITHSCORES', (err, rows) => { if (err) { return cb(err); } const entries = rows .map((r, i) => { - const match_id = parseInt(r.split(":")[0]); - const start_time = parseInt(r.split(":")[1]); - const hero_id = parseInt(r.split(":")[2]); + const match_id = parseInt(r.split(':')[0]); + const start_time = parseInt(r.split(':')[1]); + const hero_id = parseInt(r.split(':')[2]); const score = parseInt(rows[i + 1]); return { @@ -2574,23 +2579,23 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/live": { + '/live': { get: { - operationId: generateOperationId("get", "/live"), - summary: "GET /live", - description: "Get top currently ongoing live games", - tags: ["live"], + operationId: generateOperationId('get', '/live'), + summary: 'GET /live', + description: 'Get top currently ongoing live games', + tags: ['live'], parameters: [], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - title: "LiveResponse", - type: "object", + title: 'LiveResponse', + type: 'object', properties: {}, }, }, @@ -2598,9 +2603,9 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - route: () => "/live", + route: () => '/live', func: (req, res, cb) => { - redis.zrangebyscore("liveGames", "-inf", "inf", (err, rows) => { + redis.zrangebyscore('liveGames', '-inf', 'inf', (err, rows) => { if (err) { return cb(err); } @@ -2618,41 +2623,41 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/scenarios/itemTimings": { + '/scenarios/itemTimings': { get: { - operationId: generateOperationId("get", "/scenarios/itemTimings"), - summary: "GET /scenarios/itemTimings", + operationId: generateOperationId('get', '/scenarios/itemTimings'), + summary: 'GET /scenarios/itemTimings', description: - "Win rates for certain item timings on a hero for items that cost at least 1400 gold", - tags: ["scenarios"], + 'Win rates for certain item timings on a hero for items that cost at least 1400 gold', + tags: ['scenarios'], parameters: [ { - name: "item", - in: "query", - description: "Filter by item name e.g. \"spirit_vessel\"", + name: 'item', + in: 'query', + description: 'Filter by item name e.g. "spirit_vessel"', required: false, schema: { - type: "string", + type: 'string', }, }, - { $ref: "#/components/parameters/heroIdParam" }, + { $ref: '#/components/parameters/heroIdParam' }, ], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/ScenarioItemTimingsResponse", + $ref: '#/components/schemas/ScenarioItemTimingsResponse', }, }, }, }, }, }, - route: () => "/scenarios/itemTimings", + route: () => '/scenarios/itemTimings', func: (req, res, cb) => { queries.getItemTimings(req, (err, result) => { if (err) { @@ -2663,40 +2668,40 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/scenarios/laneRoles": { + '/scenarios/laneRoles': { get: { - operationId: generateOperationId("get", "/scenarios/laneRoles"), - summary: "GET /scenarios/laneRoles", - description: "Win rates for heroes in certain lane roles", - tags: ["scenarios"], + operationId: generateOperationId('get', '/scenarios/laneRoles'), + summary: 'GET /scenarios/laneRoles', + description: 'Win rates for heroes in certain lane roles', + tags: ['scenarios'], parameters: [ { - name: "lane_role", - in: "query", - description: "Filter by lane role 1-4 (Safe, Mid, Off, Jungle)", + name: 'lane_role', + in: 'query', + description: 'Filter by lane role 1-4 (Safe, Mid, Off, Jungle)', required: false, schema: { - type: "string", + type: 'string', }, }, - { $ref: "#/components/parameters/heroIdParam" }, + { $ref: '#/components/parameters/heroIdParam' }, ], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/ScenarioLaneRolesResponse", + $ref: '#/components/schemas/ScenarioLaneRolesResponse', }, }, }, }, }, }, - route: () => "/scenarios/laneRoles", + route: () => '/scenarios/laneRoles', func: (req, res, cb) => { queries.getLaneRoles(req, (err, result) => { if (err) { @@ -2707,29 +2712,29 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/scenarios/misc": { + '/scenarios/misc': { get: { - operationId: generateOperationId("get", "/scenarios/misc"), - summary: "GET /scenarios/misc", - description: "Miscellaneous team scenarios", - tags: ["scenarios"], - parameters: [{ $ref: "#/components/parameters/scenarioParam" }], + operationId: generateOperationId('get', '/scenarios/misc'), + summary: 'GET /scenarios/misc', + description: 'Miscellaneous team scenarios', + tags: ['scenarios'], + parameters: [{ $ref: '#/components/parameters/scenarioParam' }], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/ScenarioMiscResponse", + $ref: '#/components/schemas/ScenarioMiscResponse', }, }, }, }, }, }, - route: () => "/scenarios/misc", + route: () => '/scenarios/misc', func: (req, res, cb) => { queries.getTeamScenarios(req, (err, result) => { if (err) { @@ -2740,34 +2745,34 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/schema": { + '/schema': { get: { - operationId: generateOperationId("get", "/schema"), - summary: "GET /schema", - description: "Get database schema", - tags: ["schema"], + operationId: generateOperationId('get', '/schema'), + summary: 'GET /schema', + description: 'Get database schema', + tags: ['schema'], parameters: [], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - $ref: "#/components/schemas/SchemaResponse", + $ref: '#/components/schemas/SchemaResponse', }, }, }, }, }, }, - route: () => "/schema", + route: () => '/schema', func: (req, res, cb) => { - db.select(["table_name", "column_name", "data_type"]) - .from("information_schema.columns") + db.select(['table_name', 'column_name', 'data_type']) + .from('information_schema.columns') .where({ - table_schema: "public", + table_schema: 'public', }) .asCallback((err, result) => { if (err) { @@ -2778,51 +2783,51 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/constants/{resource}": { + '/constants/{resource}': { get: { - operationId: generateOperationId("get", "/constants/{resource}"), - summary: "GET /constants", + operationId: generateOperationId('get', '/constants/{resource}'), + summary: 'GET /constants', description: - "Get static game data mirrored from the dotaconstants repository.", - tags: ["constants"], + 'Get static game data mirrored from the dotaconstants repository.', + tags: ['constants'], parameters: [ { - name: "resource", - in: "path", + name: 'resource', + in: 'path', description: - "Resource name e.g. `heroes`. [List of resources](https://github.com/odota/dotaconstants/tree/master/build)", + 'Resource name e.g. `heroes`. [List of resources](https://github.com/odota/dotaconstants/tree/master/build)', required: true, schema: { - type: "string", + type: 'string', }, }, ], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { nullable: true, oneOf: [ { - type: "object", + type: 'object', additionalProperties: { - title: "ConstantResourceResponse", + title: 'ConstantResourceResponse', }, }, { - type: "array", + type: 'array', items: { oneOf: [ { - type: "object", + type: 'object', additionalProperties: { - title: "ConstantResourceResponse", + title: 'ConstantResourceResponse', }, }, { - type: "integer", + type: 'integer', }, ], }, @@ -2833,7 +2838,7 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - route: () => "/constants/:resource?", + route: () => '/constants/:resource?', func: (req, res, cb) => { const { resource } = req.params; if (resource in constants) { @@ -2843,30 +2848,30 @@ The OpenDota API offers 50,000 free calls per month and a rate limit of 60 reque }, }, }, - "/constants": { + '/constants': { get: { - operationId: generateOperationId("get", "/constants"), - summary: "GET /constants", - description: "Gets an array of available resources.", - tags: ["constants"], + operationId: generateOperationId('get', '/constants'), + summary: 'GET /constants', + description: 'Gets an array of available resources.', + tags: ['constants'], parameters: [], responses: { 200: { - description: "Success", + description: 'Success', content: { - "application/json; charset=utf-8": { + 'application/json; charset=utf-8': { schema: { - type: "array", + type: 'array', items: { - title: "ConstantsResponse", - type: "string", + title: 'ConstantsResponse', + type: 'string', }, }, }, }, }, }, - route: () => "/constants", + route: () => '/constants', func: (req, res) => { return res.json(Object.keys(constants)); }, diff --git a/scripts/convertSpec.js b/scripts/convertSpec.js index 111c8a96e..ba596231f 100644 --- a/scripts/convertSpec.js +++ b/scripts/convertSpec.js @@ -1,4 +1,4 @@ -const fs = require("fs"); -const spec = require("../routes/spec.js"); +const fs = require('fs'); +const spec = require('../routes/spec.js'); -fs.writeFileSync("../spec.json", JSON.stringify(spec, null, 2), "utf-8"); +fs.writeFileSync('../spec.json', JSON.stringify(spec, null, 2), 'utf-8'); diff --git a/store/archive.js b/store/archive.js index 7aedb891c..ba3dcb34a 100644 --- a/store/archive.js +++ b/store/archive.js @@ -1,65 +1,79 @@ -const config = require("../config"); +const config = require('../config'); const { gzipSync, gunzipSync } = require('zlib'); -const { S3Client, PutObjectCommand, GetObjectCommand } = require("@aws-sdk/client-s3"); +const { + S3Client, + PutObjectCommand, + GetObjectCommand, +} = require('@aws-sdk/client-s3'); -const client = config.MATCH_ARCHIVE_S3_ENDPOINT ? new S3Client({ - region: 'us-east-1', - credentials: { +const client = config.MATCH_ARCHIVE_S3_ENDPOINT + ? new S3Client({ + region: 'us-east-1', + credentials: { accessKeyId: config.MATCH_ARCHIVE_S3_KEY_ID, secretAccessKey: config.MATCH_ARCHIVE_S3_KEY_SECRET, - }, - endpoint: 'https://' + config.MATCH_ARCHIVE_S3_ENDPOINT, - // any other options are passed to new AWS.S3() - // See: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Config.html#constructor-property -}) : null; + }, + endpoint: 'https://' + config.MATCH_ARCHIVE_S3_ENDPOINT, + // any other options are passed to new AWS.S3() + // See: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Config.html#constructor-property + }) + : null; async function stream2buffer(stream) { - return new Promise((resolve, reject) => { - const _buf = []; - stream.on("data", (chunk) => _buf.push(chunk)); - stream.on("end", () => resolve(Buffer.concat(_buf))); - stream.on("error", (err) => reject(err)); - }); + return new Promise((resolve, reject) => { + const _buf = []; + stream.on('data', (chunk) => _buf.push(chunk)); + stream.on('end', () => resolve(Buffer.concat(_buf))); + stream.on('error', (err) => reject(err)); + }); } async function archiveGet(key) { - if (!client) { - return; - } - const command = new GetObjectCommand({ - Bucket: config.MATCH_ARCHIVE_S3_BUCKET, - Key: key, - }); - try { - const data = await client.send(command); - if (!data.Body) { - return; - } - const buffer = await stream2buffer(data.Body); - const result = gunzipSync(buffer); - console.log('[ARCHIVE] read %s bytes, decompressed %s bytes', buffer.length, result.length); - return result; - } catch (e) { - return; + if (!client) { + return; + } + const command = new GetObjectCommand({ + Bucket: config.MATCH_ARCHIVE_S3_BUCKET, + Key: key, + }); + try { + const data = await client.send(command); + if (!data.Body) { + return; } + const buffer = await stream2buffer(data.Body); + const result = gunzipSync(buffer); + console.log( + '[ARCHIVE] read %s bytes, decompressed %s bytes', + buffer.length, + result.length + ); + return result; + } catch (e) { + return; + } } async function archivePut(key, blob) { - if (!client) { - return; - } - const data = gzipSync(blob); - const command = new PutObjectCommand({ - Bucket: config.MATCH_ARCHIVE_S3_BUCKET, - Key: key, - Body: data, - }); - const result = await client.send(command); - console.log('[ARCHIVE] original %s bytes, archived %s bytes', blob.length, data.length); - return result; + if (!client) { + return; + } + const data = gzipSync(blob); + const command = new PutObjectCommand({ + Bucket: config.MATCH_ARCHIVE_S3_BUCKET, + Key: key, + Body: data, + }); + const result = await client.send(command); + console.log( + '[ARCHIVE] original %s bytes, archived %s bytes', + blob.length, + data.length + ); + return result; } module.exports = { - archiveGet, - archivePut, -}; \ No newline at end of file + archiveGet, + archivePut, +}; diff --git a/store/buildMatch.js b/store/buildMatch.js index 09916e831..321af5cb0 100644 --- a/store/buildMatch.js +++ b/store/buildMatch.js @@ -1,17 +1,17 @@ /** * 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 { archiveGet } = require("./archive"); -const { getPlayerMatchData, getMatchData } = require("./queries"); +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 { archiveGet } = require('./archive'); +const { getPlayerMatchData, getMatchData } = require('./queries'); const { computeMatchData } = compute; const { buildReplayUrl, isContributor } = utility; @@ -31,12 +31,12 @@ async function extendPlayerData(player, match) { computeMatchData(p); const row = await db .first() - .from("rank_tier") + .from('rank_tier') .where({ account_id: p.account_id || null }); p.rank_tier = row ? row.rating : null; const subscriber = await db .first() - .from("subscriber") + .from('subscriber') .where({ account_id: p.account_id || null }); p.is_subscriber = Boolean(subscriber?.status); return Promise.resolve(p); @@ -44,21 +44,21 @@ async function extendPlayerData(player, match) { async function prodataInfo(matchId) { const result = await db - .first(["radiant_team_id", "dire_team_id", "leagueid"]) - .from("matches") + .first(['radiant_team_id', 'dire_team_id', 'leagueid']) + .from('matches') .where({ match_id: matchId, }); if (!result) { return Promise.resolve({}); } - const leaguePromise = db.first().from("leagues").where({ + const leaguePromise = db.first().from('leagues').where({ leagueid: result.leagueid, }); - const radiantTeamPromise = db.first().from("teams").where({ + const radiantTeamPromise = db.first().from('teams').where({ team_id: result.radiant_team_id, }); - const direTeamPromise = db.first().from("teams").where({ + const direTeamPromise = db.first().from('teams').where({ team_id: result.dire_team_id, }); const [league, radiantTeam, direTeam] = await Promise.all([ @@ -79,7 +79,7 @@ async function backfill(matchId) { }; await new Promise((resolve, reject) => { utility.getData( - utility.generateJob("api_details", match).url, + utility.generateJob('api_details', match).url, (err, body) => { if (err) { console.error(err); @@ -90,12 +90,12 @@ async function backfill(matchId) { return queries.insertMatch( match, { - type: "api", + type: 'api', skipParse: true, }, () => { // Count for logging - utility.redisCount(redis, "steam_api_backfill"); + utility.redisCount(redis, 'steam_api_backfill'); resolve(); } ); @@ -114,7 +114,7 @@ async function getMatch(matchId) { const blob = await archiveGet(matchId.toString()); if (blob) { match = JSON.parse(blob); - utility.redisCount(redis, "match_archive_read"); + utility.redisCount(redis, 'match_archive_read'); } } if (!match) { @@ -126,25 +126,25 @@ async function getMatch(matchId) { // Still don't have it return Promise.resolve(); } - utility.redisCount(redis, "build_match"); + utility.redisCount(redis, 'build_match'); let playersMatchData = []; try { // If we fetched from archive we already have players - playersMatchData = match.players || await getPlayerMatchData(matchId); + playersMatchData = match.players || (await getPlayerMatchData(matchId)); if (playersMatchData.length === 0) { - throw new Error("no players found for match"); + throw new Error('no players found for match'); } } catch (e) { console.error(e); if ( - e.message.startsWith("no players found") || - e.message.startsWith("Unexpected") || - e.message.includes("Attempt to access memory outside buffer bounds") + e.message.startsWith('no players found') || + e.message.startsWith('Unexpected') || + e.message.includes('Attempt to access memory outside buffer bounds') ) { - utility.redisCount(redis, "cassandra_repair"); + utility.redisCount(redis, 'cassandra_repair'); // Delete corrupted data and backfill await cassandra.execute( - "DELETE FROM player_matches where match_id = ?", + 'DELETE FROM player_matches where match_id = ?', [Number(matchId)], { prepare: true } ); @@ -174,12 +174,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 = db.first().from('match_gcdata').where({ match_id: matchId, }); const cosmeticsPromise = Promise.all( Object.keys(match.cosmetics || {}).map((itemId) => - db.first().from("cosmetics").where({ + db.first().from('cosmetics').where({ item_id: itemId, }) ) @@ -228,9 +228,8 @@ async function getMatch(matchId) { matchResult.replay_salt ); } - const matchWithBenchmarks = await queries.getMatchBenchmarksPromisified( - matchResult - ); + const matchWithBenchmarks = + await queries.getMatchBenchmarksPromisified(matchResult); return Promise.resolve(matchWithBenchmarks); } diff --git a/store/buildSets.js b/store/buildSets.js index 8dfcc9b3c..0461c0a1a 100644 --- a/store/buildSets.js +++ b/store/buildSets.js @@ -1,29 +1,29 @@ /** * Function to build/cache sets of players * */ -const async = require("async"); -const moment = require("moment"); +const async = require('async'); +const moment = require('moment'); module.exports = function buildSets(db, redis, cb) { - console.log("rebuilding sets"); + console.log('rebuilding sets'); async.parallel( { // users in this set are added to the trackedPlayers set subscribers(cb) { - db.select(["account_id"]) - .from("subscriber") - .where("status", "=", "active") + db.select(['account_id']) + .from('subscriber') + .where('status', '=', 'active') .asCallback((err, docs) => { if (err) { return cb(err); } const command = redis.multi(); - command.del("tracked"); + command.del('tracked'); docs.forEach((player) => { // Refresh donators with expire date in the future command.zadd( - "tracked", - moment().add(1, "day").format("X"), + 'tracked', + moment().add(1, 'day').format('X'), player.account_id ); }); @@ -34,7 +34,7 @@ module.exports = function buildSets(db, redis, cb) { }, (err) => { if (err) { - console.log("error occurred during buildSets: %s", err); + console.log('error occurred during buildSets: %s', err); return cb(err); } return cb(err); diff --git a/store/buildStatus.js b/store/buildStatus.js index 380967763..e9ba0b91f 100644 --- a/store/buildStatus.js +++ b/store/buildStatus.js @@ -2,8 +2,8 @@ * Function to build status data * */ // const config = require('../config'); -const async = require("async"); -const utility = require("../util/utility"); +const async = require('async'); +const utility = require('../util/utility'); module.exports = function buildStatus(db, redis, cb) { function generatePercentiles(arr) { @@ -24,89 +24,89 @@ module.exports = function buildStatus(db, redis, cb) { async.series( { user_players(cb) { - redis.zcard("visitors", cb); + redis.zcard('visitors', cb); }, tracked_players(cb) { - redis.zcard("tracked", cb); + redis.zcard('tracked', cb); }, matches_last_day(cb) { - utility.getRedisCountDay(redis, "added_match", cb); + utility.getRedisCountDay(redis, 'added_match', cb); }, matches_last_hour(cb) { - utility.getRedisCountHour(redis, "added_match", cb); + utility.getRedisCountHour(redis, 'added_match', cb); }, retriever_matches_last_day(cb) { - utility.getRedisCountDay(redis, "retriever", cb); + utility.getRedisCountDay(redis, 'retriever', cb); }, retriever_players_last_day(cb) { - utility.getRedisCountDay(redis, "retriever_player", cb); + utility.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); + utility.getRedisCountDay(redis, 'parser', cb); }, cached_gcdata_last_day(cb) { - utility.getRedisCountDay(redis, "cached_gcdata", cb); + utility.getRedisCountDay(redis, 'cached_gcdata', cb); }, requests_last_day(cb) { - utility.getRedisCountDay(redis, "request", cb); + utility.getRedisCountDay(redis, 'request', cb); }, requests_api_key_last_day(cb) { - utility.getRedisCountDay(redis, "request_api_key", cb); + utility.getRedisCountDay(redis, 'request_api_key', cb); }, steam_api_backfill_last_day(cb) { - utility.getRedisCountDay(redis, "steam_api_backfill", cb); + utility.getRedisCountDay(redis, 'steam_api_backfill', cb); }, match_archive_read_last_day(cb) { - utility.getRedisCountDay(redis, "match_archive_read", cb); + utility.getRedisCountDay(redis, 'match_archive_read', cb); }, cassandra_repair_last_day(cb) { - utility.getRedisCountDay(redis, "cassandra_repair", cb); + utility.getRedisCountDay(redis, 'cassandra_repair', cb); }, build_match_last_day(cb) { - utility.getRedisCountDay(redis, "build_match", cb); + utility.getRedisCountDay(redis, 'build_match', cb); }, error_last_day(cb) { - utility.getRedisCountDay(redis, "500_error", cb); + utility.getRedisCountDay(redis, '500_error', cb); }, fullhistory_last_day(cb) { - utility.getRedisCountDay(redis, "fullhistory", cb); + utility.getRedisCountDay(redis, 'fullhistory', cb); }, skip_seq_num_last_day(cb) { - utility.getRedisCountDay(redis, "skip_seq_num", cb); + utility.getRedisCountDay(redis, 'skip_seq_num', cb); }, api_hits_last_day(cb) { - utility.getRedisCountDay(redis, "api_hits", cb); + utility.getRedisCountDay(redis, 'api_hits', cb); }, api_hits_ui_last_day(cb) { - utility.getRedisCountDay(redis, "api_hits_ui", cb); + utility.getRedisCountDay(redis, 'api_hits_ui', cb); }, fhQueue(cb) { - redis.llen("fhQueue", cb); + redis.llen('fhQueue', cb); }, gcQueue(cb) { - redis.llen("gcQueue", cb); + redis.llen('gcQueue', cb); }, mmrQueue(cb) { - redis.llen("mmrQueue", cb); + redis.llen('mmrQueue', cb); }, countsQueue(cb) { - redis.llen("countsQueue", cb); + redis.llen('countsQueue', cb); }, scenariosQueue(cb) { - redis.llen("scenariosQueue", cb); + redis.llen('scenariosQueue', cb); }, benchmarksQueue(cb) { - redis.llen("parsedBenchmarksQueue", cb); + redis.llen('parsedBenchmarksQueue', cb); }, retriever(cb) { redis.zrangebyscore( - "retrieverCounts", - "-inf", - "inf", - "WITHSCORES", + 'retrieverCounts', + '-inf', + 'inf', + 'WITHSCORES', (err, results) => { if (err) { return cb(err); @@ -115,7 +115,7 @@ module.exports = function buildStatus(db, redis, cb) { results.forEach((result, i) => { if (i % 2 === 0) { response.push({ - hostname: result.split(".")[0], + hostname: result.split('.')[0], count: results[i + 1], }); } @@ -126,10 +126,10 @@ module.exports = function buildStatus(db, redis, cb) { }, api_paths(cb) { redis.zrangebyscore( - "api_paths", - "-inf", - "inf", - "WITHSCORES", + 'api_paths', + '-inf', + 'inf', + 'WITHSCORES', (err, results) => { if (err) { return cb(err); @@ -138,7 +138,7 @@ module.exports = function buildStatus(db, redis, cb) { results.forEach((result, i) => { if (i % 2 === 0) { response.push({ - hostname: result.split(".")[0], + hostname: result.split('.')[0], count: results[i + 1], }); } @@ -148,7 +148,7 @@ module.exports = function buildStatus(db, redis, cb) { ); }, last_added(cb) { - redis.lrange("matches_last_added", 0, -1, (err, result) => { + redis.lrange('matches_last_added', 0, -1, (err, result) => { cb( err, result.map((r) => JSON.parse(r)) @@ -156,7 +156,7 @@ module.exports = function buildStatus(db, redis, cb) { }); }, last_parsed(cb) { - redis.lrange("matches_last_parsed", 0, -1, (err, result) => { + redis.lrange('matches_last_parsed', 0, -1, (err, result) => { cb( err, result.map((r) => JSON.parse(r)) @@ -164,12 +164,12 @@ module.exports = function buildStatus(db, redis, cb) { }); }, load_times(cb) { - redis.lrange("load_times", 0, -1, (err, arr) => { + redis.lrange('load_times', 0, -1, (err, arr) => { cb(err, generatePercentiles(arr)); }); }, health(cb) { - redis.hgetall("health", (err, result) => { + redis.hgetall('health', (err, result) => { if (err) { return cb(err); } diff --git a/store/cacheFunctions.js b/store/cacheFunctions.js index 06b87faeb..93dcb8b30 100644 --- a/store/cacheFunctions.js +++ b/store/cacheFunctions.js @@ -1,5 +1,5 @@ -const redis = require("./redis"); -const config = require("../config"); +const redis = require('./redis'); +const config = require('../config'); const write = (req, data, cb) => { // console.log(`[WRITECACHE] cache:${req.key}:${req.account_id}`); @@ -10,7 +10,7 @@ const write = (req, data, cb) => { cb ); }; -const getKeys = () => ["wl", "heroes", "peers", "counts"]; +const getKeys = () => ['wl', 'heroes', 'peers', 'counts']; module.exports = { read: (req, cb) => { diff --git a/store/cassandra.js b/store/cassandra.js index 6ceb3f499..ecbb71045 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"); +const cassandraDriver = require('cassandra-driver'); +const url = require('url'); +const config = require('../config'); -const spl = config.CASSANDRA_URL.split(","); +const spl = config.CASSANDRA_URL.split(','); const cps = spl.map((u) => url.parse(u).host); -console.log("connecting %s", config.CASSANDRA_URL); +console.log('connecting %s', config.CASSANDRA_URL); const cassandra = new cassandraDriver.Client({ contactPoints: cps, - localDataCenter: "datacenter1", + localDataCenter: 'datacenter1', keyspace: url.parse(spl[0]).path.substring(1), }); module.exports = cassandra; diff --git a/store/db.js b/store/db.js index 87cfc1366..7e9f45dc6 100644 --- a/store/db.js +++ b/store/db.js @@ -1,15 +1,15 @@ /** * Interface to PostgreSQL client * */ -const pg = require("pg"); -const knex = require("knex"); -const config = require("../config"); +const pg = require('pg'); +const knex = require('knex'); +const config = require('../config'); // remember: all values returned from the server are either NULL or a string pg.types.setTypeParser(20, (val) => (val === null ? null : parseInt(val, 10))); -console.log("connecting %s", config.POSTGRES_URL); +console.log('connecting %s', config.POSTGRES_URL); const db = knex({ - client: "pg", + client: 'pg', connection: config.POSTGRES_URL, pool: { // min: 2, diff --git a/store/elasticsearch.js b/store/elasticsearch.js index fa23429df..6e15d336c 100644 --- a/store/elasticsearch.js +++ b/store/elasticsearch.js @@ -1,16 +1,16 @@ /** * Interface to ElasticSearch client * */ -const elasticsearch = require("@elastic/elasticsearch"); -const config = require("../config"); +const elasticsearch = require('@elastic/elasticsearch'); +const config = require('../config'); -console.log("connecting %s", config.ELASTICSEARCH_URL); +console.log('connecting %s', config.ELASTICSEARCH_URL); const es = new elasticsearch.Client({ node: `http://${config.ELASTICSEARCH_URL}`, - apiVersion: "6.8", + apiVersion: '6.8', }); -const INDEX = config.NODE_ENV === "test" ? "dota-test" : "dota"; +const INDEX = config.NODE_ENV === 'test' ? 'dota-test' : 'dota'; module.exports = { es, diff --git a/store/queries.js b/store/queries.js index ee837b7ed..d9f243517 100644 --- a/store/queries.js +++ b/store/queries.js @@ -1,21 +1,21 @@ /** * 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 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 { redisCount, @@ -73,8 +73,8 @@ function cleanRowCassandra(cassandra, table, row, cb) { return doCleanRow(null, cassandraColumnInfo[table], row, cb); } return cassandra.execute( - "SELECT column_name FROM system_schema.columns WHERE keyspace_name = ? AND table_name = ?", - [config.NODE_ENV === "test" ? "yasp_test" : "yasp", table], + 'SELECT column_name FROM system_schema.columns WHERE keyspace_name = ? AND table_name = ?', + [config.NODE_ENV === 'test' ? 'yasp_test' : 'yasp', table], (err, result) => { if (err) { return cb(err); @@ -114,23 +114,23 @@ function getMatchBenchmarks(m, cb) { (metric, cb) => { // Use data from previous epoch let key = [ - "benchmarks", + 'benchmarks', utility.getStartOfBlockMinutes( config.BENCHMARK_RETENTION_MINUTES, -1 ), metric, p.hero_id, - ].join(":"); + ].join(':'); const backupKey = [ - "benchmarks", + 'benchmarks', utility.getStartOfBlockMinutes( config.BENCHMARK_RETENTION_MINUTES, 0 ), metric, p.hero_id, - ].join(":"); + ].join(':'); const raw = benchmarks[metric](m, p); p.benchmarks[metric] = { raw, @@ -152,7 +152,7 @@ function getMatchBenchmarks(m, cb) { raw !== null && !Number.isNaN(Number(raw)) ) { - return redis.zcount(key, "0", raw, (err, count) => { + return redis.zcount(key, '0', raw, (err, count) => { if (err) { return cb(err); } @@ -186,9 +186,9 @@ async function getMatchBenchmarksPromisified(m) { function getDistributions(redis, cb) { const keys = [ - "distribution:ranks", - "distribution:mmr", - "distribution:country_mmr", + 'distribution:ranks', + 'distribution:mmr', + 'distribution:country_mmr', ]; const result = {}; async.each( @@ -198,7 +198,7 @@ function getDistributions(redis, cb) { if (err) { return cb(err); } - result[r.split(":")[1]] = JSON.parse(blob); + result[r.split(':')[1]] = JSON.parse(blob); return cb(err); }); }, @@ -222,11 +222,11 @@ function getProPlayers(db, redis, cb) { function getLeaderboard(db, redis, key, n, cb) { redis.zrevrangebyscore( key, - "inf", - "-inf", - "WITHSCORES", - "LIMIT", - "0", + 'inf', + '-inf', + 'WITHSCORES', + 'LIMIT', + '0', n, (err, rows) => { if (err) { @@ -242,8 +242,8 @@ function getLeaderboard(db, redis, key, n, cb) { // get player data from DB return db .select() - .from("players") - .whereIn("account_id", accountIds) + .from('players') + .whereIn('account_id', accountIds) .asCallback((err, names) => { if (err) { return cb(err); @@ -354,23 +354,23 @@ function getHeroBenchmarks(db, redis, options, cb) { (percentile, cb) => { // Use data from previous epoch let key = [ - "benchmarks", + 'benchmarks', utility.getStartOfBlockMinutes( config.BENCHMARK_RETENTION_MINUTES, -1 ), metric, heroId, - ].join(":"); + ].join(':'); const backupKey = [ - "benchmarks", + 'benchmarks', utility.getStartOfBlockMinutes( config.BENCHMARK_RETENTION_MINUTES, 0 ), metric, heroId, - ].join(":"); + ].join(':'); redis.exists(key, (err, exists) => { if (err) { return cb(err); @@ -388,7 +388,7 @@ function getHeroBenchmarks(db, redis, options, cb) { key, position, position, - "WITHSCORES", + 'WITHSCORES', (err, result) => { const obj = { percentile, @@ -416,8 +416,8 @@ function getHeroBenchmarks(db, redis, options, cb) { } function getMmrEstimate(accountId, cb) { - db.first("estimate") - .from("mmr_estimates") + db.first('estimate') + .from('mmr_estimates') .where({ account_id: accountId }) .asCallback(cb); } @@ -428,7 +428,7 @@ function getPlayerMatches(accountId, queryObj, cb) { return cb(null, []); } // call clean method to ensure we have column info cached - return cleanRowCassandra(cassandra, "player_caches", {}, (err) => { + return cleanRowCassandra(cassandra, 'player_caches', {}, (err) => { if (err) { return cb(err); } @@ -438,11 +438,11 @@ function getPlayerMatches(accountId, queryObj, cb) { SELECT %s FROM player_caches WHERE account_id = ? ORDER BY match_id DESC - ${queryObj.dbLimit ? `LIMIT ${queryObj.dbLimit}` : ""} + ${queryObj.dbLimit ? `LIMIT ${queryObj.dbLimit}` : ''} `, queryObj.project .filter((f) => cassandraColumnInfo.player_caches[f]) - .join(",") + .join(',') ); const matches = []; return cassandra.eachRow( @@ -477,11 +477,11 @@ function getPlayerMatches(accountId, queryObj, cb) { function getPlayerRatings(db, accountId, cb) { if (!Number.isNaN(Number(accountId))) { - db.from("player_ratings") + db.from('player_ratings') .where({ account_id: Number(accountId), }) - .orderBy("time", "asc") + .orderBy('time', 'asc') .asCallback((err, result) => { cb(err, result); }); @@ -516,29 +516,29 @@ function getPlayerHeroRankings(accountId, cb) { function getPlayer(db, accountId, cb) { if (!Number.isNaN(Number(accountId))) { db.first( - "players.account_id", - "personaname", - "name", - "plus", - "cheese", - "steamid", - "avatar", - "avatarmedium", - "avatarfull", - "profileurl", - "last_login", - "loccountrycode", - "subscriber.status" + 'players.account_id', + 'personaname', + 'name', + 'plus', + 'cheese', + 'steamid', + 'avatar', + 'avatarmedium', + 'avatarfull', + 'profileurl', + 'last_login', + 'loccountrycode', + 'subscriber.status' ) - .from("players") + .from('players') .leftJoin( - "notable_players", - "players.account_id", - "notable_players.account_id" + 'notable_players', + 'players.account_id', + 'notable_players.account_id' ) - .leftJoin("subscriber", "players.account_id", "subscriber.account_id") + .leftJoin('subscriber', 'players.account_id', 'subscriber.account_id') .where({ - "players.account_id": Number(accountId), + 'players.account_id': Number(accountId), }) .asCallback(cb); } else { @@ -572,23 +572,23 @@ function getPeers(db, input, player, cb) { teammatesArr, (t, cb) => { db.first( - "players.account_id", - "personaname", - "name", - "avatar", - "avatarfull", - "last_login", - "subscriber.status" + 'players.account_id', + 'personaname', + 'name', + 'avatar', + 'avatarfull', + 'last_login', + 'subscriber.status' ) - .from("players") + .from('players') .leftJoin( - "notable_players", - "players.account_id", - "notable_players.account_id" + 'notable_players', + 'players.account_id', + 'notable_players.account_id' ) - .leftJoin("subscriber", "players.account_id", "subscriber.account_id") + .leftJoin('subscriber', 'players.account_id', 'subscriber.account_id') .where({ - "players.account_id": t.account_id, + 'players.account_id': t.account_id, }) .asCallback((err, row) => { if (err || !row) { @@ -629,7 +629,7 @@ function getProPeers(db, input, player, cb) { return cb(err); } const arr = result.rows - .map((r) => ({ ...r, ...teammates[r.account_id]})) + .map((r) => ({ ...r, ...teammates[r.account_id] })) .filter((r) => r.account_id !== player.account_id && r.games) .sort((a, b) => b.games - a.games); return cb(err, arr); @@ -645,7 +645,7 @@ function getMatchRating(match, cb) { } return db .first() - .from("solo_competitive_rank") + .from('solo_competitive_rank') .where({ account_id: player.account_id }) .asCallback((err, row) => { cb(err, row ? row.rating : null); @@ -674,7 +674,7 @@ function getMatchRankTier(match, cb) { } return db .first() - .from("rank_tier") + .from('rank_tier') .where({ account_id: player.account_id }) .asCallback((err, row) => { cb(err, row ? row.rating : null); @@ -698,17 +698,17 @@ function upsert(db, table, row, conflict, cb) { if (err) { return cb(err); } - const values = Object.keys(row).map(() => "?"); + const values = Object.keys(row).map(() => '?'); const update = Object.keys(row).map((key) => - util.format("%s=%s", key, `EXCLUDED.${key}`) + util.format('%s=%s', key, `EXCLUDED.${key}`) ); const query = util.format( - "INSERT INTO %s (%s) VALUES (%s) ON CONFLICT (%s) DO UPDATE SET %s", + 'INSERT INTO %s (%s) VALUES (%s) ON CONFLICT (%s) DO UPDATE SET %s', table, - Object.keys(row).join(","), - values.join(","), - Object.keys(conflict).join(","), - update.join(",") + Object.keys(row).join(','), + values.join(','), + Object.keys(conflict).join(','), + update.join(',') ); return db .raw( @@ -735,7 +735,7 @@ function insertPlayer(db, player, indexPlayer, cb) { es.update( { index: INDEX, - type: "player", + type: 'player', id: player.account_id, body: { doc: { @@ -755,7 +755,7 @@ function insertPlayer(db, player, indexPlayer, cb) { return upsert( db, - "players", + 'players', player, { account_id: player.account_id, @@ -771,7 +771,7 @@ function bulkIndexPlayer(bulkActions, cb) { { body: bulkActions, index: INDEX, - type: "player", + type: 'player', }, cb ); @@ -786,7 +786,7 @@ function insertPlayerRating(db, row, cb) { row.match_id && (row.solo_competitive_rank || row.competitive_rank) ) { - db("player_ratings") + db('player_ratings') .insert({ account_id: row.account_id, match_id: row.match_id, @@ -803,7 +803,7 @@ function insertPlayerRating(db, row, cb) { if (row.solo_competitive_rank) { upsert( db, - "solo_competitive_rank", + 'solo_competitive_rank', { account_id: row.account_id, rating: row.solo_competitive_rank, @@ -819,7 +819,7 @@ function insertPlayerRating(db, row, cb) { if (row.competitive_rank) { upsert( db, - "competitive_rank", + 'competitive_rank', { account_id: row.account_id, rating: row.competitive_rank, @@ -835,7 +835,7 @@ function insertPlayerRating(db, row, cb) { if (row.rank_tier) { upsert( db, - "rank_tier", + 'rank_tier', { account_id: row.account_id, rating: row.rank_tier }, { account_id: row.account_id }, cb @@ -848,7 +848,7 @@ function insertPlayerRating(db, row, cb) { if (row.leaderboard_rank) { upsert( db, - "leaderboard_rank", + 'leaderboard_rank', { account_id: row.account_id, rating: row.leaderboard_rank, @@ -871,7 +871,7 @@ function writeCache(accountId, cache, cb) { (match, cb) => { cleanRowCassandra( cassandra, - "player_caches", + 'player_caches', match, (err, cleanedMatch) => { if (err) { @@ -879,11 +879,11 @@ function writeCache(accountId, cache, cb) { } const serializedMatch = serialize(cleanedMatch); const query = util.format( - "INSERT INTO player_caches (%s) VALUES (%s)", - Object.keys(serializedMatch).join(","), + 'INSERT INTO player_caches (%s) VALUES (%s)', + Object.keys(serializedMatch).join(','), Object.keys(serializedMatch) - .map(() => "?") - .join(",") + .map(() => '?') + .join(',') ); const arr = Object.keys(serializedMatch).map( (k) => serializedMatch[k] @@ -924,7 +924,7 @@ function insertPlayerCache(match, cb) { ) { // join player with match to form player_match Object.keys(match).forEach((key) => { - if (key !== "players") { + if (key !== 'players') { playerMatch[key] = match[key]; } }); @@ -945,7 +945,7 @@ function insertPlayerCache(match, cb) { async function updateTeamRankings(match, options) { if ( - options.origin === "scanner" && + options.origin === 'scanner' && match.radiant_team_id && match.dire_team_id && match.radiant_win !== undefined @@ -955,12 +955,12 @@ async function updateTeamRankings(match, options) { const team1Win = Number(match.radiant_win); const kFactor = 32; const data1 = await db - .select("rating") - .from("team_rating") + .select('rating') + .from('team_rating') .where({ team_id: team1 }); const data2 = await db - .select("rating") - .from("team_rating") + .select('rating') + .from('team_rating') .where({ team_id: team2 }); const currRating1 = Number((data1 && data1[0] && data1[0].rating) || 1000); const currRating2 = Number((data2 && data2[0] && data2[0].rating) || 1000); @@ -1013,8 +1013,8 @@ function insertMatch(match, options, cb) { : undefined; const abilityUpgrades = []; const savedAbilityLvls = { - 5288: "track", - 5368: "greevils_greed", + 5288: 'track', + 5368: 'greevils_greed', }; function preprocess(cb) { @@ -1054,7 +1054,7 @@ function insertMatch(match, options, cb) { p.ability_upgrades.forEach((au) => { if (au.ability in savedAbilityLvls) { abilityLvls[au.ability] = (abilityLvls[au.ability] || 0) + 1; - const abilityUpgrade = { ...au, level: abilityLvls[au.ability],}; + const abilityUpgrade = { ...au, level: abilityLvls[au.ability] }; abilityUpgrades.push(abilityUpgrade); } }); @@ -1065,9 +1065,9 @@ function insertMatch(match, options, cb) { } function updateMatchSeriesType(cb) { - if (options.type === "gcdata") { + if (options.type === 'gcdata') { db.raw( - "UPDATE matches SET series_id = ?, series_type = ? WHERE match_id = ?", + 'UPDATE matches SET series_id = ?, series_type = ? WHERE match_id = ?', [match.series_id, match.series_type, match.match_id] ).asCallback(cb); } else { @@ -1079,7 +1079,7 @@ function insertMatch(match, options, cb) { if (match.version) { return upsert( db, - "parsed_matches", + 'parsed_matches', { match_id: match.match_id, }, @@ -1094,7 +1094,12 @@ function insertMatch(match, options, cb) { async function upsertMatchPostgres(cb) { // Check if leagueid is premium/professional - const result = match.leagueid && await db.raw(`select leagueid from leagues where leagueid = ? and (tier = 'premium' OR tier = 'professional')`, [match.leagueid]); + const result = + match.leagueid && + (await db.raw( + `select leagueid from leagues where leagueid = ? and (tier = 'premium' OR tier = 'professional')`, + [match.leagueid] + )); const pass = result?.rows?.length > 0 && utility.isProMatch(match); if (!pass) { // Skip this if not a pro match @@ -1105,7 +1110,7 @@ function insertMatch(match, options, cb) { function upsertMatch(cb) { upsert( trx, - "matches", + 'matches', match, { match_id: match.match_id, @@ -1131,7 +1136,7 @@ function insertMatch(match, options, cb) { } upsert( trx, - "player_matches", + 'player_matches', pm, { match_id: pm.match_id, @@ -1153,7 +1158,7 @@ function insertMatch(match, options, cb) { p.match_id = match.match_id; upsert( trx, - "picks_bans", + 'picks_bans', p, { match_id: p.match_id, @@ -1170,7 +1175,7 @@ function insertMatch(match, options, cb) { if (match.start_time) { return upsert( trx, - "match_patch", + 'match_patch', { match_id: match.match_id, patch: @@ -1206,7 +1211,7 @@ function insertMatch(match, options, cb) { (tm, cb) => { upsert( trx, - "team_match", + 'team_match', tm, { team_id: tm.team_id, @@ -1249,7 +1254,7 @@ function insertMatch(match, options, cb) { } function getAverageRank(cb) { - if (options.origin === "scanner") { + if (options.origin === 'scanner') { getMatchRankTier(match, (err, avg) => { match.average_rank = avg || null; return cb(); @@ -1262,7 +1267,7 @@ function insertMatch(match, options, cb) { function upsertMatchCassandra(cb) { // NOTE parsed insert doesn't have original match info so can't archive here // unless we insert then read it back from cassandra - return cleanRowCassandra(cassandra, "matches", match, (err, match) => { + return cleanRowCassandra(cassandra, 'matches', match, (err, match) => { if (err) { return cb(err); } @@ -1271,14 +1276,14 @@ function insertMatch(match, options, cb) { return cb(err); } const query = util.format( - "INSERT INTO matches (%s) VALUES (%s)", - Object.keys(obj).join(","), + 'INSERT INTO matches (%s) VALUES (%s)', + Object.keys(obj).join(','), Object.keys(obj) - .map(() => "?") - .join(",") + .map(() => '?') + .join(',') ); const arr = Object.keys(obj).map((k) => - obj[k] === "true" || obj[k] === "false" ? JSON.parse(obj[k]) : obj[k] + obj[k] === 'true' || obj[k] === 'false' ? JSON.parse(obj[k]) : obj[k] ); return cassandra.execute( query, @@ -1294,7 +1299,7 @@ function insertMatch(match, options, cb) { players || [], (pm, cb) => { pm.match_id = match.match_id; - cleanRowCassandra(cassandra, "player_matches", pm, (err, pm) => { + cleanRowCassandra(cassandra, 'player_matches', pm, (err, pm) => { if (err) { return cb(err); } @@ -1303,14 +1308,14 @@ function insertMatch(match, options, cb) { return cb(err); } const query2 = util.format( - "INSERT INTO player_matches (%s) VALUES (%s)", - Object.keys(obj2).join(","), + 'INSERT INTO player_matches (%s) VALUES (%s)', + Object.keys(obj2).join(','), Object.keys(obj2) - .map(() => "?") - .join(",") + .map(() => '?') + .join(',') ); const arr2 = Object.keys(obj2).map((k) => - obj2[k] === "true" || obj2[k] === "false" + obj2[k] === 'true' || obj2[k] === 'false' ? JSON.parse(obj2[k]) : obj2[k] ); @@ -1340,8 +1345,8 @@ function insertMatch(match, options, cb) { function telemetry(cb) { // console.log('[INSERTMATCH] updating telemetry'); const types = { - api: "matches_last_added", - parsed: "matches_last_parsed", + api: 'matches_last_added', + parsed: 'matches_last_parsed', }; if (types[options.type]) { redis.lpush( @@ -1355,11 +1360,11 @@ function insertMatch(match, options, cb) { ); redis.ltrim(types[options.type], 0, 9); } - if (options.type === "parsed") { - redisCount(redis, "parser"); + if (options.type === 'parsed') { + redisCount(redis, 'parser'); } - if (options.origin === "scanner") { - redisCount(redis, "added_match"); + if (options.origin === 'scanner') { + redisCount(redis, 'added_match'); } return cb(); } @@ -1388,28 +1393,28 @@ function insertMatch(match, options, cb) { if (options.skipCounts) { return cb(); } - if (options.origin === "scanner") { - return redis.rpush("countsQueue", JSON.stringify(match), cb); + if (options.origin === 'scanner') { + return redis.rpush('countsQueue', JSON.stringify(match), cb); } return cb(); } function decideScenarios(cb) { if ( - options.type === "parsed" && + options.type === 'parsed' && match.match_id % 100 < config.SCENARIOS_SAMPLE_PERCENT ) { - return redis.rpush("scenariosQueue", match.match_id, cb); + return redis.rpush('scenariosQueue', match.match_id, cb); } return cb(); } function decideBenchmarks(cb) { if ( - options.origin === "scanner" && + options.origin === 'scanner' && match.match_id % 100 < config.BENCHMARKS_SAMPLE_PERCENT ) { - return redis.rpush("parsedBenchmarksQueue", match.match_id, cb); + return redis.rpush('parsedBenchmarksQueue', match.match_id, cb); } return cb(); } @@ -1419,14 +1424,14 @@ function insertMatch(match, options, cb) { match.players, (p, cb) => { if ( - options.origin === "scanner" && + options.origin === 'scanner' && match.lobby_type === 7 && p.account_id && p.account_id !== utility.getAnonymousAccountId() && config.ENABLE_RANDOM_MMR_UPDATE ) { redis.rpush( - "mmrQueue", + 'mmrQueue', JSON.stringify({ match_id: match.match_id, account_id: p.account_id, @@ -1447,13 +1452,13 @@ function insertMatch(match, options, cb) { (p, cb) => { if ( match.match_id % 100 < Number(config.SCANNER_PLAYER_PERCENT) && - options.origin === "scanner" && + options.origin === 'scanner' && p.account_id && p.account_id !== utility.getAnonymousAccountId() ) { upsert( db, - "players", + 'players', { account_id: p.account_id }, { account_id: p.account_id }, cb @@ -1469,12 +1474,12 @@ function insertMatch(match, options, cb) { function decideGcData(cb) { // Don't get replay URLs for event matches if ( - options.origin === "scanner" && + options.origin === 'scanner' && match.game_mode !== 19 && match.match_id % 100 < Number(config.GCDATA_PERCENT) ) { redis.rpush( - "gcQueue", + 'gcQueue', JSON.stringify({ match_id: match.match_id, pgroup: match.pgroup, @@ -1501,7 +1506,7 @@ function insertMatch(match, options, cb) { return async.some( match.players, (p, cb) => { - redis.zscore("tracked", String(p.account_id), (err, score) => + redis.zscore('tracked', String(p.account_id), (err, score) => cb(err, Boolean(score)) ); }, @@ -1519,7 +1524,7 @@ function insertMatch(match, options, cb) { priority = -2; } return queue.addJob( - "parse", + 'parse', { data: { match_id: match.match_id, @@ -1574,7 +1579,7 @@ function insertMatch(match, options, cb) { function getItemTimings(req, cb) { const heroId = req.query.hero_id || 0; - const item = req.query.item || ""; + const item = req.query.item || ''; db.raw( `SELECT hero_id, item, time, sum(games) games, sum(wins) wins FROM scenarios @@ -1606,7 +1611,7 @@ function getTeamScenarios(req, cb) { const scenario = (su.teamScenariosQueryParams.includes(req.query.scenario) && req.query.scenario) || - ""; + ''; db.raw( `SELECT scenario, is_radiant, region, sum(games) games, sum(wins) wins FROM team_scenarios @@ -1624,7 +1629,7 @@ function getMetadata(req, callback) { cb(null, su.metadata); }, banner(cb) { - redis.get("banner", cb); + redis.get('banner', cb); }, user(cb) { cb(null, req.user); @@ -1648,7 +1653,7 @@ function getMetadata(req, callback) { async function getMatchData(matchId) { const result = await cassandra.execute( - "SELECT * FROM matches where match_id = ?", + 'SELECT * FROM matches where match_id = ?', [Number(matchId)], { prepare: true, @@ -1662,7 +1667,7 @@ async function getMatchData(matchId) { async function getPlayerMatchData(matchId) { const result = await cassandra.execute( - "SELECT * FROM player_matches where match_id = ?", + 'SELECT * FROM player_matches where match_id = ?', [Number(matchId)], { prepare: true, diff --git a/store/queue.js b/store/queue.js index f2a63d878..0c424178d 100644 --- a/store/queue.js +++ b/store/queue.js @@ -1,14 +1,14 @@ /** * Provides methods for working with the job queue * */ -const moment = require("moment"); -const async = require("async"); -const redis = require("./redis"); -const db = require("./db"); +const moment = require('moment'); +const async = require('async'); +const redis = require('./redis'); +const db = require('./db'); function runQueue(queueName, parallelism, processor) { function processOneJob(cb) { - redis.blpop(queueName, "0", (err, job) => { + redis.blpop(queueName, '0', (err, job) => { if (err) { throw err; } @@ -46,7 +46,7 @@ function runReliableQueue(queueName, parallelism, processor) { ) RETURNING * `, - [moment().add(2, "minute"), queueName] + [moment().add(2, 'minute'), queueName] ) .asCallback((err, result) => { const job = result && result.rows && result.rows[0]; @@ -55,7 +55,7 @@ function runReliableQueue(queueName, parallelism, processor) { } if (!job) { trx.commit(); - console.log("no job available, waiting"); + console.log('no job available, waiting'); return setTimeout(cb, 5000); } return processor(job.data, (err) => { @@ -66,7 +66,7 @@ function runReliableQueue(queueName, parallelism, processor) { if (!err || job.attempts <= 0) { // remove the job from the queue if successful or out of attempts trx - .raw("DELETE FROM queue WHERE id = ?", [job.id]) + .raw('DELETE FROM queue WHERE id = ?', [job.id]) .asCallback((err) => { if (err) { throw err; @@ -113,7 +113,7 @@ function addJob(queueName, job, options, cb) { } function getJob(jobId, cb) { - db.raw("SELECT * FROM queue WHERE id = ?", [jobId]).asCallback( + db.raw('SELECT * FROM queue WHERE id = ?', [jobId]).asCallback( (err, result) => { if (err) { return cb(err); diff --git a/store/redis.js b/store/redis.js index b7b3cf567..9ac6a532a 100644 --- a/store/redis.js +++ b/store/redis.js @@ -1,14 +1,14 @@ /** * Interface to Redis client * */ -const redis = require("redis"); -const config = require("../config"); +const redis = require('redis'); +const config = require('../config'); -console.log("connecting %s", config.REDIS_URL); +console.log('connecting %s', config.REDIS_URL); const client = redis.createClient(config.REDIS_URL, { detect_buffers: true, }); -client.on("error", (err) => { +client.on('error', (err) => { console.error(err); process.exit(1); }); diff --git a/store/search.js b/store/search.js index 97f7e2219..be6e22a52 100644 --- a/store/search.js +++ b/store/search.js @@ -1,16 +1,16 @@ /** * Methods for search functionality * */ -const async = require("async"); -const db = require("./db"); +const async = require('async'); +const db = require('./db'); /** * @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"]) - .from("players") + db.first(['account_id', 'personaname', 'avatarfull']) + .from('players') .where(search) .asCallback(cb); } diff --git a/store/searchES.js b/store/searchES.js index 09cfec1fa..7f20e23ea 100644 --- a/store/searchES.js +++ b/store/searchES.js @@ -1,17 +1,17 @@ /** * Methods for search functionality * */ -const async = require("async"); -const db = require("./db"); -const { es, INDEX } = require("./elasticsearch"); +const async = require('async'); +const db = require('./db'); +const { es, INDEX } = require('./elasticsearch'); /** * @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"]) - .from("players") + db.first(['account_id', 'personaname', 'avatarfull']) + .from('players') .where(search) .asCallback(cb); } @@ -44,7 +44,7 @@ function search(options, cb) { }, }, }, - sort: [{ _score: "desc" }, { last_match_time: "desc" }], + sort: [{ _score: 'desc' }, { last_match_time: 'desc' }], }, }, (err, { body }) => { diff --git a/svc/apiadmin.js b/svc/apiadmin.js index eeac99426..6a056146b 100644 --- a/svc/apiadmin.js +++ b/svc/apiadmin.js @@ -1,24 +1,24 @@ -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"); +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'); const stripe = stripeLib(config.STRIPE_SECRET); const { invokeInterval } = utility; function storeUsageCounts(cursor, cb) { - redis.hscan("usage_count", cursor, (err, results) => { + redis.hscan('usage_count', cursor, (err, results) => { if (err) { cb(err); } else { const cursor = results[0]; const values = results[1]; - const apiTimestamp = moment().startOf("day"); + const apiTimestamp = moment().startOf('day'); async.eachOfLimit( values, @@ -26,13 +26,13 @@ function storeUsageCounts(cursor, cb) { (e, i, cb2) => { if (i % 2) { cb2(); - } else if (e.includes(":")) { + } else if (e.includes(':')) { cb2(); } else if (config.ENABLE_API_LIMIT) { const split = e; // console.log("Updating usage for", e, "usage", values[i + 1]); let apiRecord; - db.from("api_keys") + db.from('api_keys') .where({ api_key: split, }) @@ -51,17 +51,17 @@ function storeUsageCounts(cursor, cb) { apiRecord.api_key, apiRecord.customer_id, apiTimestamp, - "", + '', values[i + 1], values[i + 1], ] ); } - throw Error("No record found."); + throw Error('No record found.'); }) .then(() => cb2()) .catch((e) => { - if (e.message === "No record found.") { + if (e.message === 'No record found.') { cb2(); } else { cb2(e); @@ -76,7 +76,7 @@ function storeUsageCounts(cursor, cb) { return cb(err); } - if (cursor !== "0") { + if (cursor !== '0') { return storeUsageCounts(cursor, cb); } @@ -94,7 +94,7 @@ async function updateStripeUsage(cb) { // From the docs: // By default, returns a list of subscriptions that have not been canceled. // In order to list canceled subscriptions, specify status=canceled. Use all for completeness. - status: "all", + status: 'all', }; let num = 0; try { @@ -103,8 +103,8 @@ async function updateStripeUsage(cb) { num++; // Deactivate any keys which failed to bill // updateAPIKeysInRedis deletes the keys so just do that there - if (sub.status === "canceled") { - console.log("updateStripeUsage CANCELED SUBSCRIPTION", sub.id); + if (sub.status === 'canceled') { + console.log('updateStripeUsage CANCELED SUBSCRIPTION', sub.id); await db.raw( ` UPDATE api_keys SET is_canceled = true WHERE subscription_id = ? @@ -117,8 +117,8 @@ async function updateStripeUsage(cb) { const startTime = moment .unix(sub.current_period_end - 1) - .startOf("month"); - const endTime = moment.unix(sub.current_period_end - 1).endOf("month"); + .startOf('month'); + const endTime = moment.unix(sub.current_period_end - 1).endOf('month'); const res = await db.raw( ` @@ -138,7 +138,7 @@ async function updateStripeUsage(cb) { GROUP BY api_key_usage.api_key, api_key_usage.ip ) as t1 `, - [startTime.format("YYYY-MM-DD"), endTime.format("YYYY-MM-DD"), sub.id] + [startTime.format('YYYY-MM-DD'), endTime.format('YYYY-MM-DD'), sub.id] ); if (res.rows.length > 0 && res.rows[0].usage_count) { @@ -149,11 +149,11 @@ async function updateStripeUsage(cb) { // 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), - action: "set", + action: 'set', timestamp: sub.current_period_end - 1, }); console.log( - "updateStripeUsage updated", + 'updateStripeUsage updated', sub.id, usageCount, Math.ceil(usageCount / config.API_BILLING_UNIT) @@ -170,31 +170,37 @@ async function updateStripeUsage(cb) { } } -invokeInterval((cb) => { - queries.getAPIKeys(db, (err, rows) => { - if (err) { - cb(err); - } else if (rows.length > 0) { - const keys = rows.map((e) => e.api_key); - console.log("getApikeys", rows.length, keys); - redis - .multi() - .del("api_keys") - .sadd("api_keys", keys) - .exec((err, res) => { - if (err) { - cb(err); - } - console.log("[API KEY CACHE] Got response:", res); - cb(); - }); - } else { - cb(); - } - }); -}, 5 * 60 * 1000); // Update every 5 min +invokeInterval( + (cb) => { + queries.getAPIKeys(db, (err, rows) => { + if (err) { + cb(err); + } else if (rows.length > 0) { + const keys = rows.map((e) => e.api_key); + console.log('getApikeys', rows.length, keys); + redis + .multi() + .del('api_keys') + .sadd('api_keys', keys) + .exec((err, res) => { + if (err) { + cb(err); + } + console.log('[API KEY CACHE] Got response:', res); + cb(); + }); + } else { + cb(); + } + }); + }, + 5 * 60 * 1000 +); // Update every 5 min -invokeInterval((cb) => { - storeUsageCounts(0, cb); -}, 10 * 60 * 1000); // Every 10 minutes +invokeInterval( + (cb) => { + storeUsageCounts(0, cb); + }, + 10 * 60 * 1000 +); // Every 10 minutes invokeInterval(updateStripeUsage, 5 * 60 * 1000); // Every 5 minutes diff --git a/svc/autofullhistory.js b/svc/autofullhistory.js index 5f1e978ce..1ef7e37be 100644 --- a/svc/autofullhistory.js +++ b/svc/autofullhistory.js @@ -1,9 +1,9 @@ /** * Worker to auto-queue full history requests for random players * */ -const async = require("async"); -const db = require("../store/db"); -const redis = require("../store/redis"); +const async = require('async'); +const db = require('../store/db'); +const redis = require('../store/redis'); function getSummaries(cb) { db.raw( @@ -17,7 +17,7 @@ function getSummaries(cb) { result.rows, (row, cb) => { return redis.rpush( - "fhQueue", + 'fhQueue', JSON.stringify({ account_id: row.account_id, short_history: true, diff --git a/svc/backupscanner.js b/svc/backupscanner.js index 057e6cc2f..4b36bdb17 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"); +const async = require('async'); +const utility = require('../util/utility'); +const redis = require('../store/redis'); +const queries = require('../store/queries'); +const config = require('../config'); const { generateJob, getData } = utility; const { insertMatch } = queries; -const apiKeys = config.STEAM_API_KEY.split(","); -const apiHosts = config.STEAM_API_HOST.split(","); +const apiKeys = config.STEAM_API_KEY.split(','); +const apiHosts = config.STEAM_API_HOST.split(','); const parallelism = Math.min(apiHosts.length * 1, apiKeys.length); const delay = 1000; @@ -20,7 +20,7 @@ function processMatch(matchId, cb) { if (res) { return cb(); } - const job = generateJob("api_details", { + const job = generateJob('api_details', { match_id: matchId, }); const { url } = job; @@ -40,8 +40,8 @@ function processMatch(matchId, cb) { return insertMatch( match, { - type: "api", - origin: "scanner", + type: 'api', + origin: 'scanner', skipCounts: false, }, (err) => { @@ -63,7 +63,7 @@ function processMatch(matchId, cb) { } function processPlayer(accountId, cb) { - const ajob = generateJob("api_history", { + const ajob = generateJob('api_history', { account_id: accountId, }); getData( @@ -79,7 +79,7 @@ function processPlayer(accountId, cb) { // Skip this player on this iteration return cb(); } - return redis.get("match_seq_num", (err, res) => { + return redis.get('match_seq_num', (err, res) => { if (err) { return cb(err); } @@ -97,9 +97,9 @@ function start(err) { if (err) { throw err; } - console.log("starting backupscanner loop"); + console.log('starting backupscanner loop'); setTimeout(() => { - redis.zrange("tracked", 0, -1, (err, ids) => { + redis.zrange('tracked', 0, -1, (err, ids) => { if (err) { throw err; } diff --git a/svc/benchmarks.js b/svc/benchmarks.js index d5e95d467..989f6ea55 100644 --- a/svc/benchmarks.js +++ b/svc/benchmarks.js @@ -1,9 +1,9 @@ -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"); +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'); const { benchmarks } = benchmarksUtil; @@ -23,14 +23,14 @@ async function doBenchmarks(matchID, cb) { !Number.isNaN(Number(metric)) ) { const rkey = [ - "benchmarks", + 'benchmarks', utility.getStartOfBlockMinutes( config.BENCHMARK_RETENTION_MINUTES, 0 ), key, p.hero_id, - ].join(":"); + ].join(':'); redis.zadd(rkey, metric, match.match_id); // expire at time two epochs later (after prev/current cycle) const expiretime = utility.getStartOfBlockMinutes( @@ -50,4 +50,4 @@ async function doBenchmarks(matchID, cb) { } } -queue.runQueue("parsedBenchmarksQueue", 1, doBenchmarks); +queue.runQueue('parsedBenchmarksQueue', 1, doBenchmarks); diff --git a/svc/buildsets.js b/svc/buildsets.js index 6f1fb0cc7..7f30fea4d 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"); +const buildSets = require('../store/buildSets'); +const redis = require('../store/redis'); +const db = require('../store/db'); +const utility = require('../util/utility'); const { invokeInterval } = utility; diff --git a/svc/cassandraDelete.js b/svc/cassandraDelete.js index da44ddfcc..ea72f8bb6 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"); +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'); function genRandomNumber(byteCount, radix) { - return BigInt(`0x${ crypto.randomBytes(byteCount).toString("hex")}`).toString( + return BigInt(`0x${crypto.randomBytes(byteCount).toString('hex')}`).toString( radix ); } @@ -15,7 +15,7 @@ const PARSED_DATA_DELETE_ID = 0; async function start() { // Get the current max_match_id from postgres, subtract 200000000 - const max = (await db.raw("select max(match_id) from public_matches")) + const max = (await db.raw('select max(match_id) from public_matches')) ?.rows?.[0]?.max; const limit = max - 200000000; while (true) { @@ -25,7 +25,7 @@ async function start() { // Convert to signed bigint const randomBigint = BigInt.asIntN(64, genRandomNumber(8, 10)); const result = await cassandra.execute( - "select match_id, version, token(match_id) from matches where token(match_id) >= ? limit 500 ALLOW FILTERING;", + 'select match_id, version, token(match_id) from matches where token(match_id) >= ? limit 500 ALLOW FILTERING;', [randomBigint.toString()], { prepare: true, @@ -45,16 +45,16 @@ async function start() { .map((result) => result.match_id); console.log( ids.length, - "out of", + 'out of', result.rows.length, - "to delete, ex:", + 'to delete, ex:', ids[0]?.toString() ); // Delete matches await Promise.all( ids.map((id) => - cassandra.execute("DELETE from matches where match_id = ?", [id], { + cassandra.execute('DELETE from matches where match_id = ?', [id], { prepare: true, }) ) @@ -63,7 +63,7 @@ async function start() { await Promise.all( ids.map((id) => cassandra.execute( - "DELETE from player_matches where match_id = ?", + 'DELETE from player_matches where match_id = ?', [id], { prepare: true, @@ -71,11 +71,21 @@ async function start() { ) ) ); - const parsedIds = result.rows.filter(result => result.version != null).map(result => result.match_id); - config.MATCH_ARCHIVE_S3_ENDPOINT && await Promise.all(parsedIds.map(id => doArchive(id))); + const parsedIds = result.rows + .filter((result) => result.version != null) + .map((result) => result.match_id); + config.MATCH_ARCHIVE_S3_ENDPOINT && + (await Promise.all(parsedIds.map((id) => doArchive(id)))); // TODO remove insert once backfill complete - await Promise.all(parsedIds.map(id => db.raw("INSERT INTO parsed_matches(match_id) VALUES(?) ON CONFLICT DO NOTHING", [Number(id)]))); + await Promise.all( + parsedIds.map((id) => + db.raw( + 'INSERT INTO parsed_matches(match_id) VALUES(?) ON CONFLICT DO NOTHING', + [Number(id)] + ) + ) + ); } catch (e) { console.log(e); } @@ -86,7 +96,9 @@ async function doArchive(matchId) { // archive old parsed match blobs to s3 compatible storage const match = await getMatchData(matchId); const playerMatches = await getPlayerMatchData(matchId); - const blob = Buffer.from(JSON.stringify({...match, players: playerMatches })); + const blob = Buffer.from( + JSON.stringify({ ...match, players: playerMatches }) + ); const result = await archivePut(matchId.toString(), blob); if (result) { // TODO Delete from Cassandra after archival diff --git a/svc/cosmetics.js b/svc/cosmetics.js index 8b76adb48..c40e9376c 100644 --- a/svc/cosmetics.js +++ b/svc/cosmetics.js @@ -1,15 +1,15 @@ -const vdf = require("simple-vdf"); -const async = require("async"); -const db = require("../store/db"); -const utility = require("../util/utility"); -const queries = require("../store/queries"); +const vdf = require('simple-vdf'); +const async = require('async'); +const db = require('../store/db'); +const utility = require('../util/utility'); +const queries = require('../store/queries'); const { invokeInterval, cleanItemSchema } = utility; function doCosmetics(cb) { utility.getData( { - url: "https://raw.githubusercontent.com/SteamDatabase/GameTracking-Dota2/master/game/dota/pak01_dir/scripts/items/items_game.txt", + url: 'https://raw.githubusercontent.com/SteamDatabase/GameTracking-Dota2/master/game/dota/pak01_dir/scripts/items/items_game.txt', raw: true, }, (err, body) => { @@ -26,14 +26,14 @@ function doCosmetics(cb) { item.item_id = Number(itemId); const hero = item.used_by_heroes && - typeof item.used_by_heroes === "object" && + typeof item.used_by_heroes === 'object' && Object.keys(item.used_by_heroes)[0]; function insert(cb) { // console.log(item); return queries.upsert( db, - "cosmetics", + 'cosmetics', item, { item_id: item.item_id, @@ -49,11 +49,11 @@ function doCosmetics(cb) { return cb(); } if (item.image_inventory) { - const spl = item.image_inventory.split("/"); + const spl = item.image_inventory.split('/'); const iconname = spl[spl.length - 1]; return utility.getData( { - url: utility.generateJob("api_item_icon", { + url: utility.generateJob('api_item_icon', { iconname, }).url, noRetry: true, diff --git a/svc/counts.js b/svc/counts.js index 9babf8cd2..48e511433 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"); +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'); const { getMatchRankTier, @@ -44,8 +44,8 @@ function updateHeroRankings(match, cb) { const win = Number(isRadiant(player) === player.radiant_win); const kFactor = 100; return db - .select("score") - .from("hero_ranking") + .select('score') + .from('hero_ranking') .where({ account_id: player.account_id, hero_id: player.hero_id, @@ -64,7 +64,7 @@ function updateHeroRankings(match, cb) { const newScore = currRating1 + ratingDiff1; return db .raw( - "INSERT INTO hero_ranking VALUES(?, ?, ?) ON CONFLICT(account_id, hero_id) DO UPDATE SET score = ?", + 'INSERT INTO hero_ranking VALUES(?, ?, ?) ON CONFLICT(account_id, hero_id) DO UPDATE SET score = ?', [player.account_id, player.hero_id, newScore, newScore] ) .asCallback(cb); @@ -127,7 +127,7 @@ function upsertMatchSample(match, cb) { const newMatch = { ...match, ...matchMmrData }; return upsert( trx, - "public_matches", + 'public_matches', newMatch, { match_id: newMatch.match_id, @@ -143,7 +143,7 @@ function upsertMatchSample(match, cb) { pm.match_id = match.match_id; upsert( trx, - "public_player_matches", + 'public_player_matches', pm, { match_id: pm.match_id, @@ -182,27 +182,27 @@ function updateRecord(field, match, player) { redis.zadd( `records:${field}`, match[field] || player[field], - [match.match_id, match.start_time, player.hero_id].join(":") + [match.match_id, match.start_time, player.hero_id].join(':') ); // Keep only 100 top scores - redis.zremrangebyrank(`records:${field}`, "0", "-101"); - const expire = moment().add(1, "month").startOf("month").format("X"); + redis.zremrangebyrank(`records:${field}`, '0', '-101'); + const expire = moment().add(1, 'month').startOf('month').format('X'); redis.expireat(`records:${field}`, expire); } function updateRecords(match, cb) { - updateRecord("duration", match, {}); + updateRecord('duration', match, {}); match.players.forEach((player) => { - updateRecord("kills", match, player); - updateRecord("deaths", match, player); - updateRecord("assists", match, player); - updateRecord("last_hits", match, player); - updateRecord("denies", match, player); - updateRecord("gold_per_min", match, player); - updateRecord("xp_per_min", match, player); - updateRecord("hero_damage", match, player); - updateRecord("tower_damage", match, player); - updateRecord("hero_healing", match, player); + updateRecord('kills', match, player); + updateRecord('deaths', match, player); + updateRecord('assists', match, player); + updateRecord('last_hits', match, player); + updateRecord('denies', match, player); + updateRecord('gold_per_min', match, player); + updateRecord('xp_per_min', match, player); + updateRecord('hero_damage', match, player); + updateRecord('tower_damage', match, player); + updateRecord('hero_healing', match, player); }); cb(); } @@ -290,7 +290,7 @@ function updateHeroSearch(match, cb) { return db .raw( - "INSERT INTO hero_search (match_id, teamA, teamB, teamAWin, start_time) VALUES (?, ?, ?, ?, ?)", + 'INSERT INTO hero_search (match_id, teamA, teamB, teamAWin, start_time) VALUES (?, ?, ?, ?, ?)', [match.match_id, teamA, teamB, teamAWin, match.start_time] ) .asCallback(cb); @@ -302,14 +302,14 @@ function updateTurbo(match, cb) { const heroId = player.hero_id; if (heroId) { const win = Number(isRadiant(player) === match.radiant_win); - redis.hincrby("turboPicks", heroId, 1); + redis.hincrby('turboPicks', heroId, 1); if (win) { - redis.hincrby("turboWins", heroId, 1); + redis.hincrby('turboWins', heroId, 1); } } } - redis.expireat("turboPicks", moment().endOf("month").unix()); - redis.expireat("turboWins", moment().endOf("month").unix()); + redis.expireat('turboPicks', moment().endOf('month').unix()); + redis.expireat('turboWins', moment().endOf('month').unix()); cb(); } @@ -346,7 +346,7 @@ function updateMatchups(match, cb) { */ function processCounts(match, cb) { - console.log("match %s", match.match_id); + console.log('match %s', match.match_id); return async.parallel( { updateRankings(cb) { @@ -407,4 +407,4 @@ function processCounts(match, cb) { ); } -queue.runQueue("countsQueue", 1, processCounts); +queue.runQueue('countsQueue', 1, processCounts); diff --git a/svc/distributions.js b/svc/distributions.js index dbba4f518..65f33dc4e 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"); +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'); const { invokeInterval } = utility; const sql = {}; -const sqlq = fs.readdirSync("./sql"); +const sqlq = fs.readdirSync('./sql'); sqlq.forEach((f) => { - sql[f.split(".")[0]] = fs.readFileSync(`./sql/${f}`, "utf8"); + sql[f.split('.')[0]] = fs.readFileSync(`./sql/${f}`, 'utf8'); }); function mapMmr(results) { @@ -59,13 +59,13 @@ function doDistributions(cb) { async.parallel( { country_mmr(cb) { - loadData("country_mmr", mapCountry, cb); + loadData('country_mmr', mapCountry, cb); }, mmr(cb) { - loadData("mmr", mapMmr, cb); + loadData('mmr', mapMmr, cb); }, ranks(cb) { - loadData("ranks", mapMmr, cb); + loadData('ranks', mapMmr, cb); }, }, (err, result) => { diff --git a/svc/fullhistory.js b/svc/fullhistory.js index 1cc1b2b9b..72e1ee549 100644 --- a/svc/fullhistory.js +++ b/svc/fullhistory.js @@ -1,25 +1,25 @@ /** * 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"); +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'); const { insertMatch } = queries; -const apiKeys = config.STEAM_API_KEY.split(","); +const apiKeys = config.STEAM_API_KEY.split(','); // number of api requests to send at once const parallelism = Math.min(20, apiKeys.length); function processFullHistory(job, cb) { function updatePlayer(player, cb) { // done with this player, update - db("players") + db('players') .update({ full_history_time: new Date(), fh_unavailable: player.fh_unavailable, @@ -31,8 +31,8 @@ function processFullHistory(job, cb) { if (err) { return cb(err); } - console.log("got full match history for %s", player.account_id); - redisCount(redis, "fullhistory"); + console.log('got full match history for %s', player.account_id); + redisCount(redis, 'fullhistory'); return cb(err); }); } @@ -41,7 +41,7 @@ function processFullHistory(job, cb) { getData(url, (err, body) => { if (err) { // non-retryable error, probably the user's account is private - console.log("non-retryable error"); + console.log('non-retryable error'); return cb(err); } // if !body.result, retry @@ -78,7 +78,7 @@ function processFullHistory(job, cb) { // if test or only want last 100 (no paging), set short_history // const heroArray = job.short_history || config.NODE_ENV === 'test' ? ['0'] : Object.keys(constants.heroes); // As of December 2021 filtering by hero ID doesn't work - const heroArray = ["0"]; + 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( @@ -86,13 +86,13 @@ function processFullHistory(job, cb) { parallelism, (heroId, cb) => { // make a request for every possible hero - const container = generateJob("api_history", { + const container = generateJob('api_history', { account_id: player.account_id, hero_id: heroId, matches_requested: 100, }); getApiMatchPage(player, container.url, (err) => { - console.log("%s matches found", Object.keys(player.match_ids).length); + console.log('%s matches found', Object.keys(player.match_ids).length); cb(err); }); }, @@ -100,21 +100,21 @@ function processFullHistory(job, cb) { player.fh_unavailable = Boolean(err); if (err) { // non-retryable error while scanning, user had a private account - console.log("error: %s", JSON.stringify(err)); + console.log('error: %s', JSON.stringify(err)); updatePlayer(player, cb); } else { // check what matches the player is already associated with queries.getPlayerMatches( player.account_id, { - project: ["match_id"], + project: ['match_id'], }, (err, docs) => { if (err) { return cb(err); } console.log( - "%s matches found, %s already in db, %s to add", + '%s matches found, %s already in db, %s to add', Object.keys(player.match_ids).length, docs.length, Object.keys(player.match_ids).length - docs.length @@ -131,7 +131,7 @@ function processFullHistory(job, cb) { parallelism, (matchId, cb) => { // process api jobs directly with parallelism - const container = generateJob("api_details", { + const container = generateJob('api_details', { match_id: Number(matchId), }); getData(container.url, (err, body) => { @@ -142,7 +142,7 @@ function processFullHistory(job, cb) { return insertMatch( match, { - type: "api", + type: 'api', skipParse: true, }, cb @@ -163,4 +163,4 @@ function processFullHistory(job, cb) { ); } -queue.runQueue("fhQueue", 1, processFullHistory); +queue.runQueue('fhQueue', 1, processFullHistory); diff --git a/svc/gcdata.js b/svc/gcdata.js index ea5ae667d..59383fb41 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"); +const getGcData = require('../util/getGcData'); +const queue = require('../store/queue'); +const config = require('../config'); +const utility = require('../util/utility'); const { getRetrieverArr } = utility; const retrieverArr = getRetrieverArr(); @@ -15,7 +15,7 @@ function processGcData(job, cb) { } queue.runQueue( - "gcQueue", + 'gcQueue', Number(config.GCDATA_PARALLELISM) * retrieverArr.length, processGcData ); diff --git a/svc/heroes.js b/svc/heroes.js index d9fec4af8..21ec5f306 100644 --- a/svc/heroes.js +++ b/svc/heroes.js @@ -1,13 +1,13 @@ -const async = require("async"); -const db = require("../store/db"); -const utility = require("../util/utility"); -const queries = require("../store/queries"); +const async = require('async'); +const db = require('../store/db'); +const utility = require('../util/utility'); +const queries = require('../store/queries'); const { invokeInterval, generateJob, getData } = utility; function doHeroes(cb) { - const container = generateJob("api_heroes", { - language: "english", + const container = generateJob('api_heroes', { + language: 'english', }); getData(container.url, (err, body) => { if (err) { @@ -17,7 +17,7 @@ function doHeroes(cb) { return cb(); } return getData( - "https://raw.githubusercontent.com/odota/dotaconstants/master/build/heroes.json", + 'https://raw.githubusercontent.com/odota/dotaconstants/master/build/heroes.json', (err, heroData) => { if (err || !heroData) { return cb(); @@ -28,7 +28,7 @@ function doHeroes(cb) { const heroDataHero = heroData[hero.id] || {}; queries.upsert( db, - "heroes", + 'heroes', { ...hero, primary_attr: heroDataHero.primary_attr, diff --git a/svc/herostats.js b/svc/herostats.js index 3537483a4..b8ed855d3 100644 --- a/svc/herostats.js +++ b/svc/herostats.js @@ -1,15 +1,15 @@ -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"); +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'); const { invokeInterval } = utility; function doHeroStats(cb) { - const minTime = moment().subtract(30, "day").format("X"); - const maxTime = moment().format("X"); + const minTime = moment().subtract(30, 'day').format('X'); + const maxTime = moment().format('X'); async.parallel( { publicHeroes(cb) { @@ -74,8 +74,8 @@ function doHeroStats(cb) { ).asCallback(cb); }, turboHeroes(cb) { - redis.hgetall("turboPicks", (err, picks) => { - redis.hgetall("turboWins", (err, wins) => { + redis.hgetall('turboPicks', (err, picks) => { + redis.hgetall('turboWins', (err, wins) => { const result = { rows: Object.keys(picks).map((key) => { return { @@ -100,7 +100,7 @@ function doHeroStats(cb) { result[key].rows.forEach((row) => { objectResponse[row.hero_id] = { ...objectResponse[row.hero_id], - ...(key === "publicHeroes" + ...(key === 'publicHeroes' ? { [`${row.rank_tier}_pick`]: row.pick, [`${row.rank_tier}_win`]: row.win, @@ -110,7 +110,7 @@ function doHeroStats(cb) { }); }); return redis.set( - "heroStats", + 'heroStats', JSON.stringify(Object.values(objectResponse)), cb ); diff --git a/svc/items.js b/svc/items.js index ed15ef03a..6291285c5 100644 --- a/svc/items.js +++ b/svc/items.js @@ -1,13 +1,13 @@ -const async = require("async"); -const db = require("../store/db"); -const utility = require("../util/utility"); -const queries = require("../store/queries"); +const async = require('async'); +const db = require('../store/db'); +const utility = require('../util/utility'); +const queries = require('../store/queries'); const { invokeInterval } = utility; function doItems(cb) { - const container = utility.generateJob("api_items", { - language: "english", + const container = utility.generateJob('api_items', { + language: 'english', }); utility.getData(container.url, (err, body) => { if (err) { @@ -21,7 +21,7 @@ function doItems(cb) { (item, cb) => { queries.upsert( db, - "items", + 'items', item, { id: item.id, diff --git a/svc/leagues.js b/svc/leagues.js index 0368c0380..80ba5fea5 100644 --- a/svc/leagues.js +++ b/svc/leagues.js @@ -1,12 +1,12 @@ -const async = require("async"); -const utility = require("../util/utility"); -const db = require("../store/db"); -const queries = require("../store/queries"); +const async = require('async'); +const utility = require('../util/utility'); +const db = require('../store/db'); +const queries = require('../store/queries'); const { invokeInterval, generateJob, getData } = utility; function doLeagues(cb) { - const container = generateJob("api_leagues", {}); + const container = generateJob('api_leagues', {}); getData(container.url, (err, apiLeagues) => { if (err) { return cb(err); @@ -16,15 +16,15 @@ function doLeagues(cb) { apiLeagues.infos, (league, cb) => { const openQualifierTier = - league.name.indexOf("Open Qualifier") === -1 ? null : "excluded"; - let eventTier = "excluded"; + league.name.indexOf('Open Qualifier') === -1 ? null : 'excluded'; + let eventTier = 'excluded'; if (league.tier === 2) { - eventTier = "professional"; + eventTier = 'professional'; } else if (league.tier >= 3) { - eventTier = "premium"; + eventTier = 'premium'; } if (league.league_id === 4664) { - eventTier = "premium"; + eventTier = 'premium'; } league.tier = openQualifierTier || eventTier || null; league.ticket = null; @@ -32,7 +32,7 @@ function doLeagues(cb) { league.leagueid = league.league_id; queries.upsert( db, - "leagues", + 'leagues', league, { leagueid: league.league_id, diff --git a/svc/livegames.js b/svc/livegames.js index 17abfde41..e0e3ddd16 100644 --- a/svc/livegames.js +++ b/svc/livegames.js @@ -1,20 +1,20 @@ -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"); +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'); const { invokeInterval } = utility; function doLiveGames(cb) { // Get the list of pro players db.select() - .from("notable_players") + .from('notable_players') .asCallback((err, proPlayers) => { // Get the list of live games - const apiKeys = config.STEAM_API_KEY.split(","); + const apiKeys = config.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) => { if (err) { @@ -41,14 +41,14 @@ function doLiveGames(cb) { }); // convert the BigInt to a string match.lobby_id = match.lobby_id.toString(); - redis.zadd("liveGames", match.lobby_id, match.lobby_id); + redis.zadd('liveGames', match.lobby_id, match.lobby_id); redis.setex( `liveGame:${match.lobby_id}`, 28800, JSON.stringify(match) ); // Keep only the 100 highest values - redis.zremrangebyrank("liveGames", "0", "-101"); + redis.zremrangebyrank('liveGames', '0', '-101'); } cb(); // Get detailed stats for each live game diff --git a/svc/migrater.js b/svc/migrater.js index f58d0a8dd..ba532b9b7 100644 --- a/svc/migrater.js +++ b/svc/migrater.js @@ -1,10 +1,10 @@ -const async = require("async"); -const fs = require("fs"); -const db = require("../store/db"); +const async = require('async'); +const fs = require('fs'); +const db = require('../store/db'); // const cassandra = require('../store/cassandra'); -const utility = require("../util/utility"); +const utility = require('../util/utility'); -const sqlQuery = fs.readFileSync("./sql/create_tables.sql", "utf8"); +const sqlQuery = fs.readFileSync('./sql/create_tables.sql', 'utf8'); // const cassQuery = fs.readFileSync('./sql/create_tables.cql', 'utf8'); const { invokeInterval } = utility; diff --git a/svc/mmr.js b/svc/mmr.js index 65861aca0..ffe01c60c 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"); +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'); const retrieverArr = getRetrieverArr(); @@ -23,7 +23,7 @@ function processMmr(job, cb) { if (err) { return cb(err); } - redisCount(redis, "retriever_player"); + redisCount(redis, 'retriever_player'); const player = { account_id: job.account_id || null, plus: Boolean(data.is_plus_subscriber), @@ -48,7 +48,7 @@ function processMmr(job, cb) { } queue.runQueue( - "mmrQueue", + 'mmrQueue', config.MMR_PARALLELISM * retrieverArr.length, processMmr ); diff --git a/svc/monitor.js b/svc/monitor.js index 6126a478b..4befa4d45 100644 --- a/svc/monitor.js +++ b/svc/monitor.js @@ -1,19 +1,19 @@ /** * 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"); +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'); -const apiKey = config.STEAM_API_KEY.split(",")[0]; +const apiKey = config.STEAM_API_KEY.split(',')[0]; function invokeInterval(func) { // invokes the function immediately, waits for callback, waits the delay, and then calls it again (function invoker() { - console.log("running %s", func.name); + console.log('running %s', func.name); console.time(func.name); func((err, result) => { if (err) { @@ -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); + redis.hset('health', func.name, JSON.stringify(final)); + redis.expire('health', 900); console.timeEnd(func.name); setTimeout(invoker, final && final.delay ? final.delay : 10000); }); @@ -34,10 +34,10 @@ function invokeInterval(func) { function steamApi(cb) { request( - `${"http://api.steampowered.com/IDOTA2Match_570/GetMatchHistory/V001/?key="}${apiKey}`, + `${'http://api.steampowered.com/IDOTA2Match_570/GetMatchHistory/V001/?key='}${apiKey}`, (err, resp, body) => { if (err || resp.statusCode !== 200) { - return cb("bad http response"); + return cb('bad http response'); } try { const fail = @@ -49,20 +49,20 @@ function steamApi(cb) { threshold: 1, }); } catch (e) { - return cb("malformed http response"); + return cb('malformed http response'); } } ); } function seqNumDelay(cb) { - utility.getData(utility.generateJob("api_history", {}).url, (err, body) => { + utility.getData(utility.generateJob('api_history', {}).url, (err, body) => { if (err) { - return cb("failed to get current sequence number"); + 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 redis.get('match_seq_num', (err, num) => { if (err) { return cb(err); } @@ -91,7 +91,7 @@ function parseDelay(cb) { } function gcDelay(cb) { - redis.llen("gcQueue", (err, result) => { + redis.llen('gcQueue', (err, result) => { if (err) { return cb(err); } diff --git a/svc/parser.js b/svc/parser.js index 63d54cf82..9c23f35fe 100755 --- a/svc/parser.js +++ b/svc/parser.js @@ -5,34 +5,34 @@ * 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"); +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'); const { insertMatch } = queries; const { buildReplayUrl } = utility; const app = express(); -app.get("/healthz", (req, res) => { - res.end("ok"); +app.get('/healthz', (req, res) => { + res.end('ok'); }); app.listen(config.PORT || config.PARSER_PORT); function runParse(match, job, cb) { let { url } = match; - if (config.NODE_ENV === "test") { + if (config.NODE_ENV === 'test') { url = `https://odota.github.io/testfiles/${match.match_id}_1.dem`; } console.log(new Date(), url); cp.exec( `curl --max-time 180 --fail ${url} | ${ - url && url.slice(-3) === "bz2" ? "bunzip2" : "cat" + url && url.slice(-3) === 'bz2' ? 'bunzip2' : 'cat' } | curl -X POST -T - ${ config.PARSER_HOST } | node processors/createParsedDataBlob.js ${match.match_id}`, @@ -45,7 +45,7 @@ function runParse(match, job, cb) { return insertMatch( result, { - type: "parsed", + type: 'parsed', skipParse: true, }, cb @@ -56,7 +56,7 @@ function runParse(match, job, cb) { function parseProcessor(job, cb) { const match = job; - if (!match.game_mode && match.origin !== "scanner") { + if (!match.game_mode && match.origin !== 'scanner') { // Skip parses without game_mode that weren't from scanner (do this to clear queue of event matches) return cb(); } @@ -83,7 +83,7 @@ function parseProcessor(job, cb) { if (err) { console.error(err.stack || err); } else { - console.log("completed parse of match %s", match.match_id); + console.log('completed parse of match %s', match.match_id); } return cb(err, match.match_id); } @@ -91,7 +91,7 @@ function parseProcessor(job, cb) { } queue.runReliableQueue( - "parse", + 'parse', Number(config.PARSER_PARALLELISM) || numCPUs, parseProcessor ); diff --git a/svc/profiler.js b/svc/profiler.js index 178a3dfdd..39c614516 100644 --- a/svc/profiler.js +++ b/svc/profiler.js @@ -1,23 +1,23 @@ /** * Worker to fetch updated player profiles * */ -const async = require("async"); -const queries = require("../store/queries"); -const db = require("../store/db"); +const async = require('async'); +const queries = require('../store/queries'); +const db = require('../store/db'); // const redis = require('../store/redis'); -const utility = require("../util/utility"); +const utility = require('../util/utility'); const { insertPlayer, bulkIndexPlayer } = queries; const { getData, generateJob, convert64to32 } = utility; function getSummaries(cb) { db.raw( - "SELECT account_id from players TABLESAMPLE SYSTEM_ROWS(100)" + 'SELECT account_id from players TABLESAMPLE SYSTEM_ROWS(100)' ).asCallback((err, result) => { if (err) { return cb(err); } - const container = generateJob("api_summaries", { + const container = generateJob('api_summaries', { players: result.rows, }); // Request rank_tier data for these players diff --git a/svc/proplayers.js b/svc/proplayers.js index 1a10ec484..62b9ae404 100644 --- a/svc/proplayers.js +++ b/svc/proplayers.js @@ -1,12 +1,12 @@ -const async = require("async"); -const db = require("../store/db"); -const queries = require("../store/queries"); -const utility = require("../util/utility"); +const async = require('async'); +const db = require('../store/db'); +const queries = require('../store/queries'); +const utility = require('../util/utility'); const { invokeInterval, generateJob, getData } = utility; function doProPlayers(cb) { - const container = generateJob("api_notable", {}); + const container = generateJob('api_notable', {}); getData(container.url, (err, body) => { if (err) { return cb(err); @@ -16,7 +16,7 @@ function doProPlayers(cb) { (p, cb) => { queries.upsert( db, - "notable_players", + 'notable_players', p, { account_id: p.account_id, diff --git a/svc/proxy.js b/svc/proxy.js index 49f8ffae2..3d7d57761 100644 --- a/svc/proxy.js +++ b/svc/proxy.js @@ -1,22 +1,22 @@ /** * Worker proxying requests to the Steam API. * */ -const httpProxy = require("http-proxy"); -const http = require("http"); -const config = require("../config"); +const httpProxy = require('http-proxy'); +const http = require('http'); +const config = require('../config'); const PORT = config.PORT || config.PROXY_PORT; const proxy = httpProxy.createProxyServer({ - target: "http://api.steampowered.com", + target: 'http://api.steampowered.com', changeOrigin: true, }); const server = http.createServer((req, res) => { - if (req.url === "/healthz") { - return res.end("ok"); + if (req.url === '/healthz') { + return res.end('ok'); } return proxy.web(req, res); }); server.listen(PORT); -console.log("listening on port %s", PORT); +console.log('listening on port %s', PORT); diff --git a/svc/repatch.js b/svc/repatch.js index 649005e9c..711713361 100644 --- a/svc/repatch.js +++ b/svc/repatch.js @@ -1,17 +1,17 @@ /** * 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"); +const async = require('async'); +const constants = require('dotaconstants'); +const db = require('../store/db'); +const queries = require('../store/queries'); +const utility = require('../util/utility'); const { invokeInterval } = utility; function rePatch() { - db.select(["match_id", "start_time"]) - .from("matches") + db.select(['match_id', 'start_time']) + .from('matches') .asCallback((err, matchIds) => { if (err) { throw err; @@ -21,7 +21,7 @@ function rePatch() { (match, cb) => { queries.upsert( db, - "match_patch", + 'match_patch', { match_id: match.match_id, patch: diff --git a/svc/retriever.js b/svc/retriever.js index f7b519fa3..122192a2c 100644 --- a/svc/retriever.js +++ b/svc/retriever.js @@ -2,20 +2,20 @@ * 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 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 ? { /* eslint-disable global-require */ - redis: require("../store/redis"), - crypto: require("crypto"), + redis: require('../store/redis'), + crypto: require('crypto'), /* eslint-enable global-require */ pendingTwoFactorAuth: {}, pendingSteamGuardAuth: {}, @@ -41,95 +41,95 @@ 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 = config.STEAM_USER.split(','); +let passes = config.STEAM_PASS.split(','); // For the latest list: https://api.steampowered.com/ISteamDirectory/GetCMList/v1/?format=json&cellid=0 Steam.servers = [ - { host: "155.133.242.9", port: 27018 }, - { host: "185.25.180.15", port: 27019 }, - { host: "185.25.180.15", port: 27018 }, - { host: "185.25.180.14", port: 27017 }, - { host: "185.25.180.15", port: 27017 }, - { host: "155.133.242.9", port: 27019 }, - { host: "155.133.242.9", port: 27017 }, - { host: "185.25.180.14", port: 27018 }, - { host: "185.25.180.14", port: 27019 }, - { host: "155.133.242.8", port: 27017 }, - { host: "155.133.242.8", port: 27018 }, - { host: "155.133.242.8", port: 27019 }, - { host: "162.254.197.40", port: 27018 }, - { host: "155.133.248.50", port: 27017 }, - { host: "155.133.248.51", port: 27017 }, - { host: "162.254.196.68", port: 27017 }, - { host: "162.254.197.41", port: 27017 }, - { host: "162.254.196.67", port: 27019 }, - { host: "155.133.248.53", port: 27018 }, - { host: "155.133.248.52", port: 27018 }, - { host: "162.254.196.67", port: 27017 }, - { host: "162.254.196.67", port: 27018 }, - { host: "162.254.196.83", port: 27017 }, - { host: "162.254.196.84", port: 27017 }, - { host: "155.133.248.52", port: 27017 }, - { host: "162.254.196.68", port: 27018 }, - { host: "162.254.197.40", port: 27019 }, - { host: "155.133.248.51", port: 27019 }, - { host: "155.133.248.52", port: 27019 }, - { host: "155.133.248.53", port: 27019 }, - { host: "155.133.248.50", port: 27019 }, - { host: "155.133.248.53", port: 27017 }, - { host: "162.254.196.68", port: 27019 }, - { host: "162.254.197.42", port: 27019 }, - { host: "162.254.196.84", port: 27018 }, - { host: "155.133.248.50", port: 27018 }, - { host: "162.254.196.83", port: 27019 }, - { host: "162.254.197.42", port: 27018 }, - { host: "162.254.197.41", port: 27018 }, - { host: "162.254.196.84", port: 27019 }, - { host: "162.254.196.83", port: 27018 }, - { host: "162.254.197.40", port: 27017 }, - { host: "162.254.197.41", port: 27019 }, - { host: "155.133.248.51", port: 27018 }, - { host: "162.254.197.42", port: 27017 }, - { host: "146.66.152.11", port: 27018 }, - { host: "146.66.152.11", port: 27019 }, - { host: "146.66.152.11", port: 27017 }, - { host: "146.66.152.10", port: 27019 }, - { host: "146.66.152.10", port: 27017 }, - { host: "146.66.152.10", port: 27018 }, - { host: "208.78.164.10", port: 27018 }, - { host: "208.78.164.9", port: 27019 }, - { host: "208.78.164.13", port: 27018 }, - { host: "208.78.164.9", port: 27017 }, - { host: "208.78.164.12", port: 27018 }, - { host: "208.78.164.10", port: 27017 }, - { host: "155.133.229.251", port: 27019 }, - { host: "155.133.229.251", port: 27017 }, - { host: "208.78.164.14", port: 27018 }, - { host: "208.78.164.12", port: 27019 }, - { host: "208.78.164.13", port: 27017 }, - { host: "208.78.164.9", port: 27018 }, - { host: "208.78.164.14", port: 27019 }, - { host: "208.78.164.11", port: 27018 }, - { host: "208.78.164.10", port: 27019 }, - { host: "155.133.229.250", port: 27017 }, - { host: "208.78.164.12", port: 27017 }, - { host: "208.78.164.11", port: 27019 }, - { host: "155.133.229.250", port: 27018 }, - { host: "155.133.229.251", port: 27018 }, - { host: "208.78.164.11", port: 27017 }, - { host: "155.133.229.250", port: 27019 }, - { host: "208.78.164.13", port: 27019 }, - { host: "208.78.164.14", port: 27017 }, - { host: "162.254.193.7", port: 27017 }, - { host: "162.254.193.47", port: 27019 }, - { host: "162.254.193.7", port: 27018 }, - { host: "162.254.193.46", port: 27018 }, - { host: "162.254.193.6", port: 27017 }, + { host: '155.133.242.9', port: 27018 }, + { host: '185.25.180.15', port: 27019 }, + { host: '185.25.180.15', port: 27018 }, + { host: '185.25.180.14', port: 27017 }, + { host: '185.25.180.15', port: 27017 }, + { host: '155.133.242.9', port: 27019 }, + { host: '155.133.242.9', port: 27017 }, + { host: '185.25.180.14', port: 27018 }, + { host: '185.25.180.14', port: 27019 }, + { host: '155.133.242.8', port: 27017 }, + { host: '155.133.242.8', port: 27018 }, + { host: '155.133.242.8', port: 27019 }, + { host: '162.254.197.40', port: 27018 }, + { host: '155.133.248.50', port: 27017 }, + { host: '155.133.248.51', port: 27017 }, + { host: '162.254.196.68', port: 27017 }, + { host: '162.254.197.41', port: 27017 }, + { host: '162.254.196.67', port: 27019 }, + { host: '155.133.248.53', port: 27018 }, + { host: '155.133.248.52', port: 27018 }, + { host: '162.254.196.67', port: 27017 }, + { host: '162.254.196.67', port: 27018 }, + { host: '162.254.196.83', port: 27017 }, + { host: '162.254.196.84', port: 27017 }, + { host: '155.133.248.52', port: 27017 }, + { host: '162.254.196.68', port: 27018 }, + { host: '162.254.197.40', port: 27019 }, + { host: '155.133.248.51', port: 27019 }, + { host: '155.133.248.52', port: 27019 }, + { host: '155.133.248.53', port: 27019 }, + { host: '155.133.248.50', port: 27019 }, + { host: '155.133.248.53', port: 27017 }, + { host: '162.254.196.68', port: 27019 }, + { host: '162.254.197.42', port: 27019 }, + { host: '162.254.196.84', port: 27018 }, + { host: '155.133.248.50', port: 27018 }, + { host: '162.254.196.83', port: 27019 }, + { host: '162.254.197.42', port: 27018 }, + { host: '162.254.197.41', port: 27018 }, + { host: '162.254.196.84', port: 27019 }, + { host: '162.254.196.83', port: 27018 }, + { host: '162.254.197.40', port: 27017 }, + { host: '162.254.197.41', port: 27019 }, + { host: '155.133.248.51', port: 27018 }, + { host: '162.254.197.42', port: 27017 }, + { host: '146.66.152.11', port: 27018 }, + { host: '146.66.152.11', port: 27019 }, + { host: '146.66.152.11', port: 27017 }, + { host: '146.66.152.10', port: 27019 }, + { host: '146.66.152.10', port: 27017 }, + { host: '146.66.152.10', port: 27018 }, + { host: '208.78.164.10', port: 27018 }, + { host: '208.78.164.9', port: 27019 }, + { host: '208.78.164.13', port: 27018 }, + { host: '208.78.164.9', port: 27017 }, + { host: '208.78.164.12', port: 27018 }, + { host: '208.78.164.10', port: 27017 }, + { host: '155.133.229.251', port: 27019 }, + { host: '155.133.229.251', port: 27017 }, + { host: '208.78.164.14', port: 27018 }, + { host: '208.78.164.12', port: 27019 }, + { host: '208.78.164.13', port: 27017 }, + { host: '208.78.164.9', port: 27018 }, + { host: '208.78.164.14', port: 27019 }, + { host: '208.78.164.11', port: 27018 }, + { host: '208.78.164.10', port: 27019 }, + { host: '155.133.229.250', port: 27017 }, + { host: '208.78.164.12', port: 27017 }, + { host: '208.78.164.11', port: 27019 }, + { host: '155.133.229.250', port: 27018 }, + { host: '155.133.229.251', port: 27018 }, + { host: '208.78.164.11', port: 27017 }, + { host: '155.133.229.250', port: 27019 }, + { host: '208.78.164.13', port: 27019 }, + { host: '208.78.164.14', port: 27017 }, + { host: '162.254.193.7', port: 27017 }, + { host: '162.254.193.47', port: 27019 }, + { host: '162.254.193.7', port: 27018 }, + { host: '162.254.193.46', port: 27018 }, + { host: '162.254.193.6', port: 27017 }, ]; function selfDestruct() { - console.log("shutting down"); + console.log('shutting down'); process.exit(0); } @@ -157,7 +157,7 @@ function genStats() { } function shaHash(buffer) { - const h = advancedAuth.crypto.createHash("sha1"); + const h = advancedAuth.crypto.createHash('sha1'); h.update(buffer); return h.digest(); } @@ -216,7 +216,7 @@ function getGcMatchData(idx, matchId, cb) { const end = Date.now(); // Reset delay on success matchRequestDelayIncr = 0; - console.log("received match %s in %sms", matchId, end - start); + console.log('received match %s in %sms', matchId, end - start); clearTimeout(timeout); return cb(err, matchData); }); @@ -230,8 +230,8 @@ function init() { client.steamUser = new Steam.SteamUser(client); // client.steamFriends = new Steam.SteamFriends(client); client.Dota2 = new Dota2.Dota2Client(client, false); - client.Dota2.on("ready", () => { - console.log("acct %s ready", i); + client.Dota2.on('ready', () => { + console.log('acct %s ready', i); // As of 2023-06-04 seeing some double ready which crashes the process try { cb(); @@ -239,15 +239,15 @@ function init() { console.warn(e); } }); - client.on("connected", () => { + client.on('connected', () => { const logOnDetails = chooseLoginInfo(); console.log( - "[STEAM] Trying to log on with %s", + '[STEAM] Trying to log on with %s', JSON.stringify(logOnDetails) ); client.steamUser.logOn(logOnDetails); }); - client.on("logOnResponse", (logOnResp) => { + client.on('logOnResponse', (logOnResp) => { /* if (advancedAuth) { delete client.logOnDetails.two_factor_code; @@ -284,7 +284,7 @@ function init() { } if (client && client.steamID) { - console.log("[STEAM] Logged on %s", client.steamID); + console.log('[STEAM] Logged on %s', client.steamID); // client.steamFriends.setPersonaName(client.steamID.toString()); steamObj[client.steamID] = client; client.Dota2.launch(); @@ -340,14 +340,14 @@ function init() { } }); */ - client.on("error", (err) => { + client.on('error', (err) => { console.error(err); if ( advancedAuth && (user in advancedAuth.pendingTwoFactorAuth || user in advancedAuth.pendingSteamGuardAuth) ) { - console.log("not reconnecting %s, waiting for auth...", user); + console.log('not reconnecting %s, waiting for auth...', user); client.pendingLogOn = true; } else { // console.log('reconnecting %s', user); @@ -405,8 +405,8 @@ if (config.STEAM_ACCOUNT_DATA) { }) .toString() .split(/\r\n|\r|\n/g); - users = accountData.map((a) => a.split("\t")[0]); - passes = accountData.map((a) => a.split("\t")[1]); + users = accountData.map((a) => a.split('\t')[0]); + passes = accountData.map((a) => a.split('\t')[1]); } function chooseLoginInfo() { @@ -420,22 +420,21 @@ function chooseLoginInfo() { init(); app.use(compression()); -app.get("/healthz", (req, res, cb) => { +app.get('/healthz', (req, res, cb) => { if (!allReady) { - return cb("not ready"); - } - return res.end("ok"); - + return cb('not ready'); + } + return res.end('ok'); }); app.use((req, res, cb) => { if (config.RETRIEVER_SECRET && config.RETRIEVER_SECRET !== req.query.key) { // reject request if it doesn't have key - return cb("invalid key"); + return cb('invalid key'); } return cb(); }); if (advancedAuth) { - app.get("/auth", (req, res) => { + app.get('/auth', (req, res) => { if (req.query.account) { if (req.query.two_factor) { const client = advancedAuth.pendingTwoFactorAuth[req.query.account]; @@ -447,12 +446,12 @@ if (advancedAuth) { delete client.pendingLogOn; return res.json({ - result: "success", + result: 'success', }); } return res.status(400).json({ - error: "account not pending a two-factor authentication", + error: 'account not pending a two-factor authentication', }); } @@ -466,17 +465,17 @@ if (advancedAuth) { delete client.pendingLogOn; return res.json({ - result: "success", + result: 'success', }); } return res.status(400).json({ - error: "account not pending a SteamGuard authentication", + error: 'account not pending a SteamGuard authentication', }); } return res.status(400).json({ - error: "missing two_factor or steam_guard parameter", + error: 'missing two_factor or steam_guard parameter', }); } @@ -488,7 +487,7 @@ if (advancedAuth) { } app.use((req, res, cb) => { console.log( - "numReady: %s, matches: %s/%s, profiles: %s/%s, uptime: %s, matchRequestDelay: %s, query: %s", + 'numReady: %s, matches: %s/%s, profiles: %s/%s, uptime: %s, matchRequestDelay: %s, query: %s', Object.keys(steamObj).length, matchSuccesses, matchRequests, @@ -502,22 +501,22 @@ 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 && config.NODE_ENV !== 'development') { return selfDestruct(); } if (!allReady) { - return cb("not ready"); + return cb('not ready'); } return cb(); }); -app.get("/", (req, res, cb) => { +app.get('/', (req, res, cb) => { const keys = Object.keys(steamObj); const rKey = keys[Math.floor(Math.random() * keys.length)]; if (req.query.match_id) { // Don't allow requests coming in too fast const curRequestTime = new Date(); if (matchRequests > matchRequestLimit) { - return res.status(403).json({ error: "match request limit exceeded" }); + return res.status(403).json({ error: 'match request limit exceeded' }); } if ( lastRequestTime && @@ -525,7 +524,7 @@ app.get("/", (req, res, cb) => { matchRequestDelay + matchRequestDelayIncr ) { return res.status(429).json({ - error: "too many requests", + error: 'too many requests', }); } lastRequestTime = curRequestTime; @@ -553,5 +552,5 @@ app.use((err, req, res) => ); const server = app.listen(port, () => { const host = server.address().address; - console.log("[RETRIEVER] listening at http://%s:%s", host, port); + console.log('[RETRIEVER] listening at http://%s:%s', host, port); }); diff --git a/svc/scanner.js b/svc/scanner.js index e767f4131..83fe3d850 100755 --- a/svc/scanner.js +++ b/svc/scanner.js @@ -4,11 +4,11 @@ * 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"); +const async = require('async'); +const utility = require('../util/utility'); +const config = require('../config'); +const redis = require('../store/redis'); +const queries = require('../store/queries'); const { insertMatch } = queries; const { getData, generateJob } = utility; @@ -23,7 +23,7 @@ function scanApi(seqNum) { function processMatch(match, cb) { function finishMatch(err, cb) { if (err) { - console.error("failed to insert match from scanApi %s", match.match_id); + console.error('failed to insert match from scanApi %s', match.match_id); } return cb(err); } @@ -41,8 +41,8 @@ function scanApi(seqNum) { return insertMatch( match, { - type: "api", - origin: "scanner", + type: 'api', + origin: 'scanner', }, (err) => { if (!err) { @@ -58,7 +58,7 @@ function scanApi(seqNum) { } function processPage(matchSeqNum, cb) { - const container = generateJob("api_sequence", { + const container = generateJob('api_sequence', { start_at_match_seq_num: matchSeqNum, }); getData( @@ -71,7 +71,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"); + utility.redisCount(redis, 'skip_seq_num'); return cb(); } return cb(err); @@ -85,7 +85,7 @@ function scanApi(seqNum) { delayNextRequest = true; } console.log( - "[API] match_seq_num:%s, matches:%s", + '[API] match_seq_num:%s, matches:%s', matchSeqNum, resp.length ); @@ -100,8 +100,8 @@ function scanApi(seqNum) { console.error(err.stack || err); return scanApi(seqNum); } - console.log("next_seq_num: %s", nextSeqNum); - redis.set("match_seq_num", nextSeqNum); + console.log('next_seq_num: %s', nextSeqNum); + redis.set('match_seq_num', nextSeqNum); // Completed inserting matches on this page // If not a full page, delay the next iteration return setTimeout(() => scanApi(nextSeqNum), delayNextRequest ? 3000 : 0); @@ -110,28 +110,28 @@ function scanApi(seqNum) { processPage(seqNum, finishPageSet); } if (config.START_SEQ_NUM) { - redis.get("match_seq_num", (err, result) => { + redis.get('match_seq_num', (err, result) => { if (err || !result) { throw new Error( - "failed to get match_seq_num from redis, waiting to retry" + 'failed to get match_seq_num from redis, waiting to retry' ); } const numResult = Number(result); scanApi(numResult); }); -} else if (config.NODE_ENV !== "production") { +} else if (config.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", {}); + const container = generateJob('api_history', {}); getData(container.url, (err, data) => { if (err) { - throw new Error("failed to get sequence number from webapi"); + throw new Error('failed to get sequence number from webapi'); } scanApi(data.result.matches[0].match_seq_num); }); } else { - throw new Error("failed to initialize sequence number"); + throw new Error('failed to initialize sequence number'); } -process.on("unhandledRejection", (reason, p) => { - console.log("Unhandled Rejection at: Promise", p, "reason:", reason); +process.on('unhandledRejection', (reason, p) => { + console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); throw p; }); diff --git a/svc/scenarios.js b/svc/scenarios.js index 133db60f7..abcdba28e 100644 --- a/svc/scenarios.js +++ b/svc/scenarios.js @@ -1,10 +1,10 @@ -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"); +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'); async function processScenarios(matchID, cb) { try { @@ -22,17 +22,17 @@ async function processScenarios(matchID, cb) { async.eachSeries(rows, (row, cb) => { row = Object.assign(row, { epoch_week: currentWeek, - wins: row.wins ? "1" : "0", + wins: row.wins ? '1' : '0', }); - const values = Object.keys(row).map(() => "?"); + const values = Object.keys(row).map(() => '?'); const query = util.format( - "INSERT INTO %s (%s) VALUES (%s) ON CONFLICT (%s) DO UPDATE SET wins = %s.wins + EXCLUDED.wins, games = %s.games + 1", + '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(","), - values.join(","), + Object.keys(row).join(','), + values.join(','), Object.keys(row) - .filter((column) => column !== "wins") - .join(","), + .filter((column) => column !== 'wins') + .join(','), table, table ); @@ -49,4 +49,4 @@ async function processScenarios(matchID, cb) { } } -queue.runQueue("scenariosQueue", 1, processScenarios); +queue.runQueue('scenariosQueue', 1, processScenarios); diff --git a/svc/scenariosCleanup.js b/svc/scenariosCleanup.js index e777df9b9..6290e9c64 100644 --- a/svc/scenariosCleanup.js +++ b/svc/scenariosCleanup.js @@ -1,29 +1,29 @@ -const async = require("async"); -const db = require("../store/db"); -const config = require("../config"); -const utility = require("../util/utility"); +const async = require('async'); +const db = require('../store/db'); +const config = require('../config'); +const utility = require('../util/utility'); function clearScenariosTables(cb) { const currentWeek = utility.epochWeek(); async.parallel( [ (cb) => { - db("team_scenarios") - .whereNull("epoch_week") + db('team_scenarios') + .whereNull('epoch_week') .orWhere( - "epoch_week", - "<=", + 'epoch_week', + '<=', currentWeek - config.MAXIMUM_AGE_SCENARIOS_ROWS ) .del() .asCallback(cb); }, (cb) => { - db("scenarios") - .whereNull("epoch_week") + db('scenarios') + .whereNull('epoch_week') .orWhere( - "epoch_week", - "<=", + 'epoch_week', + '<=', currentWeek - config.MAXIMUM_AGE_SCENARIOS_ROWS ) .del() @@ -39,7 +39,7 @@ function clearScenariosTables(cb) { }, (cb) => { db.raw( - "delete from hero_search where match_id < (select max(match_id) - 150000000 from hero_search)" + 'delete from hero_search where match_id < (select max(match_id) - 150000000 from hero_search)' ).asCallback(cb); }, ], diff --git a/svc/syncSubs.js b/svc/syncSubs.js index 73472fe21..096f81ee3 100644 --- a/svc/syncSubs.js +++ b/svc/syncSubs.js @@ -1,13 +1,13 @@ /** * 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"); +const db = require('../store/db'); +const utility = require('../util/utility'); +const config = require('../config'); +const stripeLib = require('stripe'); const stripe = stripeLib(config.STRIPE_SECRET); -s +s; const { invokeInterval } = utility; async function run(cb) { @@ -15,24 +15,24 @@ async function run(cb) { const result = []; for await (const sub of stripe.subscriptions.list({ limit: 100, - status: "active", - price: "price_1LE5NqCHN72mG1oKg2Y9pqXb", + status: 'active', + price: 'price_1LE5NqCHN72mG1oKg2Y9pqXb', })) { result.push(sub); } - console.log(result.length, "subs"); - await db.raw("BEGIN TRANSACTION"); + console.log(result.length, 'subs'); + await db.raw('BEGIN TRANSACTION'); // Delete all status from subscribers - await db.raw("UPDATE subscriber SET status = NULL"); + await db.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 db.raw('UPDATE subscriber SET status = ? WHERE customer_id = ?', [ sub.status, sub.customer, ]); } - await db.raw("COMMIT"); + await db.raw('COMMIT'); } invokeInterval(run, 60 * 1000); diff --git a/svc/teams.js b/svc/teams.js index f2bcdf7f7..470445feb 100644 --- a/svc/teams.js +++ b/svc/teams.js @@ -1,13 +1,13 @@ -const async = require("async"); -const db = require("../store/db"); -const utility = require("../util/utility"); -const queries = require("../store/queries"); +const async = require('async'); +const db = require('../store/db'); +const utility = require('../util/utility'); +const queries = require('../store/queries'); const { invokeInterval } = utility; function doTeams(cb) { db.raw( - "select distinct team_id from team_match order by team_id desc" + 'select distinct team_id from team_match order by team_id desc' ).asCallback((err, result) => { if (err) { return cb(err); @@ -25,7 +25,7 @@ function doTeams(cb) { team_id: m.team_id || 2, }); */ - const container = utility.generateJob("api_team_info_by_team_id", { + const container = utility.generateJob('api_team_info_by_team_id', { start_at_team_id: m.team_id, }); return utility.getData( @@ -46,10 +46,10 @@ 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 = utility.generateJob('api_get_ugc_file_details', { ugcid: logoUgc, }); - const cdnJob = utility.generateJob("steam_cdn_team_logos", { + const cdnJob = utility.generateJob('steam_cdn_team_logos', { team_id: m.team_id, }); // Steam's CDN sometimes has better versions of team logos available @@ -61,7 +61,7 @@ function doTeams(cb) { t.logo_url = cdnJob.url; return queries.upsert( db, - "teams", + 'teams', t, { team_id: m.team_id, @@ -82,7 +82,7 @@ function doTeams(cb) { } return queries.upsert( db, - "teams", + 'teams', t, { team_id: m.team_id, diff --git a/svc/web.js b/svc/web.js index e677a4acc..2890dd301 100644 --- a/svc/web.js +++ b/svc/web.js @@ -2,30 +2,30 @@ * 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 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"); +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'); const stripe = stripeLib(config.STRIPE_SECRET); const { redisCount } = utility; const app = express(); -const apiKey = config.STEAM_API_KEY.split(",")[0]; +const apiKey = config.STEAM_API_KEY.split(',')[0]; const host = config.ROOT_URL; const sessOptions = { @@ -35,18 +35,18 @@ const sessOptions = { }; const whitelistedPaths = [ - "/api", // Docs - "/api/metadata", // Login status - "/login", - "/logout", - "/return", - "/api/admin/apiMetrics", // Admin metrics - "/keys", // API Key management + '/api', // Docs + '/api/metadata', // Login status + '/login', + '/logout', + '/return', + '/api/admin/apiMetrics', // Admin metrics + '/keys', // API Key management ]; const pathCosts = { - "/api/request": 30, - "/api/explorer": 5, + '/api/request': 30, + '/api/explorer': 5, }; // PASSPORT config @@ -61,7 +61,7 @@ passport.deserializeUser((accountId, done) => { passport.use( new SteamStrategy( { - providerURL: "https://steamcommunity.com/openid", + providerURL: 'https://steamcommunity.com/openid', returnURL: `${host}/return`, realm: host, apiKey, @@ -81,20 +81,20 @@ passport.use( // Compression middleware app.use(compression()); // Dota 2 images middleware (proxy to Dota 2 CDN to serve over https) -app.use("/apps", (req, res) => { +app.use('/apps', (req, res) => { request(`http://cdn.dota2.com/${req.originalUrl}`).pipe(res); }); // Proxy to serve team logos over https -app.use("/ugc", (req, res) => { +app.use('/ugc', (req, res) => { request(`http://cloud-3.steamusercontent.com/${req.originalUrl}`) - .on("response", (resp) => { - resp.headers["content-type"] = "image/png"; + .on('response', (resp) => { + resp.headers['content-type'] = 'image/png'; }) .pipe(res); }); // Health check -app.route("/healthz").get((req, res) => { - res.send("ok"); +app.route('/healthz').get((req, res) => { + res.send('ok'); }); // Session/Passport middleware app.use(session(sessOptions)); @@ -104,7 +104,7 @@ app.use(passport.session()); // app.use(requestIp.mw()); // Dummy User ID for testing -if (config.NODE_ENV === "test") { +if (config.NODE_ENV === 'test') { app.use((req, res, cb) => { if (req.query.loggedin) { req.user = { @@ -115,9 +115,9 @@ if (config.NODE_ENV === "test") { cb(); }); - app.route("/gen429").get((req, res) => res.status(429).end()); + app.route('/gen429').get((req, res) => res.status(429).end()); - app.route("/gen500").get((req, res) => res.status(500).end()); + app.route('/gen500').get((req, res) => res.status(500).end()); } // Rate limiter and API key middleware @@ -125,10 +125,10 @@ app.use((req, res, cb) => { // console.log('[REQ]', req.originalUrl); const apiKey = (req.headers.authorization && - req.headers.authorization.replace("Bearer ", "")) || + req.headers.authorization.replace('Bearer ', '')) || req.query.api_key; if (config.ENABLE_API_LIMIT && apiKey) { - redis.sismember("api_keys", apiKey, (err, resp) => { + redis.sismember('api_keys', apiKey, (err, resp) => { if (err) { cb(err); } else { @@ -141,16 +141,16 @@ app.use((req, res, cb) => { cb(); } }); -app.set("trust proxy", true); +app.set('trust proxy', true); app.use((req, res, cb) => { - const {ip} = req; + const { ip } = req; res.locals.ip = ip; - let rateLimit = ""; + let rateLimit = ''; if (res.locals.isAPIRequest) { const requestAPIKey = (req.headers.authorization && - req.headers.authorization.replace("Bearer ", "")) || + req.headers.authorization.replace('Bearer ', '')) || req.query.api_key; res.locals.usageIdentifier = requestAPIKey; rateLimit = config.API_KEY_PER_MIN_LIMIT; @@ -162,11 +162,11 @@ app.use((req, res, cb) => { } const multi = redis .multi() - .hincrby("rate_limit", res.locals.usageIdentifier, pathCosts[req.path] || 1) - .expireat("rate_limit", utility.getStartOfBlockMinutes(1, 1)); + .hincrby('rate_limit', res.locals.usageIdentifier, pathCosts[req.path] || 1) + .expireat('rate_limit', utility.getStartOfBlockMinutes(1, 1)); if (!res.locals.isAPIRequest) { - multi.zscore("user_usage_count", res.locals.usageIdentifier); // not API request so check previous usage. + multi.zscore('user_usage_count', res.locals.usageIdentifier); // not API request so check previous usage. } multi.exec((err, resp) => { @@ -176,21 +176,21 @@ app.use((req, res, cb) => { } res.set({ - "X-Rate-Limit-Remaining-Minute": rateLimit - resp[0], - "X-IP-Address": ip, + 'X-Rate-Limit-Remaining-Minute': rateLimit - resp[0], + 'X-IP-Address': ip, }); if (!res.locals.isAPIRequest) { res.set( - "X-Rate-Limit-Remaining-Month", + 'X-Rate-Limit-Remaining-Month', config.API_FREE_LIMIT - Number(resp[2]) ); } - if (config.NODE_ENV === "development" || config.NODE_ENV === "test") { - console.log("rate limit increment", resp); + if (config.NODE_ENV === 'development' || config.NODE_ENV === 'test') { + console.log('rate limit increment', resp); } - if (resp[0] > rateLimit && config.NODE_ENV !== "test") { + if (resp[0] > rateLimit && config.NODE_ENV !== 'test') { return res.status(429).json({ - error: "rate limit exceeded", + error: 'rate limit exceeded', }); } if ( @@ -200,7 +200,7 @@ app.use((req, res, cb) => { Number(resp[2]) >= config.API_FREE_LIMIT ) { return res.status(429).json({ - error: "monthly api limit exceeded", + error: 'monthly api limit exceeded', }); } @@ -210,11 +210,11 @@ app.use((req, res, cb) => { // Telemetry middleware app.use((req, res, cb) => { const timeStart = new Date(); - res.once("finish", () => { + res.once('finish', () => { const timeEnd = new Date(); const elapsed = timeEnd - timeStart; - if (elapsed > 2000 || config.NODE_ENV === "development") { - console.log("[SLOWLOG] %s, %s", req.originalUrl, elapsed); + if (elapsed > 2000 || config.NODE_ENV === 'development') { + console.log('[SLOWLOG] %s, %s', req.originalUrl, elapsed); } // When called from a middleware, the mount point is not included in req.path. See Express docs. @@ -222,59 +222,59 @@ app.use((req, res, cb) => { res.statusCode !== 500 && res.statusCode !== 429 && !whitelistedPaths.includes( - req.baseUrl + (req.path === "/" ? "" : req.path) + req.baseUrl + (req.path === '/' ? '' : req.path) ) && elapsed < 10000 ) { const multi = redis.multi(); if (res.locals.isAPIRequest) { multi - .hincrby("usage_count", res.locals.usageIdentifier, 1) - .expireat("usage_count", utility.getEndOfMonth()); + .hincrby('usage_count', res.locals.usageIdentifier, 1) + .expireat('usage_count', utility.getEndOfMonth()); } else { multi - .zincrby("user_usage_count", 1, res.locals.usageIdentifier) - .expireat("user_usage_count", utility.getEndOfMonth()); + .zincrby('user_usage_count', 1, res.locals.usageIdentifier) + .expireat('user_usage_count', utility.getEndOfMonth()); } multi.exec((err, res) => { - if (config.NODE_ENV === "development" || config.NODE_ENV === "test") { - console.log("usage count increment", err, res); + if (config.NODE_ENV === 'development' || config.NODE_ENV === 'test') { + console.log('usage count increment', err, res); } }); } - if (req.originalUrl.indexOf("/api") === 0) { - redisCount(redis, "api_hits"); - if (req.headers.origin === "https://www.opendota.com") { - redisCount(redis, "api_hits_ui"); + if (req.originalUrl.indexOf('/api') === 0) { + redisCount(redis, 'api_hits'); + if (req.headers.origin === 'https://www.opendota.com') { + redisCount(redis, 'api_hits_ui'); } - redis.zincrby("api_paths", 1, req.path.split("/")[1] || ""); + redis.zincrby('api_paths', 1, req.path.split('/')[1] || ''); redis.expireat( - "api_paths", - moment().startOf("hour").add(1, "hour").format("X") + 'api_paths', + moment().startOf('hour').add(1, 'hour').format('X') ); } if (req.user && req.user.account_id) { - redis.zadd("visitors", moment().format("X"), req.user.account_id); + redis.zadd('visitors', moment().format('X'), req.user.account_id); } - redis.lpush("load_times", elapsed); - redis.ltrim("load_times", 0, 9999); + redis.lpush('load_times', elapsed); + redis.ltrim('load_times', 0, 9999); }); cb(); }); app.use((req, res, next) => { // Reject request if not GET and Origin header is present and not an approved domain (prevent CSRF) if ( - req.method !== "GET" && - req.header("Origin") && - req.header("Origin") !== config.UI_HOST + req.method !== 'GET' && + req.header('Origin') && + req.header('Origin') !== config.UI_HOST ) { // Make an exception for replay parse request - if (req.method === "POST" && req.path.startsWith("/api/request/")) { + if (req.method === 'POST' && req.path.startsWith('/api/request/')) { return next(); } - return res.status(403).json({ error: "Invalid Origin header" }); + return res.status(403).json({ error: 'Invalid Origin header' }); } return next(); }); @@ -286,36 +286,36 @@ app.use( }) ); app.use(bodyParser.json()); -app.route("/login").get( - passport.authenticate("steam", { - failureRedirect: "/api", +app.route('/login').get( + passport.authenticate('steam', { + failureRedirect: '/api', }) ); -app.route("/return").get( - passport.authenticate("steam", { - failureRedirect: "/api", +app.route('/return').get( + passport.authenticate('steam', { + failureRedirect: '/api', }), (req, res) => { if (config.UI_HOST) { return res.redirect(`${config.UI_HOST}/players/${req.user.account_id}`); } - return res.redirect("/api"); + return res.redirect('/api'); } ); -app.route("/logout").get((req, res) => { +app.route('/logout').get((req, res) => { req.logout(); req.session = null; if (config.UI_HOST) { return res.redirect(config.UI_HOST); } - return res.redirect("/api"); + return res.redirect('/api'); }); -app.route("/subscribeSuccess").get(async (req, res) => { +app.route('/subscribeSuccess').get(async (req, res) => { if (!req.query.session_id) { - return res.status(400).json({ error: "no session ID" }); + return res.status(400).json({ error: 'no session ID' }); } if (!req.user?.account_id) { - return res.status(400).json({ error: "no account ID" }); + return res.status(400).json({ error: 'no account ID' }); } // look up the checkout session id: https://stripe.com/docs/payments/checkout/custom-success-page const session = await stripe.checkout.sessions.retrieve(req.query.session_id); @@ -323,15 +323,15 @@ app.route("/subscribeSuccess").get(async (req, res) => { const accountId = req.user.account_id; // associate the customer id with the steam account ID (req.user.account_id) await db.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"] + '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(`${config.UI_HOST}/subscribe`); }); -app.route("/manageSub").post(async (req, res) => { +app.route('/manageSub').post(async (req, res) => { if (!req.user?.account_id) { - return res.status(400).json({ error: "no account ID" }); + return res.status(400).json({ error: 'no account ID' }); } const result = await db.raw( "SELECT customer_id FROM subscriber where account_id = ? AND status = 'active'", @@ -340,7 +340,7 @@ app.route("/manageSub").post(async (req, res) => { ); const customer = result?.rows?.[0]; if (!customer) { - return res.status(400).json({ error: "customer not found" }); + return res.status(400).json({ error: 'customer not found' }); } const session = await stripe.billingPortal.sessions.create({ customer: customer.customer_id, @@ -348,53 +348,53 @@ app.route("/manageSub").post(async (req, res) => { }); return res.json(session); }); -app.use("/api", api); +app.use('/api', api); // CORS Preflight for API keys // NB: make sure UI_HOST is set e.g. http://localhost:3000 otherwise CSRF check above will stop preflight from working -app.options("/keys", cors()); -app.use("/keys", keys); +app.options('/keys', cors()); +app.use('/keys', keys); // 404 route app.use((req, res) => res.status(404).json({ - error: "Not Found", + error: 'Not Found', }) ); // 500 route app.use((err, req, res, cb) => { - console.log("[ERR]", req.originalUrl); - redisCount(redis, "500_error"); - if (config.NODE_ENV === "development" || config.NODE_ENV === "test") { + console.log('[ERR]', req.originalUrl); + redisCount(redis, '500_error'); + if (config.NODE_ENV === 'development' || config.NODE_ENV === 'test') { // default express handler return cb(err); } console.error(err, err.stacktrace); return res.status(500).json({ - error: "Internal Server Error", + error: 'Internal Server Error', }); }); const port = config.PORT || config.FRONTEND_PORT; const server = app.listen(port, () => { - console.log("[WEB] listening on %s", port); + console.log('[WEB] listening on %s', port); }); /** * Wait for connections to end, then shut down * */ function gracefulShutdown() { - console.log("Received kill signal, shutting down gracefully."); + console.log('Received kill signal, shutting down gracefully.'); server.close(() => { - console.log("Closed out remaining connections."); + console.log('Closed out remaining connections.'); process.exit(); }); // if after setTimeout(() => { console.error( - "Could not close connections in time, forcefully shutting down" + 'Could not close connections in time, forcefully shutting down' ); process.exit(); }, 10 * 1000); } // listen for TERM signal .e.g. kill -process.once("SIGTERM", gracefulShutdown); +process.once('SIGTERM', gracefulShutdown); // listen for INT signal e.g. Ctrl-C -process.once("SIGINT", gracefulShutdown); +process.once('SIGINT', gracefulShutdown); module.exports = app; diff --git a/test/test.js b/test/test.js index f7ee6f373..f0e3f53ef 100644 --- a/test/test.js +++ b/test/test.js @@ -3,27 +3,27 @@ /** * Main test script to run tests * */ -process.env.NODE_ENV = "test"; -const async = require("async"); -const nock = require("nock"); -const assert = require("assert"); -const supertest = require("supertest"); -const stripeLib = require("stripe"); -const pg = require("pg"); -const fs = require("fs"); -const cassandraDriver = require("cassandra-driver"); -const swaggerParser = require("@apidevtools/swagger-parser"); -const config = require("../config"); -const redis = require("../store/redis"); +process.env.NODE_ENV = 'test'; +const async = require('async'); +const nock = require('nock'); +const assert = require('assert'); +const supertest = require('supertest'); +const stripeLib = require('stripe'); +const pg = require('pg'); +const fs = require('fs'); +const cassandraDriver = require('cassandra-driver'); +const swaggerParser = require('@apidevtools/swagger-parser'); +const config = require('../config'); +const redis = require('../store/redis'); // const utility = require('../util/utility'); -const detailsApi = require("./data/details_api.json"); -const summariesApi = require("./data/summaries_api.json"); -const historyApi = require("./data/history_api.json"); -const heroesApi = require("./data/heroes_api.json"); -const leaguesApi = require("./data/leagues_api.json"); -const retrieverPlayer = require("./data/retriever_player.json"); -const detailsApiPro = require("./data/details_api_pro.json"); -const spec = require("../routes/spec"); +const detailsApi = require('./data/details_api.json'); +const summariesApi = require('./data/summaries_api.json'); +const historyApi = require('./data/history_api.json'); +const heroesApi = require('./data/heroes_api.json'); +const leaguesApi = require('./data/leagues_api.json'); +const retrieverPlayer = require('./data/retriever_player.json'); +const detailsApiPro = require('./data/details_api_pro.json'); +const spec = require('../routes/spec'); const initPostgresHost = `postgres://postgres:postgres@${config.INIT_POSTGRES_HOST}/postgres`; const initCassandraHost = config.INIT_CASSANDRA_HOST; @@ -35,35 +35,35 @@ let app; let queries; let buildMatch; // fake api responses -nock("http://api.steampowered.com") +nock('http://api.steampowered.com') // fake 500 error - .get("/IDOTA2Match_570/GetMatchDetails/V001/") + .get('/IDOTA2Match_570/GetMatchDetails/V001/') .query(true) .reply(500, {}) // fake match details - .get("/IDOTA2Match_570/GetMatchDetails/V001/") + .get('/IDOTA2Match_570/GetMatchDetails/V001/') .query(true) .times(10) .reply(200, detailsApi) // fake player summaries - .get("/ISteamUser/GetPlayerSummaries/v0002/") + .get('/ISteamUser/GetPlayerSummaries/v0002/') .query(true) .reply(200, summariesApi) // fake full history - .get("/IDOTA2Match_570/GetMatchHistory/V001/") + .get('/IDOTA2Match_570/GetMatchHistory/V001/') .query(true) .reply(200, historyApi) // fake heroes list - .get("/IEconDOTA2_570/GetHeroes/v0001/") + .get('/IEconDOTA2_570/GetHeroes/v0001/') .query(true) .reply(200, heroesApi) // fake leagues - .get("/IDOTA2Match_570/GetLeagueListing/v0001/") + .get('/IDOTA2Match_570/GetLeagueListing/v0001/') .query(true) .reply(200, leaguesApi); // fake mmr response nock(`http://${config.RETRIEVER_HOST}`) - .get("/?account_id=88367253") + .get('/?account_id=88367253') .reply(200, retrieverPlayer); before(function setup(done) { this.timeout(60000); @@ -80,12 +80,12 @@ before(function setup(done) { return async.series( [ function drop(cb) { - console.log("drop postgres test database"); - client.query("DROP DATABASE IF EXISTS yasp_test", cb); + console.log('drop postgres test database'); + client.query('DROP DATABASE IF EXISTS yasp_test', cb); }, function create(cb) { - console.log("create postgres test database"); - client.query("CREATE DATABASE yasp_test", cb); + console.log('create postgres test database'); + client.query('CREATE DATABASE yasp_test', cb); }, function tables(cb) { const pool2 = new pg.Pool({ @@ -95,17 +95,17 @@ before(function setup(done) { if (err) { return cb(err); } - console.log("create postgres test tables"); + console.log('create postgres test tables'); const query = fs.readFileSync( - "./sql/create_tables.sql", - "utf8" + './sql/create_tables.sql', + 'utf8' ); return client2.query(query, cb); }); }, function setup(cb) { - db = require("../store/db"); - console.log("insert postgres test data"); + db = require('../store/db'); + console.log('insert postgres test data'); // populate the DB with this leagueid so we insert a pro match db.raw( "INSERT INTO leagues(leagueid, tier) VALUES(5399, 'professional')" @@ -119,28 +119,28 @@ before(function setup(done) { function initCassandra(cb) { const client = new cassandraDriver.Client({ contactPoints: [initCassandraHost], - localDataCenter: "datacenter1", + localDataCenter: 'datacenter1', }); async.series( [ function drop(cb) { - console.log("drop cassandra test keyspace"); - client.execute("DROP KEYSPACE IF EXISTS yasp_test", cb); + console.log('drop cassandra test keyspace'); + client.execute('DROP KEYSPACE IF EXISTS yasp_test', cb); }, function create(cb) { - console.log("create cassandra test keyspace"); + console.log('create cassandra test keyspace'); client.execute( "CREATE KEYSPACE yasp_test WITH REPLICATION = { 'class': 'NetworkTopologyStrategy', 'datacenter1': 1 };", cb ); }, function tables(cb) { - cassandra = require("../store/cassandra"); - console.log("create cassandra test tables"); + cassandra = require('../store/cassandra'); + console.log('create cassandra test tables'); async.eachSeries( fs - .readFileSync("./sql/create_tables.cql", "utf8") - .split(";") + .readFileSync('./sql/create_tables.cql', 'utf8') + .split(';') .filter((cql) => cql.length > 1), (cql, cb) => { cassandra.execute(cql, cb); @@ -153,17 +153,17 @@ before(function setup(done) { ); }, function initElasticsearch(cb) { - console.log("Create Elasticsearch Mapping"); + console.log('Create Elasticsearch Mapping'); const mapping = JSON.parse( - fs.readFileSync("./elasticsearch/index.json") + fs.readFileSync('./elasticsearch/index.json') ); - const { es } = require("../store/elasticsearch"); + const { es } = require('../store/elasticsearch'); async.series( [ (cb) => { es.indices.exists( { - index: "dota-test", // Check if index already exists, in which case, delete it + index: 'dota-test', // Check if index already exists, in which case, delete it }, (err, exists) => { if (err) { @@ -172,7 +172,7 @@ before(function setup(done) { } else if (exists.body) { es.indices.delete( { - index: "dota-test", + index: 'dota-test', }, (err) => { if (err) { @@ -190,7 +190,7 @@ before(function setup(done) { (cb) => { es.indices.create( { - index: "dota-test", + index: 'dota-test', }, cb ); @@ -198,7 +198,7 @@ before(function setup(done) { (cb) => { es.indices.close( { - index: "dota-test", + index: 'dota-test', }, cb ); @@ -206,7 +206,7 @@ before(function setup(done) { (cb) => { es.indices.putSettings( { - index: "dota-test", + index: 'dota-test', body: mapping.settings, }, cb @@ -215,8 +215,8 @@ before(function setup(done) { (cb) => { es.indices.putMapping( { - index: "dota-test", - type: "player", + index: 'dota-test', + type: 'player', body: mapping.mappings.player, }, cb @@ -225,7 +225,7 @@ before(function setup(done) { (cb) => { es.indices.open( { - index: "dota-test", + index: 'dota-test', }, cb ); @@ -235,31 +235,31 @@ before(function setup(done) { ); }, function wipeRedis(cb) { - console.log("wiping redis"); + console.log('wiping redis'); redis.flushdb((err, success) => { console.log(err, success); cb(err); }); }, function startServices(cb) { - console.log("starting services"); - app = require("../svc/web"); - queries = require("../store/queries"); - buildMatch = require("../store/buildMatch"); + console.log('starting services'); + app = require('../svc/web'); + queries = require('../store/queries'); + buildMatch = require('../store/buildMatch'); - require("../svc/parser"); + require('../svc/parser'); cb(); }, function loadMatches(cb) { - console.log("loading matches"); + console.log('loading matches'); async.mapSeries( [detailsApi.result, detailsApiPro.result, detailsApiPro.result], (m, cb) => { queries.insertMatch( m, { - type: "api", - origin: "scanner", + type: 'api', + origin: 'scanner', skipParse: true, }, cb @@ -269,7 +269,7 @@ before(function setup(done) { ); }, function loadPlayers(cb) { - console.log("loading players"); + console.log('loading players'); async.mapSeries( summariesApi.response.players, (p, cb) => { @@ -282,9 +282,9 @@ before(function setup(done) { done ); }); -describe("swagger schema", function testSwaggerSchema() { +describe('swagger schema', function testSwaggerSchema() { this.timeout(2000); - it("should be valid", (cb) => { + it('should be valid', (cb) => { const validOpts = { validate: { schema: true, @@ -306,15 +306,15 @@ describe("swagger schema", function testSwaggerSchema() { ); }); }); -describe("replay parse", function testReplayParse() { +describe('replay parse', function testReplayParse() { this.timeout(120000); const tests = { - "1781962623_1.dem": detailsApi.result, + '1781962623_1.dem': detailsApi.result, }; Object.keys(tests).forEach((key) => { const match = tests[key]; nock(`http://${config.RETRIEVER_HOST}`) - .get("/") + .get('/') .query(true) .reply(200, { match: { @@ -331,7 +331,7 @@ describe("replay parse", function testReplayParse() { match, { cassandra, - type: "api", + type: 'api', forceParse: true, attempts: 1, }, @@ -355,10 +355,10 @@ describe("replay parse", function testReplayParse() { }); }); }); -describe("teamRanking", () => { - it("should have team rankings", (cb) => { - db.select(["team_id", "rating", "wins", "losses"]) - .from("team_rating") +describe('teamRanking', () => { + it('should have team rankings', (cb) => { + db.select(['team_id', 'rating', 'wins', 'losses']) + .from('team_rating') .asCallback((err, rows) => { // We inserted the pro match twice so expect to update the ratings twice const loser = rows.find((row) => row.team_id === 4251435); @@ -372,11 +372,11 @@ describe("teamRanking", () => { }); }); // TODO test against an unparsed match to catch exceptions caused by code expecting parsed data -describe("api", () => { - it("should get API spec", function testAPISpec(cb) { +describe('api', () => { + it('should get API spec', function testAPISpec(cb) { this.timeout(5000); supertest(app) - .get("/api") + .get('/api') .end((err, res) => { if (err) { return cb(err); @@ -391,14 +391,14 @@ describe("api", () => { .replace(/{team_id}/, 15) .replace(/{hero_id}/, 1) .replace(/{league_id}/, 1) - .replace(/{field}/, "kills") - .replace(/{resource}/, "heroes"); + .replace(/{field}/, 'kills') + .replace(/{resource}/, 'heroes'); async.eachSeries( Object.keys(spec.paths[path]), (verb, cb) => { if ( - path.indexOf("/explorer") === 0 || - path.indexOf("/request") === 0 + path.indexOf('/explorer') === 0 || + path.indexOf('/request') === 0 ) { return cb(err); } @@ -408,9 +408,9 @@ describe("api", () => { if (err || res.statusCode !== 200) { console.error(verb, replacedPath, res.body); } - if (replacedPath.startsWith("/admin")) { + if (replacedPath.startsWith('/admin')) { assert.equal(res.statusCode, 403); - } else if (replacedPath.startsWith("/subscribeSuccess")) { + } else if (replacedPath.startsWith('/subscribeSuccess')) { assert.equal(res.statusCode, 400); } else { assert.equal(res.statusCode, 200); @@ -426,9 +426,9 @@ describe("api", () => { }); }); }); -describe("api management", () => { +describe('api management', () => { beforeEach(function getApiRecord(done) { - db.from("api_keys") + db.from('api_keys') .where({ account_id: 1, }) @@ -442,9 +442,9 @@ describe("api management", () => { .catch((err) => done(err)); }); - it("should get 403 when not logged in.", (done) => { + it('should get 403 when not logged in.', (done) => { supertest(app) - .get("/keys") + .get('/keys') .then((res) => { assert.equal(res.statusCode, 403); return done(); @@ -452,9 +452,9 @@ describe("api management", () => { .catch((err) => done(err)); }); - it("should not get fields for GET", (done) => { + it('should not get fields for GET', (done) => { supertest(app) - .get("/keys?loggedin=1") + .get('/keys?loggedin=1') .then((res) => { assert.equal(res.statusCode, 200); assert.deepStrictEqual(res.body, {}); @@ -463,32 +463,32 @@ describe("api management", () => { .catch((err) => done(err)); }); - it("should create api key", function testCreatingApiKey(done) { + it('should create api key', function testCreatingApiKey(done) { this.timeout(5000); supertest(app) - .post("/keys?loggedin=1") + .post('/keys?loggedin=1') .send({ token: { - id: "tok_visa", - email: "test@test.com", + id: 'tok_visa', + email: 'test@test.com', }, }) .then((res) => { assert.equal(res.statusCode, 200); supertest(app) - .get("/keys?loggedin=1") + .get('/keys?loggedin=1') .end((err, res) => { if (err) { done(err); } else { assert.equal(res.statusCode, 200); - assert.equal(res.body.customer.credit_brand, "Visa"); + assert.equal(res.body.customer.credit_brand, 'Visa'); assert.notEqual(res.body.customer.api_key, null); assert.equal(Array.isArray(res.body.openInvoices), true); assert.equal(Array.isArray(res.body.usage), true); redis.sismember( - "api_keys", + 'api_keys', res.body.customer.api_key, (err, resp) => { if (err) { @@ -504,39 +504,39 @@ describe("api management", () => { .catch((err) => done(err)); }); - it("post should not change key", function testPostDoesNotChangeKey(done) { + it('post should not change key', function testPostDoesNotChangeKey(done) { this.timeout(5000); supertest(app) - .get("/keys?loggedin=1") + .get('/keys?loggedin=1') .then((res) => { assert.equal(res.statusCode, 200); - assert.equal(res.body.customer.credit_brand, "Visa"); + assert.equal(res.body.customer.credit_brand, 'Visa'); const previousCredit = res.body.customer.credit_brand; supertest(app) - .post("/keys?loggedin=1") + .post('/keys?loggedin=1') .send({ token: { - id: "tok_discover", - email: "test@test.com", + id: 'tok_discover', + email: 'test@test.com', }, }) .then((res) => { assert.equal(res.statusCode, 200); - db.from("api_keys") + db.from('api_keys') .where({ account_id: 1, }) .then((res2) => { if (res2.length === 0) { - throw Error("No API record found"); + throw Error('No API record found'); } assert.equal(res2[0].customer_id, this.previousCustomer); assert.equal(res2[0].subscription_id, this.previousSub); supertest(app) - .get("/keys?loggedin=1") + .get('/keys?loggedin=1') .end((err, res) => { if (err) { return done(err); @@ -559,32 +559,32 @@ describe("api management", () => { .catch((err) => done(err)); }); - it("put should update payment but not change customer/sub", function testPutOnlyChangesBilling(done) { + it('put should update payment but not change customer/sub', function testPutOnlyChangesBilling(done) { this.timeout(5000); supertest(app) - .put("/keys?loggedin=1") + .put('/keys?loggedin=1') .send({ token: { - id: "tok_mastercard", - email: "test@test.com", + id: 'tok_mastercard', + email: 'test@test.com', }, }) .then((res) => { assert.equal(res.statusCode, 200); supertest(app) - .get("/keys?loggedin=1") + .get('/keys?loggedin=1') .then((res) => { assert.equal(res.statusCode, 200); - assert.equal(res.body.customer.credit_brand, "MasterCard"); + assert.equal(res.body.customer.credit_brand, 'MasterCard'); assert.equal(res.body.customer.api_key, this.previousKey); - db.from("api_keys") + db.from('api_keys') .where({ account_id: 1, }) .then((res2) => { if (res.length === 0) { - throw Error("No API record found"); + throw Error('No API record found'); } assert.equal(res2[0].customer_id, this.previousCustomer); assert.equal(res2[0].subscription_id, this.previousSub); @@ -596,33 +596,33 @@ describe("api management", () => { }) .catch((err) => done(err)); }); - it("delete should set is_deleted and remove from redis but not change other db fields", function testDeleteOnlyModifiesKey(done) { + it('delete should set is_deleted and remove from redis but not change other db fields', function testDeleteOnlyModifiesKey(done) { this.timeout(5000); assert.notEqual(this.previousKey, null); assert.equal(this.previousIsCanceled, undefined); - redis.sismember("api_keys", this.previousKey, (err, resp) => { + redis.sismember('api_keys', this.previousKey, (err, resp) => { if (err) { done(err); } else { assert.equal(resp, 1); supertest(app) - .delete("/keys?loggedin=1") + .delete('/keys?loggedin=1') .then((res) => { assert.equal(res.statusCode, 200); - db.from("api_keys") + db.from('api_keys') .where({ account_id: 1, }) .then((res2) => { if (res2.length === 0) { - throw Error("No API record found"); + throw Error('No API record found'); } assert.equal(res2[0].api_key, this.previousKey); assert.equal(res2[0].customer_id, this.previousCustomer); assert.equal(res2[0].subscription_id, this.previousSub); assert.equal(res2[0].is_canceled, true); - redis.sismember("api_keys", this.previousKey, (err, resp) => { + redis.sismember('api_keys', this.previousKey, (err, resp) => { if (err) { return done(err); } @@ -637,39 +637,39 @@ describe("api management", () => { }); }); - it("should get new key with new sub but not change customer", function testGettingNewKey(done) { + it('should get new key with new sub but not change customer', function testGettingNewKey(done) { this.timeout(5000); supertest(app) - .post("/keys?loggedin=1") + .post('/keys?loggedin=1') .send({ token: { - id: "tok_discover", - email: "test@test.com", + id: 'tok_discover', + email: 'test@test.com', }, }) .then((res) => { assert.equal(res.statusCode, 200); - db.from("api_keys") + db.from('api_keys') .where({ account_id: 1, is_canceled: null, }) .then((res2) => { if (res2.length === 0) { - throw Error("No API record found"); + throw Error('No API record found'); } assert.equal(res2[0].customer_id, this.previousCustomer); assert.notEqual(res2[0].subscription_id, this.previousSub); supertest(app) - .get("/keys?loggedin=1") + .get('/keys?loggedin=1') .end((err, res) => { if (err) { return done(err); } assert.equal(res.statusCode, 200); - assert.equal(res.body.customer.credit_brand, "Discover"); + assert.equal(res.body.customer.credit_brand, 'Discover'); assert.notEqual(res.body.customer.api_key, null); assert.notEqual(res.body.customer.api_key, this.previousKey); return done(); @@ -679,18 +679,18 @@ describe("api management", () => { }) .catch((err) => done(err)); }); - it("should fail to create key if open invoice", function openInvoice(done) { + it('should fail to create key if open invoice', function openInvoice(done) { this.timeout(5000); // delete the key first supertest(app) - .delete("/keys?loggedin=1") + .delete('/keys?loggedin=1') .then(async (res) => { assert.equal(res.statusCode, 200); const stripe = stripeLib(config.STRIPE_SECRET); await stripe.invoiceItems.create({ customer: this.previousCustomer, - price: "price_1Lm1siCHN72mG1oKkk3Jh1JT", // test $123 one time + price: 'price_1Lm1siCHN72mG1oKkk3Jh1JT', // test $123 one time }); const invoice = await stripe.invoices.create({ @@ -700,19 +700,19 @@ describe("api management", () => { await stripe.invoices.finalizeInvoice(invoice.id); supertest(app) - .post("/keys?loggedin=1") + .post('/keys?loggedin=1') .send({ token: { - id: "tok_discover", - email: "test@test.com", + id: 'tok_discover', + email: 'test@test.com', }, }) .then((res) => { assert.equal(res.statusCode, 402); - assert.equal(res.body.error, "Open invoice"); + assert.equal(res.body.error, 'Open invoice'); supertest(app) - .get("/keys?loggedin=1") + .get('/keys?loggedin=1') .end((err, res) => { if (err) { return done(err); @@ -722,7 +722,7 @@ describe("api management", () => { assert.equal(res.body.customer, null); assert.equal(res.body.openInvoices[0].id, invoice.id); assert.equal(res.body.openInvoices[0].amountDue, 12300); - db.from("api_keys") + db.from('api_keys') .where({ account_id: 1, is_canceled: null, @@ -737,15 +737,15 @@ describe("api management", () => { .catch((err) => done(err)); }); }); -describe("api limits", () => { +describe('api limits', () => { before((done) => { config.ENABLE_API_LIMIT = true; config.API_FREE_LIMIT = 10; redis .multi() - .del("user_usage_count") - .del("usage_count") - .sadd("api_keys", "KEY") + .del('user_usage_count') + .del('usage_count') + .sadd('api_keys', 'KEY') .exec((err) => { if (err) { return done(err); @@ -786,7 +786,7 @@ describe("api limits", () => { (i, cb) => { setTimeout(() => { supertest(app) - .get("/api/matches/1781962623") + .get('/api/matches/1781962623') .end((err, res) => { if (err) { return cb(err); @@ -802,7 +802,7 @@ describe("api limits", () => { ); } - it("should be able to make API calls without key with whitelisted routes unaffected. One call should fail as rate limit is hit. Last ones should succeed as they are whitelisted", function testNoApiLimit(done) { + it('should be able to make API calls without key with whitelisted routes unaffected. One call should fail as rate limit is hit. Last ones should succeed as they are whitelisted', function testNoApiLimit(done) { this.timeout(25000); testWhiteListedRoutes((err) => { if (err) { @@ -813,29 +813,29 @@ describe("api limits", () => { done(err); } else { supertest(app) - .get("/api/matches/1781962623") + .get('/api/matches/1781962623') .end((err, res) => { if (err) { done(err); } assert.equal(res.statusCode, 429); - assert.equal(res.body.error, "monthly api limit exceeded"); + assert.equal(res.body.error, 'monthly api limit exceeded'); - testWhiteListedRoutes(done, ""); + testWhiteListedRoutes(done, ''); }); } }); } - }, ""); + }, ''); }); - it("should be able to make more than 10 calls when using API KEY", function testAPIKeyLimitsAndCounting(done) { + it('should be able to make more than 10 calls when using API KEY', function testAPIKeyLimitsAndCounting(done) { this.timeout(25000); async.timesSeries( 25, (i, cb) => { supertest(app) - .get("/api/matches/1781962623?api_key=KEY") + .get('/api/matches/1781962623?api_key=KEY') .end((err, res) => { if (err) { return cb(err); @@ -853,7 +853,7 @@ describe("api limits", () => { } else { // Try a 429. Should not increment usage. supertest(app) - .get("/gen429") + .get('/gen429') .end((err, res) => { if (err) { done(err); @@ -862,13 +862,13 @@ describe("api limits", () => { // Try a 500. Should not increment usage. supertest(app) - .get("/gen500") + .get('/gen500') .end((err, res) => { if (err) { done(err); } assert.equal(res.statusCode, 500); - redis.hgetall("usage_count", (err, res) => { + redis.hgetall('usage_count', (err, res) => { if (err) { done(err); } else { @@ -881,7 +881,7 @@ describe("api limits", () => { }); }); } - }, "?api_key=KEY"); + }, '?api_key=KEY'); } ); }); diff --git a/util/compute.js b/util/compute.js index f0778b63c..3eba72b27 100644 --- a/util/compute.js +++ b/util/compute.js @@ -1,5 +1,5 @@ -const constants = require("dotaconstants"); -const utility = require("./utility"); +const constants = require('dotaconstants'); +const utility = require('./utility'); const { max, min, isRadiant } = utility; const { ancients } = constants; @@ -23,7 +23,7 @@ function countWords(playerMatch, playerFilter) { chatWords.push(message.key); } }); - chatWords = chatWords.join(" "); + chatWords = chatWords.join(' '); const tokens = utility.tokenize(chatWords); // count how frequently each word occurs const counts = {}; @@ -103,38 +103,38 @@ function computeMatchData(pm) { pm.ancient_kills = 0; Object.keys(pm.killed).forEach((key) => { if ( - key.indexOf("creep_goodguys") !== -1 || - key.indexOf("creep_badguys") !== -1 + key.indexOf('creep_goodguys') !== -1 || + key.indexOf('creep_badguys') !== -1 ) { pm.lane_kills += pm.killed[key]; } - if (key.indexOf("observer") !== -1) { + if (key.indexOf('observer') !== -1) { pm.observer_kills += pm.killed[key]; } - if (key.indexOf("sentry") !== -1) { + if (key.indexOf('sentry') !== -1) { pm.sentry_kills += pm.killed[key]; } - if (key.indexOf("npc_dota_hero") === 0) { + if (key.indexOf('npc_dota_hero') === 0) { if (!selfHero || selfHero.name !== key) { pm.hero_kills += pm.killed[key]; } } - if (key.indexOf("npc_dota_neutral") === 0) { + if (key.indexOf('npc_dota_neutral') === 0) { pm.neutral_kills += pm.killed[key]; } if (key in ancients) { pm.ancient_kills += pm.killed[key]; } - if (key.indexOf("_tower") !== -1) { + if (key.indexOf('_tower') !== -1) { pm.tower_kills += pm.killed[key]; } - if (key.indexOf("courier") !== -1) { + if (key.indexOf('courier') !== -1) { pm.courier_kills += pm.killed[key]; } - if (key.indexOf("roshan") !== -1) { + if (key.indexOf('roshan') !== -1) { pm.roshan_kills += pm.killed[key]; } - if (key.indexOf("necronomicon") !== -1) { + if (key.indexOf('necronomicon') !== -1) { pm.necronomicon_kills += pm.killed[key]; } }); @@ -171,8 +171,8 @@ function computeMatchData(pm) { pm.purchase_log = pm.purchase_log.filter( (purchase) => !( - purchase.key.indexOf("recipe_") === 0 || - purchase.key === "ward_dispenser" + purchase.key.indexOf('recipe_') === 0 || + purchase.key === 'ward_dispenser' ) ); pm.purchase_time = {}; @@ -234,7 +234,7 @@ function computeMatchData(pm) { pm.stomp = pm.radiant_win === isRadiant(pm) ? stompVal : undefined; } if (pm.pings) { - pm.pings = pm.pings["0"]; + pm.pings = pm.pings['0']; } if (pm.life_state) { pm.life_state_dead = (pm.life_state[1] || 0) + (pm.life_state[2] || 0); diff --git a/util/filter.js b/util/filter.js index c7fe1efc1..1c63d4875 100644 --- a/util/filter.js +++ b/util/filter.js @@ -1,4 +1,4 @@ -const utility = require("./utility"); +const utility = require('./utility'); const { isRadiant } = utility; diff --git a/util/filterDeps.js b/util/filterDeps.js index 795fc78bc..763e7fb19 100644 --- a/util/filterDeps.js +++ b/util/filterDeps.js @@ -2,21 +2,21 @@ * Object listing dependent columns for each filter * */ module.exports = { - win: ["player_slot", "radiant_win"], - patch: ["patch"], - game_mode: ["game_mode"], - lobby_type: ["lobby_type"], - region: ["region"], - date: ["start_time"], - lane_role: ["lane_role"], - hero_id: ["hero_id"], - is_radiant: ["player_slot"], - party_size: ["party_size"], - included_account_id: ["heroes"], - excluded_account_id: ["heroes"], - with_account_id: ["player_slot", "heroes"], - against_account_id: ["player_slot", "heroes"], - with_hero_id: ["player_slot", "heroes"], - against_hero_id: ["player_slot", "heroes"], - significant: ["duration", "game_mode", "lobby_type", "radiant_win"], + win: ['player_slot', 'radiant_win'], + patch: ['patch'], + game_mode: ['game_mode'], + lobby_type: ['lobby_type'], + region: ['region'], + date: ['start_time'], + lane_role: ['lane_role'], + hero_id: ['hero_id'], + is_radiant: ['player_slot'], + party_size: ['party_size'], + included_account_id: ['heroes'], + excluded_account_id: ['heroes'], + with_account_id: ['player_slot', 'heroes'], + against_account_id: ['player_slot', 'heroes'], + with_hero_id: ['player_slot', 'heroes'], + against_hero_id: ['player_slot', 'heroes'], + significant: ['duration', 'game_mode', 'lobby_type', 'radiant_win'], }; diff --git a/util/getGcData.js b/util/getGcData.js index b3546b734..c1c9d2db5 100644 --- a/util/getGcData.js +++ b/util/getGcData.js @@ -2,13 +2,13 @@ * 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"); +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'); const secret = config.RETRIEVER_SECRET; const { getData, redisCount } = utility; @@ -33,14 +33,14 @@ async function getGcDataFromRetriever(match, cb) { // non-retryable error // redis.lpush('nonRetryable', JSON.stringify({ matchId: match.match_id, body })); // redis.ltrim('nonRetryable', 0, 10000); - return cb(new Error("invalid body or error")); + return cb(new Error('invalid body or error')); } // Count retriever calls - redisCount(redis, "retriever"); - redis.zincrby("retrieverCounts", 1, "retriever"); + redisCount(redis, 'retriever'); + redis.zincrby('retrieverCounts', 1, 'retriever'); redis.expireat( - "retrieverCounts", - moment().startOf("hour").add(1, "hour").format("X") + 'retrieverCounts', + moment().startOf('hour').add(1, 'hour').format('X') ); // TODO add discovered account_ids to database and fetch account data/rank medal try { @@ -67,11 +67,11 @@ async function getGcDataFromRetriever(match, cb) { }; // Put extra fields in matches/player_matches await promisify(insertMatch)(matchToInsert, { - type: "gcdata", + type: 'gcdata', skipParse: true, }); // Persist GC data to database - await promisify(queries.upsert)(db, "match_gcdata", gcdata, { + await promisify(queries.upsert)(db, 'match_gcdata', gcdata, { match_id: match.match_id, }); cb(null, gcdata); @@ -85,17 +85,17 @@ async function getGcDataFromRetriever(match, cb) { module.exports = 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")); + return cb(new Error('invalid match_id')); } // Check if we have it in DB already const saved = await db.raw( - "select match_id, cluster, replay_salt from match_gcdata where match_id = ?", + 'select match_id, cluster, replay_salt from match_gcdata where match_id = ?', [match.match_id] ); const gcdata = saved.rows[0]; if (gcdata) { - console.log("found cached gcdata for %s", matchId); - redisCount(redis, "cached_gcdata"); + console.log('found cached gcdata for %s', matchId); + redisCount(redis, 'cached_gcdata'); return cb(null, gcdata); } getGcDataFromRetriever(match, cb); diff --git a/util/scenariosUtil.js b/util/scenariosUtil.js index 3430eba1d..3694d2591 100644 --- a/util/scenariosUtil.js +++ b/util/scenariosUtil.js @@ -1,5 +1,5 @@ -const constants = require("dotaconstants"); -const utility = require("./utility"); +const constants = require('dotaconstants'); +const utility = require('./utility'); const { playerWon } = utility; @@ -12,14 +12,14 @@ const dotaItems = Object.keys(constants.items) const timings = [7.5, 10, 12, 15, 20, 25, 30].map((x) => x * 60); const gameDurationBucket = [15, 30, 45, 60, 90].map((x) => x * 60); -const negativeWords = ["ff", "report", "gg", "end", "noob"]; -const positiveWords = ["gl", "glhf", "hf", "good luck", "have fun"]; +const negativeWords = ['ff', 'report', 'gg', 'end', 'noob']; +const positiveWords = ['gl', 'glhf', 'hf', 'good luck', 'have fun']; const teamScenariosQueryParams = [ - "pos_chat_1min", - "neg_chat_1min", - "courier_kill", - "first_blood", + 'pos_chat_1min', + 'neg_chat_1min', + 'courier_kill', + 'first_blood', ]; function buildTeamScenario(scenario, isRadiant, match) { @@ -79,10 +79,10 @@ const scenarioChecks = { function firstBlood(match) { const condition = match.objectives && - match.objectives.find((x) => x.type === "CHAT_MESSAGE_FIRSTBLOOD"); + match.objectives.find((x) => x.type === 'CHAT_MESSAGE_FIRSTBLOOD'); if (condition) { const isRadiant = condition.player_slot < 5; - return buildTeamScenario("first_blood", isRadiant, match); + return buildTeamScenario('first_blood', isRadiant, match); } return []; }, @@ -92,11 +92,11 @@ const scenarioChecks = { const condition = match.objectives && match.objectives.find( - (x) => x.type === "CHAT_MESSAGE_COURIER_LOST" && x.time < 180 + (x) => x.type === 'CHAT_MESSAGE_COURIER_LOST' && x.time < 180 ); if (condition) { const isRadiant = condition.team === 3; - return buildTeamScenario("courier_kill", isRadiant, match); + return buildTeamScenario('courier_kill', isRadiant, match); } return []; }, @@ -116,7 +116,7 @@ const scenarioChecks = { } if ( negativeWords.some((word) => - RegExp(`\\b${word}\\b`, "i").test(c.key) + RegExp(`\\b${word}\\b`, 'i').test(c.key) ) ) { if (c.player_slot < 128) { @@ -127,7 +127,7 @@ const scenarioChecks = { } if ( positiveWords.some((word) => - RegExp(`\\b${word}\\b`, "i").test(c.key) + RegExp(`\\b${word}\\b`, 'i').test(c.key) ) ) { if (c.player_slot < 128) { @@ -138,16 +138,16 @@ const scenarioChecks = { } } if (radiantNegative) { - rows.push(buildTeamScenario("neg_chat_1min", true, match)[0]); + rows.push(buildTeamScenario('neg_chat_1min', true, match)[0]); } if (direNegative) { - rows.push(buildTeamScenario("neg_chat_1min", false, match)[0]); + rows.push(buildTeamScenario('neg_chat_1min', false, match)[0]); } if (radiantPositive) { - rows.push(buildTeamScenario("pos_chat_1min", true, match)[0]); + rows.push(buildTeamScenario('pos_chat_1min', true, match)[0]); } if (direPositive) { - rows.push(buildTeamScenario("pos_chat_1min", false, match)[0]); + rows.push(buildTeamScenario('pos_chat_1min', false, match)[0]); } } return rows; @@ -157,11 +157,11 @@ const scenarioChecks = { // list of match object properties that are required for scenario checks. const matchProperties = [ - "players", - "objectives", - "duration", - "chat", - "radiant_win", + 'players', + 'objectives', + 'duration', + 'chat', + 'radiant_win', ]; const metadata = { diff --git a/util/utility.js b/util/utility.js index 1307ebea8..bde7694b3 100644 --- a/util/utility.js +++ b/util/utility.js @@ -2,16 +2,16 @@ * 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"); +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'); /** * Tokenizes an input string. @@ -22,10 +22,10 @@ const contributors = require("../CONTRIBUTORS"); */ function tokenize(input) { return input - .replace(/[^a-zа-я- ]+/gi, "") - .replace("/ {2,}/", " ") + .replace(/[^a-zа-я- ]+/gi, '') + .replace('/ {2,}/', ' ') .toLowerCase() - .split(" "); + .split(' '); } /* @@ -34,7 +34,7 @@ function tokenize(input) { * Takes and returns a string */ function convert64to32(id) { - return Long.fromString(id).subtract("76561197960265728").toString(); + return Long.fromString(id).subtract('76561197960265728').toString(); } /* @@ -43,41 +43,41 @@ function convert64to32(id) { * Takes and returns a string */ function convert32to64(id) { - return Long.fromString(id).add("76561197960265728").toString(); + return Long.fromString(id).add('76561197960265728').toString(); } /** * Creates a job object for enqueueing that contains details such as the Steam API endpoint to hit * */ function generateJob(type, payload) { - const apiUrl = "http://api.steampowered.com"; + const apiUrl = 'http://api.steampowered.com'; let apiKey; const opts = { api_details() { return { url: `${apiUrl}/IDOTA2Match_570/GetMatchDetails/V001/?key=${apiKey}&match_id=${payload.match_id}`, title: [type, payload.match_id].join(), - type: "api", + type: 'api', payload, }; }, api_history() { return { url: `${apiUrl}/IDOTA2Match_570/GetMatchHistory/V001/?key=${apiKey}${ - payload.account_id ? `&account_id=${payload.account_id}` : "" + payload.account_id ? `&account_id=${payload.account_id}` : '' }${ payload.matches_requested ? `&matches_requested=${payload.matches_requested}` - : "" - }${payload.hero_id ? `&hero_id=${payload.hero_id}` : ""}${ - payload.leagueid ? `&league_id=${payload.leagueid}` : "" + : '' + }${payload.hero_id ? `&hero_id=${payload.hero_id}` : ''}${ + payload.leagueid ? `&league_id=${payload.leagueid}` : '' }${ payload.start_at_match_id ? `&start_at_match_id=${payload.start_at_match_id}` - : "" + : '' }`, title: [type, payload.account_id].join(), - type: "api", + type: 'api', payload, }; }, @@ -87,7 +87,7 @@ function generateJob(type, payload) { .map((p) => convert32to64(String(p.account_id))) .join()}`, title: [type, payload.summaries_id].join(), - type: "api", + type: 'api', payload, }; }, @@ -95,28 +95,28 @@ function generateJob(type, payload) { return { url: `${apiUrl}/IDOTA2Match_570/GetMatchHistoryBySequenceNum/V001/?key=${apiKey}&start_at_match_seq_num=${payload.start_at_match_seq_num}`, title: [type, payload.seq_num].join(), - type: "api", + type: 'api', }; }, api_heroes() { return { url: `${apiUrl}/IEconDOTA2_570/GetHeroes/v0001/?key=${apiKey}&language=${payload.language}`, title: [type, payload.language].join(), - type: "api", + type: 'api', payload, }; }, api_items() { return { url: `${apiUrl}/IEconDOTA2_570/GetGameItems/v1?key=${apiKey}&language=${payload.language}`, - type: "api", + type: 'api', }; }, api_leagues() { return { - url: "http://www.dota2.com/webapi/IDOTA2League/GetLeagueInfoList/v001", + url: 'http://www.dota2.com/webapi/IDOTA2League/GetLeagueInfoList/v001', title: [type].join(), - type: "api", + type: 'api', payload, }; }, @@ -124,15 +124,15 @@ function generateJob(type, payload) { return { url: `${apiUrl}/IDOTA2Match_570/GetLiveLeagueGames/v0001/?key=${apiKey}`, title: [type].join(), - type: "api", + type: 'api', payload, }; }, api_notable() { return { - url: "http://www.dota2.com/webapi/IDOTA2Fantasy/GetProPlayerInfo/v001", + url: 'http://www.dota2.com/webapi/IDOTA2Fantasy/GetProPlayerInfo/v001', title: [type].join(), - type: "api", + type: 'api', payload, }; }, @@ -140,44 +140,44 @@ function generateJob(type, payload) { return { url: `${apiUrl}/IDOTA2Teams_570/GetTeamInfo/v1/?key=${apiKey}&team_id=${payload.team_id}`, title: [type].join(), - type: "api", + type: 'api', payload, }; }, api_item_schema() { return { url: `${apiUrl}/IEconItems_570/GetSchemaURL/v1?key=${apiKey}`, - type: "api", + type: 'api', }; }, api_item_icon() { return { url: `${apiUrl}/IEconDOTA2_570/GetItemIconPath/v1?key=${apiKey}&iconname=${payload.iconname}`, - type: "api", + type: 'api', }; }, api_top_live_game() { return { url: `${apiUrl}/IDOTA2Match_570/GetTopLiveGame/v1/?key=${apiKey}&partner=0`, - type: "api", + type: 'api', }; }, api_realtime_stats() { return { url: `${apiUrl}/IDOTA2MatchStats_570/GetRealtimeStats/v1?key=${apiKey}&server_steam_id=${payload.server_steam_id}`, - type: "api", + type: 'api', }; }, api_team_info_by_team_id() { return { url: `${apiUrl}/IDOTA2Match_570/GetTeamInfoByTeamID/v1?key=${apiKey}&start_at_team_id=${payload.start_at_team_id}&teams_requested=1`, - type: "api", + type: 'api', }; }, api_get_ugc_file_details() { return { url: `${apiUrl}/ISteamRemoteStorage/GetUGCFileDetails/v1/?key=${apiKey}&appid=570&ugcid=${payload.ugcid}`, - type: "api", + type: 'api', }; }, parse() { @@ -191,7 +191,7 @@ function generateJob(type, payload) { steam_cdn_team_logos() { return { url: `https://steamcdn-a.akamaihd.net/apps/dota2/images/team_logos/${payload.team_id}.png`, - type: "steam_cdn", + type: 'steam_cdn', }; }, }; @@ -208,7 +208,7 @@ function getData(url, cb) { let u; let delay = Number(config.DEFAULT_DELAY); let timeout = 5000; - if (typeof url === "object" && url && url.url) { + if (typeof url === 'object' && url && url.url) { // options object if (Array.isArray(url.url)) { // select a random element if array @@ -222,18 +222,18 @@ function getData(url, cb) { u = url; } const parse = urllib.parse(u, true); - const steamApi = parse.host === "api.steampowered.com"; + const steamApi = parse.host === 'api.steampowered.com'; if (steamApi) { // choose an api key to use - const apiKeys = config.STEAM_API_KEY.split(","); + const apiKeys = config.STEAM_API_KEY.split(','); parse.query.key = apiKeys[Math.floor(Math.random() * apiKeys.length)]; parse.search = null; // choose a steam api host - const apiHosts = config.STEAM_API_HOST.split(","); + const apiHosts = config.STEAM_API_HOST.split(','); parse.host = apiHosts[Math.floor(Math.random() * apiHosts.length)]; } const target = urllib.format(parse); - console.log("%s - getData: %s", new Date(), target); + console.log('%s - getData: %s', new Date(), target); return setTimeout(() => { request( { @@ -260,11 +260,11 @@ function getData(url, cb) { ) { // invalid response if (url.noRetry) { - return cb(err || "invalid response", body); + return cb(err || 'invalid response', body); } console.error( - "[INVALID] status: %s, retrying: %s", - res ? res.statusCode : "", + '[INVALID] status: %s, retrying: %s', + res ? res.statusCode : '', target ); // var backoff = res && res.statusCode === 429 ? delay * 2 : 0; @@ -278,11 +278,11 @@ function getData(url, cb) { if ( body.result.status === 15 || body.result.error === - "Practice matches are not available via GetMatchDetails" || - body.result.error === "No Match ID specified" || - body.result.error === "Match ID not found" || + 'Practice matches are not available via GetMatchDetails' || + body.result.error === 'No Match ID specified' || + body.result.error === 'Match ID not found' || (body.result.status === 2 && - body.result.statusDetail === "Error retrieving match data." && + body.result.statusDetail === 'Error retrieving match data.' && Math.random() < 0.4) ) { // private match history or attempting to get practice match/invalid id, don't retry @@ -292,10 +292,10 @@ function getData(url, cb) { if (body.result.error || body.result.status === 2) { // valid response, but invalid data, retry if (url.noRetry) { - return cb(err || "invalid data", body); + return cb(err || 'invalid data', body); } console.error( - "invalid data, retrying: %s, %s", + 'invalid data, retrying: %s, %s', target, JSON.stringify(body) ); @@ -346,7 +346,7 @@ function mergeObjects(merge, val) { merge[attr] = val[attr]; } else if (val[attr] && val[attr].constructor === Array) { merge[attr] = merge[attr].concat(val[attr]); - } else if (typeof val[attr] === "object") { + } else if (typeof val[attr] === 'object') { mergeObjects(merge[attr], val[attr]); } else { merge[attr] += Number(val[attr]); @@ -470,7 +470,7 @@ function getStartOfBlockMinutes(size, offset) { } function getEndOfMonth() { - return moment().endOf("month").unix(); + return moment().endOf('month').unix(); } /** @@ -539,7 +539,7 @@ function getPatchIndex(startTime) { * Constructs a replay url * */ function buildReplayUrl(matchId, cluster, replaySalt) { - const suffix = config.NODE_ENV === "test" ? ".dem" : ".dem.bz2"; + const suffix = config.NODE_ENV === 'test' ? '.dem' : '.dem.bz2'; if (cluster === 236) { return `http://replay${cluster}.wmsj.cn/570/${matchId}_${replaySalt}${suffix}`; } @@ -566,7 +566,7 @@ function expectedWin(rates) { * Converts a group of heroes to string * */ function groupToString(g) { - return g.sort((a, b) => a - b).join(","); + return g.sort((a, b) => a - b).join(','); } /** @@ -576,12 +576,12 @@ function matchupToString(t0, t1, t0win) { // create sorted strings of each team const rcg = groupToString(t0); const dcg = groupToString(t1); - let suffix = "0"; + let suffix = '0'; if (rcg <= dcg) { - suffix = t0win ? "0" : "1"; + suffix = t0win ? '0' : '1'; return `${rcg}:${dcg}:${suffix}`; } - suffix = t0win ? "1" : "0"; + suffix = t0win ? '1' : '0'; return `${dcg}:${rcg}:${suffix}`; } @@ -655,10 +655,10 @@ function generateMatchups(match, max, oneSided) { rCombs.shift(); dCombs.shift(); rCombs.forEach((team) => { - result.push(`${groupToString(team)}:${match.radiant_win ? "1" : "0"}`); + result.push(`${groupToString(team)}:${match.radiant_win ? '1' : '0'}`); }); dCombs.forEach((team) => { - result.push(`${groupToString(team)}:${match.radiant_win ? "0" : "1"}`); + result.push(`${groupToString(team)}:${match.radiant_win ? '0' : '1'}`); }); } else { // iterate over combinations, increment count for unique key @@ -802,10 +802,10 @@ function getLaneFromPosData(lanePos, isRadiant) { * Get array of retriever endpoints from config * */ function getRetrieverArr(useGcDataArr) { - const parserHosts = useGcDataArr ? config.GCDATA_RETRIEVER_HOST : ""; + const parserHosts = useGcDataArr ? config.GCDATA_RETRIEVER_HOST : ''; const input = parserHosts || config.RETRIEVER_HOST; const output = []; - const arr = input.split(","); + const arr = input.split(','); arr.forEach((element) => { const parsedUrl = urllib.parse(`http://${element}`, true); for (let i = 0; i < (parsedUrl.query.size || 1); i += 1) { @@ -816,9 +816,9 @@ function getRetrieverArr(useGcDataArr) { } function redisCount(redis, prefix) { - const key = `${prefix}:${moment().startOf("hour").format("X")}`; + const key = `${prefix}:${moment().startOf('hour').format('X')}`; redis.pfadd(key, uuidV4()); - redis.expireat(key, moment().startOf("hour").add(1, "day").format("X")); + redis.expireat(key, moment().startOf('hour').add(1, 'day').format('X')); } function getRedisCountDay(redis, prefix, cb) { @@ -826,7 +826,7 @@ function getRedisCountDay(redis, prefix, cb) { const keyArr = []; for (let i = 0; i < 24; i += 1) { keyArr.push( - `${prefix}:${moment().startOf("hour").subtract(i, "hour").format("X")}` + `${prefix}:${moment().startOf('hour').subtract(i, 'hour').format('X')}` ); } redis.pfcount(...keyArr, cb); @@ -837,7 +837,7 @@ function getRedisCountHour(redis, prefix, cb) { const keyArr = []; for (let i = 1; i < 2; i += 1) { keyArr.push( - `${prefix}:${moment().startOf("hour").subtract(i, "hour").format("X")}` + `${prefix}:${moment().startOf('hour').subtract(i, 'hour').format('X')}` ); } redis.pfcount(...keyArr, cb); @@ -846,7 +846,7 @@ function getRedisCountHour(redis, prefix, cb) { function invokeInterval(func, delay) { // invokes the function immediately, waits for callback, waits the delay, and then calls it again (function invoker() { - console.log("running %s", func.name); + console.log('running %s', func.name); console.time(func.name); return func((err) => { if (err) { @@ -872,7 +872,7 @@ function cleanItemSchema(input) { function checkIfInExperiment(ip, mod) { return ( - crypto.createHash("md5").update(ip).digest().readInt32BE(0) % 100 < mod + crypto.createHash('md5').update(ip).digest().readInt32BE(0) % 100 < mod ); }