mirror of
https://github.com/maxswa/osrs-json-hiscores.git
synced 2025-10-15 10:19:04 +00:00
Add functions to support JSON endpoint.
This commit is contained in:
116
src/hiscores.ts
116
src/hiscores.ts
@@ -14,7 +14,8 @@ import {
|
||||
ActivityName,
|
||||
PlayerActivityRow,
|
||||
Bosses,
|
||||
GetStatsOptions
|
||||
GetStatsOptions,
|
||||
HiscoresResponse
|
||||
} from './types';
|
||||
import {
|
||||
getStatsURL,
|
||||
@@ -31,9 +32,43 @@ import {
|
||||
httpGet,
|
||||
BOSSES,
|
||||
INVALID_FORMAT_ERROR,
|
||||
validateRSN
|
||||
validateRSN,
|
||||
PLAYER_NOT_FOUND_ERROR,
|
||||
FORMATTED_SKILL_NAMES,
|
||||
FORMATTED_BH_NAMES,
|
||||
FORMATTED_CLUE_NAMES,
|
||||
FORMATTED_BOSS_NAMES,
|
||||
FORMATTED_LEAGUE_POINTS,
|
||||
FORMATTED_LMS,
|
||||
FORMATTED_PVP_ARENA,
|
||||
FORMATTED_SOUL_WARS,
|
||||
FORMATTED_RIFTS_CLOSED
|
||||
} from './utils';
|
||||
|
||||
/**
|
||||
* Gets a player's stats from the official OSRS JSON endpoint.
|
||||
*
|
||||
* @param rsn Username of the player.
|
||||
* @param mode Gamemode to fetch ranks for.
|
||||
* @param config Optional axios request config object.
|
||||
* @returns Official JSON stats object.
|
||||
*/
|
||||
export async function getOfficialStats(
|
||||
rsn: string,
|
||||
mode: Gamemode = 'main',
|
||||
config?: AxiosRequestConfig
|
||||
): Promise<HiscoresResponse> {
|
||||
validateRSN(rsn);
|
||||
|
||||
const url = getStatsURL(mode, rsn, true);
|
||||
try {
|
||||
const response = await httpGet<HiscoresResponse>(url, config);
|
||||
return response.data;
|
||||
} catch {
|
||||
throw Error(PLAYER_NOT_FOUND_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Screen scrapes the hiscores to get the formatted rsn of a player.
|
||||
*
|
||||
@@ -60,12 +95,81 @@ export async function getRSNFormat(
|
||||
if (anchor) {
|
||||
return rsnFromElement(anchor);
|
||||
}
|
||||
throw Error('Player not found');
|
||||
throw Error(PLAYER_NOT_FOUND_ERROR);
|
||||
} catch {
|
||||
throw Error('Player not found');
|
||||
throw Error(PLAYER_NOT_FOUND_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses official JSON object of raw stats and returns a stats object.
|
||||
*
|
||||
* @param csv Raw JSON from the official OSRS API.
|
||||
* @returns Parsed stats object.
|
||||
*/
|
||||
export function parseJsonStats(json: HiscoresResponse): Stats {
|
||||
const getActivity = (formattedName: string): Activity => {
|
||||
const hiscoresActivity = json.activities.find(
|
||||
// We must match on name here since id is not guaranteed to be the same between updates
|
||||
({ name }) => name === formattedName
|
||||
);
|
||||
return {
|
||||
rank: hiscoresActivity?.rank ?? -1,
|
||||
score: hiscoresActivity?.score ?? -1
|
||||
};
|
||||
};
|
||||
const reduceActivity = <Key extends string, Reduced = Record<Key, Activity>>(
|
||||
keys: Key[],
|
||||
formattedNames: Record<Key, string>
|
||||
): Reduced =>
|
||||
keys.reduce<Reduced>(
|
||||
(reducer, key) => ({
|
||||
...reducer,
|
||||
[key]: getActivity(formattedNames[key])
|
||||
}),
|
||||
{} as Reduced
|
||||
);
|
||||
|
||||
const skills = SKILLS.reduce<Skills>((skillsObject, skillName) => {
|
||||
const hiscoresSkill = json.skills.find(
|
||||
// We must match on name here since id is not guaranteed to be the same between updates
|
||||
({ name }) => name === FORMATTED_SKILL_NAMES[skillName]
|
||||
);
|
||||
return {
|
||||
...skillsObject,
|
||||
[skillName]: {
|
||||
rank: hiscoresSkill?.rank ?? -1,
|
||||
level: hiscoresSkill?.level ?? -1,
|
||||
xp: hiscoresSkill?.xp ?? -1
|
||||
}
|
||||
};
|
||||
}, {} as Skills);
|
||||
|
||||
const bountyHunter = reduceActivity(BH_MODES, FORMATTED_BH_NAMES);
|
||||
const clues = reduceActivity(CLUES, FORMATTED_CLUE_NAMES);
|
||||
const bosses = reduceActivity(BOSSES, FORMATTED_BOSS_NAMES);
|
||||
|
||||
const leaguePoints = getActivity(FORMATTED_LEAGUE_POINTS);
|
||||
const lastManStanding = getActivity(FORMATTED_LMS);
|
||||
const pvpArena = getActivity(FORMATTED_PVP_ARENA);
|
||||
const soulWarsZeal = getActivity(FORMATTED_SOUL_WARS);
|
||||
const riftsClosed = getActivity(FORMATTED_RIFTS_CLOSED);
|
||||
|
||||
const stats: Stats = {
|
||||
skills,
|
||||
leaguePoints,
|
||||
bountyHunter,
|
||||
lastManStanding,
|
||||
pvpArena,
|
||||
soulWarsZeal,
|
||||
riftsClosed,
|
||||
clues,
|
||||
bosses
|
||||
};
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses CSV string of raw stats and returns a stats object.
|
||||
*
|
||||
@@ -259,7 +363,7 @@ export async function getStats(
|
||||
|
||||
return player;
|
||||
}
|
||||
throw Error('Player not found');
|
||||
throw Error(PLAYER_NOT_FOUND_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -281,7 +385,7 @@ export async function getStatsByGamemode(
|
||||
}
|
||||
const response = await httpGet<string>(getStatsURL(mode, rsn), config);
|
||||
if (response.status !== 200) {
|
||||
throw Error('Player not found');
|
||||
throw Error(PLAYER_NOT_FOUND_ERROR);
|
||||
}
|
||||
const stats = parseStats(response.data);
|
||||
|
||||
|
38
src/types.ts
38
src/types.ts
@@ -199,3 +199,41 @@ export interface GetStatsOptions {
|
||||
rsn?: AxiosRequestConfig;
|
||||
};
|
||||
}
|
||||
|
||||
export interface HiscoresCommon {
|
||||
/**
|
||||
* This field behaves more like an index than a true unique ID.
|
||||
*/
|
||||
id: number;
|
||||
/**
|
||||
* The display name of this skill / activity.
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* The player's official hiscores rank in this skill / activity.
|
||||
*/
|
||||
rank: number;
|
||||
}
|
||||
|
||||
export interface HiscoresSkill extends HiscoresCommon {
|
||||
/**
|
||||
* The player's current level in this skill.
|
||||
*/
|
||||
level: number;
|
||||
/**
|
||||
* The player's current experience in this skill.
|
||||
*/
|
||||
xp: number;
|
||||
}
|
||||
|
||||
export interface HiscoresActivity extends HiscoresCommon {
|
||||
/**
|
||||
* The player's current score in this activity.
|
||||
*/
|
||||
score: number;
|
||||
}
|
||||
|
||||
export interface HiscoresResponse {
|
||||
skills: HiscoresSkill[];
|
||||
activities: HiscoresActivity[];
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ import {
|
||||
|
||||
export const BASE_URL = 'https://secure.runescape.com/m=hiscore_oldschool';
|
||||
export const STATS_URL = 'index_lite.ws?player=';
|
||||
export const JSON_STATS_URL = 'index_lite.json?player=';
|
||||
export const SCORES_URL = 'overall.ws?';
|
||||
|
||||
export type GamemodeUrl = {
|
||||
@@ -188,7 +189,7 @@ export const FORMATTED_BOSS_NAMES: FormattedBossNames = {
|
||||
krilTsutsaroth: "K'ril Tsutsaroth",
|
||||
mimic: 'Mimic',
|
||||
nex: 'Nex',
|
||||
nightmare: 'The Nightmare of Ashihama',
|
||||
nightmare: 'Nightmare',
|
||||
phosanisNightmare: "Phosani's Nightmare",
|
||||
obor: 'Obor',
|
||||
phantomMuspah: 'Phantom Muspah',
|
||||
@@ -273,10 +274,11 @@ export const FORMATTED_BH_NAMES: FormattedBHNames = {
|
||||
hunterV2: 'Bounty Hunter - Hunter'
|
||||
};
|
||||
|
||||
export const FORMATTED_LMS = 'Last Man Standing';
|
||||
export const FORMATTED_PVP_ARENA = 'PvP Arena';
|
||||
export const FORMATTED_LMS = 'LMS - Rank';
|
||||
export const FORMATTED_PVP_ARENA = 'PvP Arena - Rank';
|
||||
export const FORMATTED_SOUL_WARS = 'Soul Wars Zeal';
|
||||
export const FORMATTED_LEAGUE_POINTS = 'League Points';
|
||||
export const FORMATTED_RIFTS_CLOSED = 'Rifts Closed';
|
||||
export const FORMATTED_RIFTS_CLOSED = 'Rifts closed';
|
||||
|
||||
export const INVALID_FORMAT_ERROR = 'Invalid hiscores format';
|
||||
export const PLAYER_NOT_FOUND_ERROR = 'Player not found';
|
||||
|
@@ -6,7 +6,8 @@ import {
|
||||
STATS_URL,
|
||||
SCORES_URL,
|
||||
SKILLS,
|
||||
ACTIVITIES
|
||||
ACTIVITIES,
|
||||
JSON_STATS_URL
|
||||
} from './constants';
|
||||
|
||||
/**
|
||||
@@ -14,10 +15,13 @@ import {
|
||||
*
|
||||
* @param gamemode Gamemode to fetch ranks for.
|
||||
* @param rsn Username of the player.
|
||||
* @param json If the JSON endpoint is desired instead of CSV.
|
||||
* @returns Encoded stats URL.
|
||||
*/
|
||||
export const getStatsURL = (gamemode: Gamemode, rsn: string) =>
|
||||
`${GAMEMODE_URL[gamemode]}${STATS_URL}${encodeURIComponent(rsn)}`;
|
||||
export const getStatsURL = (gamemode: Gamemode, rsn: string, json = false) =>
|
||||
`${GAMEMODE_URL[gamemode]}${
|
||||
json ? JSON_STATS_URL : STATS_URL
|
||||
}${encodeURIComponent(rsn)}`;
|
||||
|
||||
/**
|
||||
* Will generate a player table URL for the official OSRS hiscores website.
|
||||
|
Reference in New Issue
Block a user