diff --git a/.gitignore b/.gitignore index 62c8935..ebe4b53 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.idea/ \ No newline at end of file +.idea/ +node_modules \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..0a72520 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "trailingComma": "es5", + "tabWidth": 2, + "semi": true, + "singleQuote": true +} diff --git a/hiscores.js b/hiscores.js index d7d4ce3..88bad1c 100644 --- a/hiscores.js +++ b/hiscores.js @@ -1,3 +1,5 @@ +const axios = require('axios'); + const URLs = { main: 'http://services.runescape.com/m=hiscore_oldschool/', iron: 'http://services.runescape.com/m=hiscore_oldschool_ironman/', @@ -7,7 +9,7 @@ const URLs = { sdmm: 'http://services.runescape.com/m=hiscore_oldschool_seasonal/', dmmt: 'http://services.runescape.com/m=hiscore_oldschool_tournament/', stats: 'index_lite.ws?player=', - scores: 'overall.ws?' + scores: 'overall.ws?', }, hiscores = { skills: [ @@ -34,20 +36,12 @@ const URLs = { 'farming', 'runecraft', 'hunter', - 'construction'], - other: [ - 'easyclues', - 'mediumclues', - 'allclues', - 'roguebh', - 'hunterbh', - 'hardclues', - 'lms', - 'eliteclues', - 'masterclues' - ] + 'construction', + ], + clues: ['all', 'beginner', 'easy', 'medium', 'hard', 'elite', 'master'], + bh: ['rouge', 'hunter'], }, - validModes = ['full', 'main', 'iron', 'hc', 'ult', 'dmm', 'sdmm', 'dmmt'] + validModes = ['full', 'main', 'iron', 'hc', 'ult', 'dmm', 'sdmm', 'dmmt']; /** * Gets a player's stats. @@ -61,21 +55,17 @@ const URLs = { * * @returns {Object} A player object. */ -async function getStats (rsn, mode = 'full') { - if(typeof rsn !== 'string') { - throw Error('RSN must be a string') - } - else if(!/^[a-zA-Z0-9 _]+$/.test(rsn)) { - throw Error('RSN contains invalid character') - } - else if(rsn.length > 12 || rsn.length < 1) { - throw Error('RSN must be between 1 and 12 characters') - } - else if(!validModes.includes(mode.toLowerCase())) { - throw Error('Invalid game mode') - } - else { - return await getPlayerStats(rsn, mode.toLowerCase()) +async function getStats(rsn, mode = 'full') { + if (typeof rsn !== 'string') { + throw Error('RSN must be a string'); + } else if (!/^[a-zA-Z0-9 _]+$/.test(rsn)) { + throw Error('RSN contains invalid character'); + } else if (rsn.length > 12 || rsn.length < 1) { + throw Error('RSN must be between 1 and 12 characters'); + } else if (!validModes.includes(mode.toLowerCase())) { + throw Error('Invalid game mode'); + } else { + return await getPlayerStats(rsn, mode.toLowerCase()); } } @@ -91,107 +81,97 @@ async function getStats (rsn, mode = 'full') { * * @returns {Object} A player object. */ -async function getPlayerStats (rsn, mode) { +async function getPlayerStats(rsn, mode) { let player = { rsn: rsn, mode: mode, dead: false, - deironed: false - } + deironed: false, + }; - if(mode === 'full') { - const responses = [] - let csv + if (mode === 'full') { + const responses = []; - responses[0] = await fetch(URLs.main + URLs.stats + encodeURIComponent(rsn)) - if (responses[0].ok) { + responses[0] = await axios( + URLs.main + URLs.stats + encodeURIComponent(rsn) + ); + + if (responses[0].status === 200) { const otherResponses = await Promise.all([ - fetch(URLs.iron + URLs.stats + encodeURIComponent(rsn)), - fetch(URLs.hc + URLs.stats + encodeURIComponent(rsn)), - fetch(URLs.ult + URLs.stats + encodeURIComponent(rsn)), - getRSNFormat(rsn) - ]) - - player.rsn = otherResponses.pop() + axios(URLs.iron + URLs.stats + encodeURIComponent(rsn)).catch( + res => res + ), + axios(URLs.hc + URLs.stats + encodeURIComponent(rsn)).catch(res => res), + axios(URLs.ult + URLs.stats + encodeURIComponent(rsn)).catch( + res => res + ), + ]); for (let res of otherResponses) { - responses.push(res) + responses.push(res); } - if (responses[1].ok) { - if (responses[2].ok) { - player.mode = 'hc' - } - else if (responses[3].ok) { - player.mode = 'ult' - } - else { - player.mode = 'iron' + if (responses[1].status === 200) { + if (responses[2].status === 200) { + player.mode = 'hc'; + } else if (responses[3].status === 200) { + player.mode = 'ult'; + } else { + player.mode = 'iron'; } + } else { + player.mode = 'main'; } - else { - player.mode = 'main' - } - } - else { - throw Error('Player not found') + } else { + throw Error('Player not found'); } switch (player.mode) { case 'main': - csv = await responses[0].text() - player.main = parseStats(csv) - break + player.main = parseStats(responses[0].data); + break; case 'iron': - csv = await responses[0].text() - player.main = parseStats(csv) - csv = await responses[1].text() - player.iron = parseStats(csv) - if(player.main.stats.overall.xp !== player.iron.stats.overall.xp) { - player.deironed = true - player.mode = 'main' + player.main = parseStats(responses[0].data); + player.iron = parseStats(responses[1].data); + if (player.main.stats.overall.xp !== player.iron.stats.overall.xp) { + player.deironed = true; + player.mode = 'main'; } - break + break; case 'hc': - csv = await responses[0].text() - player.main = parseStats(csv) - csv = await responses[1].text() - player.iron = parseStats(csv) - csv = await responses[2].text() - player.hc = parseStats(csv) - if(player.iron.stats.overall.xp !== player.hc.stats.overall.xp) { - player.dead = true - player.mode = 'iron' + player.main = parseStats(responses[0].data); + player.iron = parseStats(responses[1].data); + player.hc = parseStats(responses[2].data); + if (player.iron.stats.overall.xp !== player.hc.stats.overall.xp) { + player.dead = true; + player.mode = 'iron'; } - if(player.main.stats.overall.xp !== player.iron.stats.overall.xp) { - player.deironed = true - player.mode = 'main' + if (player.main.stats.overall.xp !== player.iron.stats.overall.xp) { + player.deironed = true; + player.mode = 'main'; } - break + break; case 'ult': - csv = await responses[0].text() - player.main = parseStats(csv) - csv = await responses[1].text() - player.iron = parseStats(csv) - csv = await responses[3].text() - player.ult = parseStats(csv) - if(player.main.stats.overall.xp !== player.iron.stats.overall.xp) { - player.deironed = true - player.mode = 'main' + player.main = parseStats(responses[0].data); + player.iron = parseStats(responses[1].data); + player.ult = parseStats(responses[3].data); + if (player.main.stats.overall.xp !== player.iron.stats.overall.xp) { + player.deironed = true; + player.mode = 'main'; } - break + break; } - return player - } - else { - const response = await fetch(URLs[mode] + URLs.stats + encodeURIComponent(rsn)) - if(!response.ok) { - throw Error('Player not found') + return player; + } else { + const response = await axios( + URLs[mode] + URLs.stats + encodeURIComponent(rsn) + ); + if (response.status !== 200) { + throw Error('Player not found'); } - const csv = await response.text() - player[mode] = parseStats(csv) - return player + player[mode] = parseStats(response.data); + return player; } } @@ -208,18 +188,25 @@ async function getPlayerStats (rsn, mode) { * * @returns {Object[]} Array of player objects. */ -async function getHiscores (mode, category = 'overall', page = 1) { - if(!validModes.includes(mode.toLowerCase()) || mode.toLowerCase() === 'full') { - throw Error('Invalid game mode') - } - else if(!Number.isInteger(page) || page < 1) { - throw Error('Page must be an integer greater than 0') - } - else if(!hiscores.skills.includes(category.toLowerCase()) && !hiscores.other.includes(category.toLowerCase())) { - throw Error('Invalid category') - } - else { - return await getHiscoresPage(mode.toLowerCase(), category.toLowerCase(), page) +async function getHiscores(mode, category = 'overall', page = 1) { + if ( + !validModes.includes(mode.toLowerCase()) || + mode.toLowerCase() === 'full' + ) { + throw Error('Invalid game mode'); + } else if (!Number.isInteger(page) || page < 1) { + throw Error('Page must be an integer greater than 0'); + } else if ( + !hiscores.skills.includes(category.toLowerCase()) && + !hiscores.other.includes(category.toLowerCase()) + ) { + throw Error('Invalid category'); + } else { + return await getHiscoresPage( + mode.toLowerCase(), + category.toLowerCase(), + page + ); } } @@ -237,40 +224,48 @@ async function getHiscores (mode, category = 'overall', page = 1) { * @returns {Object[]} Array of player objects. */ async function getHiscoresPage(mode, category, page) { - const url = URLs[mode] + URLs.scores + - (hiscores.skills.includes(category) ? - 'table=' + hiscores.skills.indexOf(category) : - 'category_type=1' + '&table=' + hiscores.other.indexOf(category)) + - '&page=' + page + const url = + URLs[mode] + + URLs.scores + + (hiscores.skills.includes(category) + ? 'table=' + hiscores.skills.indexOf(category) + : 'category_type=1' + '&table=' + hiscores.other.indexOf(category)) + + '&page=' + + page; - const response = await fetch(url) - let players = [], element = document.createElement('html') - element.innerHTML = await response.text() - const playersHTML = element.querySelectorAll('.personal-hiscores__row') + const response = await axios(url); + let players = [], + element = document.createElement('html'); + element.innerHTML = await response.text(); + const playersHTML = element.querySelectorAll('.personal-hiscores__row'); - for(let player of playersHTML) { - const attributes = player.querySelectorAll('td') + for (let player of playersHTML) { + const attributes = player.querySelectorAll('td'); let playerInfo = { mode: mode, category: category, rank: attributes[0].innerHTML.slice(1, -1), - rsn: attributes[1].childNodes[1].innerHTML.replace(/\uFFFD/g, ' ') + rsn: attributes[1].childNodes[1].innerHTML.replace(/\uFFFD/g, ' '), + }; + + hiscores.skills.includes(category.toLowerCase()) + ? (playerInfo = Object.assign( + { + level: attributes[2].innerHTML.slice(1, -1), + xp: attributes[3].innerHTML.slice(1, -1), + }, + playerInfo + )) + : (playerInfo.score = attributes[2].innerHTML.slice(1, -1)); + + if (mode === 'hc') { + playerInfo.dead = attributes[1].childElementCount > 1; } - hiscores.skills.includes(category.toLowerCase()) ? - playerInfo = Object.assign( - {level: attributes[2].innerHTML.slice(1, -1), - xp: attributes[3].innerHTML.slice(1, -1)}, playerInfo) : - playerInfo.score = attributes[2].innerHTML.slice(1, -1) - - if(mode === 'hc') { - playerInfo.dead = attributes[1].childElementCount > 1 - } - - players.push(playerInfo) + players.push(playerInfo); } - return players + return players; } /** @@ -285,102 +280,57 @@ async function getHiscoresPage(mode, category, page) { * @returns {string} The player's formatted username. */ async function getRSNFormat(rsn) { - const url = URLs.main + URLs.scores + 'table=0&user=' + rsn + const url = URLs.main + URLs.scores + 'table=0&user=' + rsn; - const response = await fetch(url) - let element = document.createElement('html') - element.innerHTML = await response.text() - const cells = element.querySelectorAll('[style="color:#AA0022;"]') + const response = await axios(url); + let element = document.createElement('html'); + element.innerHTML = await response.text(); + const cells = element.querySelectorAll('[style="color:#AA0022;"]'); - if(cells.length >= 2) { - return cells[1].innerHTML.replace(/\uFFFD/g, ' ') - } - else { - throw Error('Player not found') + if (cells.length >= 2) { + return cells[1].innerHTML.replace(/\uFFFD/g, ' '); + } else { + throw Error('Player not found'); } } -let parseStats = (csv) => { - let stats = { - stats:{ - overall:{rank:0,level:0,xp:0}, - attack:{rank:0,level:0,xp:0}, - defence:{rank:0,level:0,xp:0}, - strength:{rank:0,level:0,xp:0}, - hitpoints:{rank:0,level:0,xp:0}, - ranged:{rank:0,level:0,xp:0}, - prayer:{rank:0,level:0,xp:0}, - magic:{rank:0,level:0,xp:0}, - cooking:{rank:0,level:0,xp:0}, - woodcutting:{rank:0,level:0,xp:0}, - fletching:{rank:0,level:0,xp:0}, - fishing:{rank:0,level:0,xp:0}, - firemaking:{rank:0,level:0,xp:0}, - crafting:{rank:0,level:0,xp:0}, - smithing:{rank:0,level:0,xp:0}, - mining:{rank:0,level:0,xp:0}, - herblore:{rank:0,level:0,xp:0}, - agility:{rank:0,level:0,xp:0}, - thieving:{rank:0,level:0,xp:0}, - slayer:{rank:0,level:0,xp:0}, - farming:{rank:0,level:0,xp:0}, - runecraft:{rank:0,level:0,xp:0}, - hunter:{rank:0,level:0,xp:0}, - construction:{rank:0,level:0,xp:0} - }, - clues:{ - all:{rank:0,score:0}, - easy:{rank:0,score:0}, - medium:{rank:0,score:0}, - hard:{rank:0,score:0}, - elite:{rank:0,score:0}, - master:{rank:0,score:0} - }, - bh:{ - rogue:{rank:0,score:0}, - hunter:{rank:0,score:0} - }, - lms:{rank:0,score:0} - }, splitCSV = csv.split('\n'), i = 0, skillInfo - for (let skill of hiscores.skills) { - skillInfo = splitCSV[i].split(',') - stats.stats[skill].rank = skillInfo[0] - stats.stats[skill].level = skillInfo[1] - stats.stats[skill].xp = skillInfo[2] - i++ - } +let parseStats = csv => { + const stats = { + stats: {}, + clues: {}, + bh: {}, + lms: {}, + }; + const splitCSV = csv.split('\n'); - skillInfo = splitCSV[26].split(',') - stats.clues.all.rank = skillInfo[0] - stats.clues.all.score = skillInfo[1] - skillInfo = splitCSV[24].split(',') - stats.clues.easy.rank = skillInfo[0] - stats.clues.easy.score = skillInfo[1] - skillInfo = splitCSV[25].split(',') - stats.clues.medium.rank = skillInfo[0] - stats.clues.medium.score = skillInfo[1] - skillInfo = splitCSV[29].split(',') - stats.clues.hard.rank = skillInfo[0] - stats.clues.hard.score = skillInfo[1] - skillInfo = splitCSV[31].split(',') - stats.clues.elite.rank = skillInfo[0] - stats.clues.elite.score = skillInfo[1] - skillInfo = splitCSV[32].split(',') - stats.clues.master.rank = skillInfo[0] - stats.clues.master.score = skillInfo[1] + const statObjects = splitCSV + .filter(stat => !!stat) + .map(stat => { + const splitStat = stat.split(','); + const obj = {}; + if (splitStat.length === 3) { + [obj.rank, obj.level, obj.xp] = splitStat; + } else { + [obj.rank, obj.score] = splitStat; + } + return obj; + }); - skillInfo = splitCSV[27].split(',') - stats.bh.rogue.rank = skillInfo[0] - stats.bh.rogue.score = skillInfo[1] - skillInfo = splitCSV[28].split(',') - stats.bh.hunter.rank = skillInfo[0] - stats.bh.hunter.score = skillInfo[1] + statObjects.forEach((obj, index) => { + if (index < hiscores.skills.length) { + stats.stats[hiscores.skills[index]] = obj; + } else if (index < hiscores.skills.length + hiscores.bh.length) { + stats.bh[hiscores.bh[index - hiscores.skills.length]] = obj; + } else if (index < hiscores.skills.length + hiscores.bh.length + 1) { + stats.lms = obj; + } else { + stats.clues[ + hiscores.clues[index - hiscores.skills.length - hiscores.bh.length - 1] + ] = obj; + } + }); - skillInfo = splitCSV[30].split(',') - stats.lms.rank = skillInfo[0] - stats.lms.score = skillInfo[1] + return stats; +}; - return stats -} - -export default {getStats, getHiscores, getRSNFormat} \ No newline at end of file +module.exports = { getStats, getHiscores, getRSNFormat };