diff --git a/src/hiscores.ts b/src/hiscores.ts index d992fe2..40fdaaf 100644 --- a/src/hiscores.ts +++ b/src/hiscores.ts @@ -1,5 +1,5 @@ import axios from 'axios'; -// import cheerio from 'cheerio'; +import * as cheerio from 'cheerio'; import { Player, Mode, @@ -9,40 +9,115 @@ import { Skills, BH as BHStats, Clues, + Gamemode, + Category, } from './types'; -import { getStatsURL, SKILLS, BH, CLUES } from './utils'; +import { + getStatsURL, + SKILLS, + BH, + CLUES, + MODES, + getPlayerTableURL, + getHiscoresPageURL, +} from './utils'; -// 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()); -// } -// } +export async function getStats( + rsn: string, + mode: Mode = 'full' +): Promise { + 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 (!MODES.includes(mode)) { + throw Error('Invalid game mode'); + } + if (mode === 'full') { + const mainRes = await axios(getStatsURL('main', rsn)); + if (mainRes.status === 200) { + const otherResponses = await Promise.all([ + axios(getStatsURL('iron', rsn)).catch(err => err), + axios(getStatsURL('hc', rsn)).catch(err => err), + axios(getStatsURL('ult', rsn)).catch(err => err), + getRSNFormat(rsn), + ]); -// async function getPlayerStats(rsn: string, mode: Mode): Promise { -// if (mode === 'full') { -// } else { -// const response = await axios(getStatsURL(mode, rsn)); -// if (response.status !== 200) { -// throw Error('Player not found'); -// } -// const player: Player = { -// rsn, -// mode, -// dead: false, -// deironed: false, -// [mode]: parseStats(response.data), -// }; -// return player; -// } -// } + let gameMode: Gamemode = 'main'; + const [ironRes, hcRes, ultRes, formattedName] = otherResponses; + + if (ironRes.status === 200) { + if (hcRes.status === 200) { + gameMode = 'hc'; + } else if (ultRes.status === 200) { + gameMode = 'ult'; + } else { + gameMode = 'iron'; + } + } else { + gameMode = 'main'; + } + + const player: Player = { + rsn, + mode: gameMode, + dead: false, + deironed: false, + }; + + switch (gameMode) { + case 'iron': + player.main = parseStats(mainRes.data); + player.iron = parseStats(ironRes.data); + if (player.main.skills.overall.xp !== player.iron.skills.overall.xp) { + player.deironed = true; + player.mode = 'main'; + } + break; + case 'hc': + player.main = parseStats(mainRes.data); + player.iron = parseStats(ironRes.data); + player.hc = parseStats(hcRes.data); + if (player.iron.skills.overall.xp !== player.hc.skills.overall.xp) { + player.dead = true; + player.mode = 'iron'; + } + if (player.main.skills.overall.xp !== player.iron.skills.overall.xp) { + player.deironed = true; + player.mode = 'main'; + } + break; + case 'ult': + player.main = parseStats(mainRes.data); + player.iron = parseStats(ironRes.data); + player.ult = parseStats(ultRes.data); + if (player.main.skills.overall.xp !== player.iron.skills.overall.xp) { + player.deironed = true; + player.mode = 'main'; + } + break; + } + + return player; + } + throw Error('Player not found'); + } else { + const response = await axios(getStatsURL(mode, rsn)); + if (response.status !== 200) { + throw Error('Player not found'); + } + const player: Player = { + rsn, + mode, + dead: false, + deironed: false, + [mode]: parseStats(response.data), + }; + return player; + } +} // /** // * Gets a hiscore page. @@ -79,45 +154,32 @@ import { getStatsURL, SKILLS, BH, CLUES } from './utils'; // } // } -// /** -// * Gets a hiscore page. -// * -// * Scrapes OSRS hiscores and converts to objects. -// * -// * @access private -// * -// * @param {string} mode The game mode. -// * @param {string} category The category of hiscores. -// * @param {number} page The page of players. -// * -// * @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; +// async function getHiscoresPage( +// mode: Gamemode, +// category: Category, +// page: number +// ) { +// const url = getHiscoresPageURL(mode, category, page); // const players = []; // const response = await axios(url); // const $ = cheerio.load(response.data); // const playersHTML = $('.personal-hiscores__row').toArray(); -// for (let player of playersHTML) { +// for (const player of playersHTML) { // const attributes = player.children.filter(node => node.name === 'td'); -// console.log(); + // let playerInfo = { -// mode: mode, -// category: category, -// rank: attributes[0].children[0].data.slice(1, -1), -// rsn: attributes[1].children[1].children[0].data.replace(/\uFFFD/g, ' '), +// mode, +// category, +// rank: (attributes[0].children[0].data || '').slice(1, -1), +// rsn: (attributes[1].children[1].children[0].data || '').replace( +// /\uFFFD/g, +// ' ' +// ), // }; -// hiscores.skills.includes(category.toLowerCase()) +// SKILLS.includes(category) // ? (playerInfo = Object.assign( // { // level: attributes[2].children[0].data.slice(1, -1), @@ -137,32 +199,21 @@ import { getStatsURL, SKILLS, BH, CLUES } from './utils'; // return players; // } -// /** -// * Returns proper capitalization and punctuation in a username. -// * -// * Searches hiscores table with rsn and returns the text from the username cell. -// * -// * @access public -// * -// * @param {string} rsn The player's username. -// * -// * @returns {string} The player's formatted username. -// */ -// async function getRSNFormat(rsn) { -// const url = -// URLs.main + URLs.scores + 'table=0&user=' + encodeURIComponent(rsn); +export const getRSNFormat = async (rsn: string) => { + const url = getPlayerTableURL('main', rsn); -// try { -// const response = await axios(url); -// const $ = cheerio.load(response.data); -// return $('[style="color:#AA0022;"]')[1].children[0].data.replace( -// /\uFFFD/g, -// ' ' -// ); -// } catch { -// throw Error('Player not found'); -// } -// } + try { + const response = await axios(url); + const $ = cheerio.load(response.data); + const rawName = $('[style="color:#AA0022;"]')[1].children[0].data; + if (rawName) { + return rawName.replace(/\uFFFD/g, ' '); + } + throw Error('Player not found'); + } catch { + throw Error('Player not found'); + } +}; export const parseStats = (csv: string): Stats => { const splitCSV = csv