<html>
<head>
<meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
<title>Advanced League of Legends Match History & Statistics</title>
<link href="https://fonts.googleapis.com/css?family=Noto+Sans+SC:700" rel="stylesheet">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css" integrity="sha384-B4dIYHKNBt8Bc12p+WXckhzcICo0wtJAoU8YZTY5qE0Id1GSseTk6S+L3BlXeVIU" crossorigin="anonymous">
-->
<div id="inputpanel">
<select id="regionselect">
- <option v-for="r in regions" :value="r">{{ r }}</option>
+ <option v-for="(url, name) in regions" :value="name">{{ name }}</option>
</select>
<input v-on:keyup.enter="submit" id="nameinput" type="text" placeholder="Summoner Name">
<button v-on:click="submit" id="gobutton"><span class="fas fa-arrow-right"></span></button>
<label for="champselect">Champion</label>
<select id="champselect">
<option>All</option>
- <option v-for="c in champions">{{ c.name }}</option>
+ <option v-for="c in champions">{{ c }}</option>
</select>
<button id="refreshbutton" v-on:click="refreshHistory">Refresh</button>
</div>
+ <hr />
<div id="matchlist">
<table>
<tr>
function getRegions() {
- return [ "euw", "na", "kr", "br" ];
+ $.ajax("/lol/regions")
+ .done((data) => {
+ app.regions = JSON.parse(data);
+ });
}
function getChampions() {
- return [ {name: "Aatrox"}, {name: "Annie"}, {name: "Braum"}, {name: "Not"} ];
+ $.ajax("/lol/champions")
+ .done((data) => {
+ app.champions = JSON.parse(data);
+ });
}
function getMatchProps() {
return [
];
}
function getMatches() {
- return [
- {champ: "Xerath", lane: "Middle"},
- {champ: "Quinn", lane: "Bottom"},
- ];
+ $.ajax("/lol/matches?region=" + app.region + "&summoner=" + app.summoner);
+ .done((data) => {
+ app.matches = JSON.parse(data);
+ });
}
function getInfo() {
app.summoner = $("#nameinput").val();
summoner: "",
region: "",
view: "",
- regions: getRegions(),
- champions: getChampions(),
- matchprops: getMatchProps(),
- matches: getMatches(),
+ regions: [],
+ champions: [],
+ matchprops: [],
+ matches: [],
},
methods: {
submit: function() {
setUrl();
},
refreshHistory: function() {
+ getMatches();
},
},
});
window.addEventListener('load', () => {
let view = parseUrl();
setView(view);
+ getRegions();
+ getChampions();
});
body {
- font-family: 'Noto Sans SC', sans-serif;
+ font-family: "Noto Sans SC", sans-serif;
font-weight: bold;
padding: 0px;
margin: 0px;
}
+
#background {
position: fixed;
width: 100%;
height: 100%;
- background-color: rgb(200, 50, 50);
+ background-color: #c83232;
box-shadow: 0 0 400px rgba(0, 0, 0, 0.6) inset;
}
-input, select, button {
- font-size:inherit;
- border: none;
-}
-@keyframes startup {
- 0% {
- top: 50%;
- transform: translate(-50%, -50%);
- width: 65%;
- }
- 100% {
- top: 0%;
- transform: translate(-50%, 0%);
- width: calc(100% - 20px);
- }
-}
-@keyframes startdown {
- 100% {
- top: 50%;
- transform: translate(-50%, -50%);
- width: 65%;
- }
- 0% {
- top: 0%;
- transform: translate(-50%, 0%);
- width: calc(100% - 20px);
- }
-}
-#start.slideup {
- animation: startup 0.3s forwards linear;
-}
-#start.slidedown {
- animation: startdown 0.3s forwards linear;
-}
-#start.up {
- top: 0%;
- transform: translate(-50%, 0%);
- width: calc(100% - 20px);
-}
-#start.down {
- top: 50%;
- transform: translate(-50%, -50%);
- width: 65%;
-}
+
#start {
position: absolute;
- width: 65%;
- left: 50%;
+ margin: 7px;
+ left: calc(50% - 7px);
top: 50%;
transform: translate(-50%, -50%);
- padding: 10px;
text-align: center;
background-color: transparent;
- font-size: 5rem;
+ border-radius: 4px;
+ background-color: white;
+ box-shadow: 1px 1px 40px rgba(0, 0, 0, 0.8);
+}
+
+@media only screen and (orientation: landscape) {
+ #start {
+ width: 65%;
+ }
+}
+@media only screen and (orientation: portrait) {
+ #start {
+ width: 85%;
+ }
}
#title {
width: 100%;
text-shadow: 1px 1px 40px black;
user-select: none;
}
+
#inputpanel {
- border-radius: 5px;
- width: calc(100% - 20px);
- height: 50px;
- background-color: white;
- padding: 10px;
- box-shadow: 1px 1px 40px rgba(0, 0, 0, 0.8);
- font-size: 2rem;
+ width: calc(100% - 14px);
+ height: 7vmin;
+ font-size: 4.5vmin;
position: relative;
+ margin: 7px;
}
-#regionselect {
- width: 110px;
- height: 50px;
+
+#matchhistory, #stats {
+ position: absolute;
+ font-size: 2.5vmin;
+ width: calc(100% - 14px);
+ min-height: calc(100% - (7vmin + 21px) - 14px);
+ margin: 7px;
+ top: calc(7vmin + 21px);
+ background-color: white;
+ border-radius: 4px;
+ text-align: center;
+}
+
+#historyfilters {
position: relative;
- top: 0px;
- padding: 0px 0px 0px 10px;
- margin: 5px;
+ width: calc(100% - 14px);
+ height: calc(21vmin + 14px);
+ margin: 7px;
+}
+
+#matchlist {
+ width: calc(100% - 14px);
+ margin: 7px;
+ height: auto;
+}
+
+hr {
+ margin: 0;
+}
+
+select {
text-align: center;
- background-color: rgb(200, 50, 50);
+}
+
+select:hover {
+ background-color: #b41e1e;
+}
+
+select > option {
+ background-color: #c83232;
+}
+
+input {
+ border: none;
+ outline: none;
+ font-size: inherit;
+}
+
+button, select {
+ vertical-align: baseline;
+ width: 18vmin;
+ height: 7vmin;
+ background-color: #c83232;
color: white;
- border-radius: 5px;
+ border-radius: 4px;
border: none;
outline: none;
+ font-size: inherit;
+}
+
+button:hover {
+ background-color: #b41e1e;
+}
+
+.bottom-right {
+ position: absolute;
+ right: 0px;
+ bottom: 0px;
+}
+
+#regionselect {
text-transform: uppercase;
position: absolute;
- left: 5px;
- top: 5px;
+ left: 0;
z-index: 1;
}
-#regionselect:hover {
- background-color: rgb(180, 30, 30);
-}
-#regionselect>option {
- background-color: rgb(200, 50, 50);
-}
+
#nameinput {
text-align: center;
border-bottom: 1px solid lightgray;
- outline: none;
- margin: 5px;
- width: calc(100% - 220px - 10px);
- height: 50px;
+ width: calc(100% - 36vmin);
+ height: 7vmin;
position: absolute;
- left: 110px;
- top: 5px;
+ left: 18vmin;
+ top: 0px;
}
+
#gobutton, #statsbutton, #historybutton {
position: absolute;
- top: 5px;
- right: 5px;
+ margin: 0px;
+ top: 0px;
+ right: 0px;
z-index: 1;
}
+
#refreshbutton {
position: absolute;
- right: 5px;
- bottom: 5px;
+ right: 0px;
+ bottom: 0px;
}
-button {
- vertical-align: baseline;
- margin: 5px;
- width: 110px;
- height: 50px;
- background-color: rgb(200, 50, 50);
- color: white;
- border-radius: 5px;
- outline: none;
+
+@keyframes startup {
+ 0% {
+ top: 50%;
+ transform: translate(-50%, -50%);
+ @media only screen and (orientation: landscape) {
+ width: 65%;
+ }
+ @media only screen and (orientation: portrait) {
+ width: 85%;
+ }
+ }
+ 100% {
+ top: 0%;
+ transform: translate(-50%, 0%);
+ width: calc(100% - 14px);
+ }
+}
+@keyframes startdown {
+ 100% {
+ top: 50%;
+ transform: translate(-50%, -50%);
+ @media only screen and (orientation: landscape) {
+ width: 65%;
+ }
+ @media only screen and (orientation: portrait) {
+ width: 85%;
+ }
+ }
+ 0% {
+ top: 0%;
+ transform: translate(-50%, 0%);
+ width: calc(100% - 14px);
+ }
}
-#gobutton:hover {
- background-color: rgba(180, 30, 30);
+#start.slideup {
+ animation: startup 0.3s forwards linear;
}
-#matchhistory, #stats {
- position: absolute;
- width: calc(100% - 20px);
- min-height: calc(100% - 90px - 10px);
- margin: 10px;
- top: 80px;
- background-color: white;
- border-radius: 5px;
- text-align: center;
- overflow: visible;
+
+#start.slidedown {
+ animation: startdown 0.3s forwards linear;
}
-.bottom-right {
- position: absolute;
- right: 0px;
- bottom: 0px;
+
+#start.up {
+ top: 0;
+ width: calc(100% - 14px);
+ transform: translate(-50%, 0%);
+}
+
+#start.down {
+ top: 50%;
+ transform: translate(-50%, -50%);
+}
+@media only screen and (orientation: landscape) {
+ #start.down {
+ width: 65%;
+ }
+}
+@media only screen and (orientation: portrait) {
+ #start.down {
+ width: 85%;
+ }
}
+
+/*# sourceMappingURL=style.css.map */
--- /dev/null
+$widget-width: 18vmin
+$widget-height: 7vmin
+$color1: rgb(200, 50, 50)
+$color1-dark: rgb(180, 30, 30)
+$color2: white
+$font-size1: 4.5vmin
+$font-size2: 2.5vmin
+$margin: 7px
+$border: 4px
+
+body
+ font-family: 'Noto Sans SC', sans-serif
+ font-weight: bold
+ padding: 0px
+ margin: 0px
+#background
+ position: fixed
+ width: 100%
+ height: 100%
+ background-color: $color1
+ box-shadow: 0 0 400px rgba(0, 0, 0, 0.6) inset
+#start
+ position: absolute
+ margin: $margin
+ left: calc(50% - #{$margin})
+ top: 50%
+ transform: translate(-50%, -50%)
+ text-align: center
+ background-color: transparent
+ border-radius: $border
+ background-color: $color2
+ box-shadow: 1px 1px 40px rgba(0, 0, 0, 0.8)
+@media only screen and (orientation: landscape)
+ #start
+ width: 65%
+@media only screen and (orientation: portrait)
+ #start
+ width: 85%
+#title
+ width: 100%
+ color: $color2
+ text-shadow: 1px 1px 40px black
+ user-select: none
+#inputpanel
+ width: calc(100% - #{2 * $margin})
+ height: $widget-height
+ font-size: $font-size1
+ position: relative
+ margin: $margin
+#matchhistory, #stats
+ position: absolute
+ font-size: $font-size2
+ width: calc(100% - #{2 * $margin})
+ min-height: calc(100% - (#{$widget-height} + #{3 * $margin}) - #{2 * $margin})
+ margin: $margin
+ top: calc(#{$widget-height} + #{3 * $margin})
+ background-color: $color2
+ border-radius: $border
+ text-align: center
+#historyfilters
+ position: relative
+ width: calc(100% - #{2 * $margin})
+ height: calc(#{3 * $widget-height} + #{2 * $margin})
+ margin: $margin
+#matchlist
+ width: calc(100% - #{2 * $margin})
+ margin: $margin
+ height: auto
+hr
+ margin: 0
+select
+ text-align: center
+select:hover
+ background-color: $color1-dark
+select>option
+ background-color: $color1
+input
+ border: none
+ outline: none
+ font-size: inherit
+button, select
+ vertical-align: baseline
+ width: $widget-width
+ height: $widget-height
+ background-color: $color1
+ color: $color2
+ border-radius: $border
+ border: none
+ outline: none
+ font-size: inherit
+button:hover
+ background-color: $color1-dark
+.bottom-right
+ position: absolute
+ right: 0px
+ bottom: 0px
+#regionselect
+ text-transform: uppercase
+ position: absolute
+ left: 0
+ z-index: 1
+#nameinput
+ text-align: center
+ border-bottom: 1px solid lightgray
+ width: calc(100% - #{2 * $widget-width})
+ height: $widget-height
+ position: absolute
+ left: $widget-width
+ top: 0px
+#gobutton, #statsbutton, #historybutton
+ position: absolute
+ margin: 0px
+ top: 0px
+ right: 0px
+ z-index: 1
+#refreshbutton
+ position: absolute
+ right: 0px
+ bottom: 0px
+@keyframes startup
+ 0%
+ top: 50%
+ transform: translate(-50%, -50%)
+ @media only screen and (orientation: landscape)
+ width: 65%
+ @media only screen and (orientation: portrait)
+ width: 85%
+ 100%
+ top: 0%
+ transform: translate(-50%, 0%)
+ width: calc(100% - #{2 * $margin})
+@keyframes startdown
+ 100%
+ top: 50%
+ transform: translate(-50%, -50%)
+ @media only screen and (orientation: landscape)
+ width: 65%
+ @media only screen and (orientation: portrait)
+ width: 85%
+ 0%
+ top: 0%
+ transform: translate(-50%, 0%)
+ width: calc(100% - #{2 * $margin})
+#start.slideup
+ animation: startup 0.3s forwards linear
+#start.slidedown
+ animation: startdown 0.3s forwards linear
+#start.up
+ top: 0
+ width: calc(100% - #{2 * $margin})
+ transform: translate(-50%, 0%)
+#start.down
+ top: 50%
+ transform: translate(-50%, -50%)
+ @media only screen and (orientation: landscape)
+ width: 65%
+ @media only screen and (orientation: portrait)
+ width: 85%
const express = require("express")
-const JavaScriptObfuscator = require("javascript-obfuscator");
const fs = require("fs")
+const request = require("request-promise-native")
const app = express()
const port = 3000
+const sleep = require('util').promisify(setTimeout)
+
+const patch = "8.24.1";
+const key = "RGAPI-c6cae96a-c4b0-4842-9017-ddd736f3f628";
+let appRateLimit1;
+let appRateLimit120;
+let appRateLimitCount1;
+let appRateLimitCount120;
let rules = [
["/lol", "index.html"],
["/lol/style.css", "style.css"],
];
-let obfuscateJs = false;
-
-function obfuscate(data) {
- let obfResult = JavaScriptObfuscator.obfuscate(
- String(data),
- {
- compact: true,
- identifierNamesGenerator: "mangled",
- }
- );
-
- return obfResult.getObfuscatedCode();
-}
-
for (i in rules) {
let path = rules[i][0];
let file = rules[i][1];
app.get(path, (req, res) => {
- if (file.endsWith(".js") && obfuscateJs) {
- fs.readFile("html/" + file, (err, data) => {
- if (err) throw err;
-
- let obf = obfuscate(data);
- res.send(obf);
- });
- } else {
- res.sendFile(__dirname + "/html/" + file);
- }
+ res.sendFile(__dirname + "/html/" + file);
+ });
+}
+
+async function riotRequest(region, url, params, retries) {
+ if (retries < 1) throw "Error too many tries";
+ let req = "https://" + regions[region] + url + "?api_key=" + key;
+ for (p in params) {
+ req += "&" + p + "=" + params[p];
+ }
+ try {
+ let result = await request({uri: req, resolveWithFullResponse: true, json: true});
+ let appRateLimit = result.headers["x-app-rate-limit"];
+ let appRateLimitCount = result.headers["x-app-rate-limit-count"];
+ appRateLimit1 = appRateLimit.split(",")[0].split(":")[0];
+ appRateLimit120 = appRateLimit.split(",")[1].split(":")[0];
+ appRateLimitCount1 = appRateLimitCount.split(",")[0].split(":")[0];
+ appRateLimitCount120 = appRateLimitCount.split(",")[1].split(":")[0];
+ let delay1 = 1000 / (appRateLimit1 - 1);
+ let delay120 = 120000 / (appRateLimit120 - 1);
+ await sleep(Math.max(delay1, delay120));
+ console.log(Math.max(delay1, delay120));
+ return result.body;
+ } catch (err) {
+ console.log(err.message);
+ return await riotRequest(region, url, params, retries - 1);
+ }
+}
+
+async function getAllMatches(region, accountId) {
+ let matches = [];
+ let totalGames;
+ let bI = 0, eI = 99;
+
+ do {
+ let m = await riotRequest(region, "/lol/match/v4/matchlists/by-account/" + accountId, { beginIndex: bI, endIndex: eI }, 5);
+ console.log(m);
+ totalGames = m.totalGames;
+ matches = matches.concat(m.matches);
+ console.log("Added games " + bI + " to " + eI + ", " + matches.length + " of " + totalGames);
+ bI = eI + 1;
+ eI += 100;
+ } while (bI <= totalGames);
+
+ return matches;
+}
+
+// Static Data
+// -----------
+
+const regions = {
+ "BR": "br1.api.riotgames.com",
+ "EUNE": "eun1.api.riotgames.com",
+ "EUW": "euw1.api.riotgames.com",
+ "JP": "jp1.api.riotgames.com",
+ "KR": "kr.api.riotgames.com",
+ "LAN": "la1.api.riotgames.com",
+ "LAS": "la2.api.riotgames.com",
+ "NA": "na1.api.riotgames.com",
+ "OCE": "oc1.api.riotgames.com",
+ "TR": "tr1.api.riotgames.com",
+ "RU": "ru.api.riotgames.com",
+ "PBE": "pbe1.api.riotgames.com",
+};
+app.get("/lol/regions", (req, res) => {
+ res.send(JSON.stringify(regions));
+});
+let champions = null;
+function getChampions(cb) {
+ request("http://ddragon.leagueoflegends.com/cdn/" + patch + "/data/en_US/champion.json", (err, res, body) => {
+ champions = JSON.parse(body).data;
+ cb();
});
}
+app.get("/lol/champions", (req, res) => {
+ if (champions == null)
+ getChampions(() => {
+ res.send(JSON.stringify(Object.keys(champions)));
+ });
+ else
+ res.send(JSON.stringify(Object.keys(champions)));
+});
+let users = {};
+if (fs.existsSync("users.js")) {
+ fs.readFile("users.js", (err, data) => {
+ users = JSON.parse(data);
+ });
+}
+app.get("/lol/matches", async (req, res) => {
+ let region = req.query.region;
+ let summoner = req.query.summoner;
+ let regionUrl = regions[region];
+ try {
+ let data = await riotRequest(region, "/lol/summoner/v4/summoners/by-name/" + summoner, {}, 5);
+ let accountId = data.accountId;
+ if (users[accountId]) {
+ res.send(JSON.stringify(users[accountId]));
+ return;
+ }
+ let matches = await getAllMatches(region, accountId);
+ users[accountId] = matches;
+ fs.writeFile("users.js", JSON.stringify(users), (err) => {
+ console.log("Error writing file: " + err);
+ });
+ res.send(matches);
+ } catch (err) {
+ console.log(err);
+ }
+});
+
+// -----------
app.listen(port, () => {
console.log("Listening on port %d", port)
"uuid": "^3.3.2"
}
},
+ "request-promise-core": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz",
+ "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=",
+ "requires": {
+ "lodash": "^4.13.1"
+ }
+ },
+ "request-promise-native": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz",
+ "integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=",
+ "requires": {
+ "request-promise-core": "1.1.1",
+ "stealthy-require": "^1.1.0",
+ "tough-cookie": ">=2.3.3"
+ }
+ },
"restore-cursor": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
"integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
},
+ "stealthy-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
+ "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
+ },
"string-template": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/string-template/-/string-template-1.0.0.tgz",
"express": "^4.16.4",
"javascript-obfuscator": "^0.18.1",
"request": "^2.88.0",
+ "request-promise-native": "^1.0.5",
"sqlite3": "^4.0.4"
},
"devDependencies": {},