From a7cbafa5826c773ea38c6fd34e1ddf2b83452443 Mon Sep 17 00:00:00 2001 From: maxswa Date: Thu, 13 Jun 2019 17:08:58 -0400 Subject: [PATCH] Convert parseStats to typescript --- src/hiscores.ts | 233 +++++++++++++++++++++++++++++++++++++++++++ src/index.ts | 1 + src/utils/helpers.ts | 5 + src/utils/index.ts | 2 + 4 files changed, 241 insertions(+) create mode 100644 src/hiscores.ts create mode 100644 src/index.ts create mode 100644 src/utils/helpers.ts create mode 100644 src/utils/index.ts diff --git a/src/hiscores.ts b/src/hiscores.ts new file mode 100644 index 0000000..d992fe2 --- /dev/null +++ b/src/hiscores.ts @@ -0,0 +1,233 @@ +import axios from 'axios'; +// import cheerio from 'cheerio'; +import { + Player, + Mode, + Activity, + Skill, + Stats, + Skills, + BH as BHStats, + Clues, +} from './types'; +import { getStatsURL, SKILLS, BH, CLUES } 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()); +// } +// } + +// 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; +// } +// } + +// /** +// * Gets a hiscore page. +// * +// * Scrapes OSRS hiscores and converts to objects. +// * +// * @access public +// * +// * @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 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 +// ); +// } +// } + +// /** +// * 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; + +// const players = []; +// const response = await axios(url); +// const $ = cheerio.load(response.data); +// const playersHTML = $('.personal-hiscores__row').toArray(); + +// for (let 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, ' '), +// }; + +// hiscores.skills.includes(category.toLowerCase()) +// ? (playerInfo = Object.assign( +// { +// level: attributes[2].children[0].data.slice(1, -1), +// xp: attributes[3].children[0].data.slice(1, -1), +// }, +// playerInfo +// )) +// : (playerInfo.score = attributes[2].children[0].data.slice(1, -1)); + +// if (mode === 'hc') { +// playerInfo.dead = attributes[1].children.length > 1; +// } + +// players.push(playerInfo); +// } + +// 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); + +// 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'); +// } +// } + +export const parseStats = (csv: string): Stats => { + const splitCSV = csv + .split('\n') + .filter(entry => !!entry) + .map(stat => stat.split(',')); + + const skillObjects: Skill[] = splitCSV + .filter(stat => stat.length === 3) + .map(stat => { + const skill: Skill = { + rank: parseInt(stat[0], 10), + level: parseInt(stat[1], 10), + xp: parseInt(stat[2], 10), + }; + return skill; + }); + + const activityObjects: Activity[] = splitCSV + .filter(stat => stat.length === 2) + .map(stat => { + const activity: Activity = { + rank: parseInt(stat[0], 10), + score: parseInt(stat[1], 10), + }; + return activity; + }); + + const bhObjects = activityObjects.splice(0, BH.length); + const [lms] = activityObjects.splice(0, 1); + const clueObjects = activityObjects.splice(0, CLUES.length); + + const skills: Skills = skillObjects.reduce( + (prev, curr, index) => { + const newSkills = { ...prev }; + newSkills[SKILLS[index]] = curr; + return newSkills; + }, + {} as Skills + ); + + const bh: BHStats = bhObjects.reduce( + (prev, curr, index) => { + const newBH = { ...prev }; + newBH[BH[index]] = curr; + return newBH; + }, + {} as BHStats + ); + + const clues: Clues = clueObjects.reduce( + (prev, curr, index) => { + const newClues = { ...prev }; + newClues[CLUES[index]] = curr; + return newClues; + }, + {} as Clues + ); + + const stats: Stats = { + skills, + bh, + lms, + clues, + }; + + return stats; +}; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..8d5cc60 --- /dev/null +++ b/src/index.ts @@ -0,0 +1 @@ +export * from './hiscores'; diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts new file mode 100644 index 0000000..20f23b4 --- /dev/null +++ b/src/utils/helpers.ts @@ -0,0 +1,5 @@ +import { Gamemode } from '../types'; +import { BASE_URL, GAMEMODE_URL, STATS_URL } from './constants'; + +export const getStatsURL = (gamemode: Gamemode, rsn: string) => + `${BASE_URL}${GAMEMODE_URL[gamemode]}${STATS_URL}${encodeURIComponent(rsn)}`; diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..5a9087d --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,2 @@ +export * from './constants'; +export * from './helpers';