]> gitweb.ps.run Git - lolstats/blob - index.js
cleanup
[lolstats] / index.js
1 const fs = require("fs")
2 const request = require("request-promise-native")
3 const sleep = require("util").promisify(setTimeout)
4 const low = require("lowdb")
5 const FileAsync = require("lowdb/adapters/FileAsync")
6
7 const express = require("express")
8 const app = express()
9 const port = 3000
10
11 const patch = "8.24.1";
12 const key = "RGAPI-dce1850a-0954-4b1e-8072-77a26739f076";
13
14 let appRateLimit1;
15 let appRateLimit120;
16 let appRateLimitCount1;
17 let appRateLimitCount120;
18
19 // Express routing
20 // ---------------
21 let rules = [
22   ["/lol", "index.html"],
23   ["/lol/script.js", "script.js"],
24   ["/lol/style.css", "style.css"],
25 ];
26
27 for (i in rules) {
28   let path = rules[i][0];
29   let file = rules[i][1];
30   app.get(path, (req, res) => {
31     res.sendFile(__dirname + "/html/" + file);
32   });
33 }
34 // ---------------
35
36 // Riot API request
37 // ----------------
38 const regions = {
39   "BR": "br1.api.riotgames.com",
40   "EUNE": "eun1.api.riotgames.com",
41   "EUW": "euw1.api.riotgames.com",
42   "JP": "jp1.api.riotgames.com",
43   "KR": "kr.api.riotgames.com",
44   "LAN": "la1.api.riotgames.com",
45   "LAS": "la2.api.riotgames.com",
46   "NA": "na1.api.riotgames.com",
47   "OCE": "oc1.api.riotgames.com",
48   "TR": "tr1.api.riotgames.com",
49   "RU": "ru.api.riotgames.com",
50   "PBE": "pbe1.api.riotgames.com",
51 };
52
53 async function riotRequest(region, url, params, retries) {
54   if (retries < 1) throw "Error too many tries";
55
56   let req = "https://" + regions[region] + url + "?api_key=" + key;
57   for (p in params) {
58     req += "&" + p + "=" + params[p];
59   }
60
61   try {
62     console.log("[Info] request: " + req);
63     let result = await request({uri: req, resolveWithFullResponse: true, json: true});
64
65     let appRateLimit = result.headers["x-app-rate-limit"];
66     let appRateLimitCount = result.headers["x-app-rate-limit-count"];
67     appRateLimit1 = appRateLimit.split(",")[0].split(":")[0];
68     appRateLimit120 = appRateLimit.split(",")[1].split(":")[0];
69     appRateLimitCount1 = appRateLimitCount.split(",")[0].split(":")[0];
70     appRateLimitCount120 = appRateLimitCount.split(",")[1].split(":")[0];
71
72     let delay1 = 1000 / (appRateLimit1 - 1);
73     let delay120 = 120000 / (appRateLimit120 - 1);
74     await sleep(Math.max(delay1, delay120));
75
76     if (typeof result.body == "object") return result.body;
77     return JSON.parse(result.body);
78   } catch (err) {
79     console.log("[Error] riotRequest: " + err.message);
80     return await riotRequest(region, url, params, retries - 1);
81   }
82 }
83 // ----------------
84
85 // User functionality
86 // ------------------
87 let users = {};
88 let nameLookup = {};
89
90 async function loadUserDb(region, accountId) {
91   if (!users[region]) users[region] = {};
92   if (users[region][accountId]) return;
93   let adapter = new FileAsync(region + "/" + accountId + ".json");
94   let db = await low(adapter);
95   await db.defaults({ games: [], gameSummaries: [], account: {} })
96     .write();
97   users[region][accountId] = db;
98 }
99
100 async function loadUserInfo(region, summonerName) {
101   if (!nameLookup[region]) nameLookup[region] = {};
102   if (nameLookup[region][summonerName]) return;
103   let user = await riotRequest(region, "/lol/summoner/v4/summoners/by-name/" + summonerName, {}, 5);
104   let accountId = user.accountId;
105   await loadUserDb(region, accountId);
106   users[region][accountId].set("account", user);
107   nameLookup[region][summonerName] = accountId;
108 }
109
110 async function loadGameSummaries(region, accountId) {
111   let totalGames;
112   let bI = 0, eI = 100;
113
114   loop1:
115   do {
116     let matchlist = await riotRequest(region, "/lol/match/v4/matchlists/by-account/" + accountId, { beginIndex: bI, endIndex: eI }, 5);
117     totalGames = matchlist.totalGames;
118     for (m in matchlist.matches) {
119       let match = matchlist.matches[m];
120       if (users[region][accountId].get("gameSummaries").find({ gameId: match.gameId }).value()) {
121         break loop1;
122       }
123       users[region][accountId].get("gameSummaries").push(match).write();
124     }
125     bI = eI;
126     eI += 100;
127   } while (bI <= totalGames);
128   users[region][accountId].write();
129   users[region][accountId].set("gameSummaries",
130     users[region][accountId].get("gameSummaries").sortBy("timestamp").sortedUniqBy((x) => x.gameId).value()).write();
131 }
132
133 app.get("/lol/matches", async (req, res) => {
134   let region = req.query.region;
135   let summoner = req.query.summoner;
136   console.log("[Info] getting game summaries for " + summoner + " from " + region);
137   try {
138     await loadUserInfo(region, summoner);
139     let accountId = nameLookup[region][summoner];
140     await loadGameSummaries(region, accountId);
141     let matches = users[region][accountId].get("gameSummaries").value();
142     console.log(matches.length);
143     res.send(JSON.stringify(matches));
144   } catch (err) {
145     console.log("[Error] GET /lol/matches: " + err);
146     res.send("error");
147   }
148 });
149 // ------------------
150
151 // Static Data
152 // -----------
153 let champions;
154 let champLookup = {};
155 function loadChampions() {
156   request("http://ddragon.leagueoflegends.com/cdn/" + patch + "/data/en_US/champion.json", (err, res, body) => {
157     if (err) console.log("[Error] loadChampions: " + err);
158     champions = JSON.parse(body).data;
159     for (c in champions) {
160       champLookup[champions[c].key] = c;
161     }
162   });
163 }
164
165 app.get("/lol/champions", (req, res) => {
166   res.send(JSON.stringify(Object.keys(champions)));
167 });
168
169 app.get("/lol/champlookup", (req, res) => {
170   res.send(JSON.stringify(champLookup));
171 });
172 // -----------
173
174 loadChampions();
175 app.listen(port, () => {
176   console.log("Listening on port %d", port)
177 });