mirror of
https://github.com/maxswa/osrs-json-hiscores.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
16 Commits
bump-axios
...
v2.16.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4872d04301 | ||
|
|
55312ba0fd | ||
|
|
331a448503 | ||
|
|
84c704a846 | ||
|
|
561495632c | ||
|
|
bdfdf1eb14 | ||
|
|
e349776060 | ||
|
|
546bc5acf7 | ||
|
|
66528cd9d9 | ||
|
|
99ea3fb722 | ||
|
|
7f16d26e3c | ||
|
|
5483e54d78 | ||
|
|
97532b0c03 | ||
|
|
f9a56e2b3a | ||
|
|
9f43f0c39f | ||
|
|
d67ffa3e20 |
@@ -12,7 +12,7 @@ import {
|
|||||||
getSkillPageURL,
|
getSkillPageURL,
|
||||||
getStatsURL,
|
getStatsURL,
|
||||||
BOSSES,
|
BOSSES,
|
||||||
INVALID_FORMAT_ERROR,
|
InvalidFormatError,
|
||||||
BH_MODES,
|
BH_MODES,
|
||||||
parseJsonStats,
|
parseJsonStats,
|
||||||
HiscoresResponse
|
HiscoresResponse
|
||||||
@@ -276,12 +276,12 @@ test('Parse CSV with unknown activity', () => {
|
|||||||
const statsWithUnknownActivity = `${lynxTitanStats}
|
const statsWithUnknownActivity = `${lynxTitanStats}
|
||||||
-1,-1`;
|
-1,-1`;
|
||||||
expect(() => parseStats(statsWithUnknownActivity)).toThrow(
|
expect(() => parseStats(statsWithUnknownActivity)).toThrow(
|
||||||
INVALID_FORMAT_ERROR
|
InvalidFormatError
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Parse invalid CSV', () => {
|
test('Parse invalid CSV', () => {
|
||||||
expect(() => parseStats('invalid')).toThrow(INVALID_FORMAT_ERROR);
|
expect(() => parseStats('invalid')).toThrow(InvalidFormatError);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Get name format', () => {
|
describe('Get name format', () => {
|
||||||
@@ -580,6 +580,12 @@ describe('Get stats options', () => {
|
|||||||
)
|
)
|
||||||
).toBeFalsy();
|
).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
it('omits excluded gamemodes', async () => {
|
||||||
|
const response = await getStats(rsn, {
|
||||||
|
otherGamemodes: ['ironman', 'ultimate']
|
||||||
|
});
|
||||||
|
expect(response.hardcore).toBeUndefined();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('CSV and JSON parsing outputs identical object', async () => {
|
test('CSV and JSON parsing outputs identical object', async () => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "osrs-json-hiscores",
|
"name": "osrs-json-hiscores",
|
||||||
"version": "2.16.0",
|
"version": "2.16.2",
|
||||||
"description": "The Old School Runescape API wrapper that does more!",
|
"description": "The Old School Runescape API wrapper that does more!",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"types": "lib/index.d.ts",
|
"types": "lib/index.d.ts",
|
||||||
@@ -67,7 +67,10 @@
|
|||||||
],
|
],
|
||||||
"ignorePatterns": [
|
"ignorePatterns": [
|
||||||
"**/@types/*"
|
"**/@types/*"
|
||||||
]
|
],
|
||||||
|
"rules": {
|
||||||
|
"max-classes-per-file": "off"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"trailingComma": "none",
|
"trailingComma": "none",
|
||||||
|
|||||||
163
src/hiscores.ts
163
src/hiscores.ts
@@ -1,8 +1,4 @@
|
|||||||
import {
|
import axios, { AxiosRequestConfig } from 'axios';
|
||||||
AxiosRequestConfig,
|
|
||||||
AxiosResponse,
|
|
||||||
InternalAxiosRequestConfig
|
|
||||||
} from 'axios';
|
|
||||||
import { BinaryData, JSDOM } from 'jsdom';
|
import { BinaryData, JSDOM } from 'jsdom';
|
||||||
import {
|
import {
|
||||||
Player,
|
Player,
|
||||||
@@ -35,9 +31,10 @@ import {
|
|||||||
getActivityPageURL,
|
getActivityPageURL,
|
||||||
httpGet,
|
httpGet,
|
||||||
BOSSES,
|
BOSSES,
|
||||||
INVALID_FORMAT_ERROR,
|
InvalidFormatError,
|
||||||
|
PlayerNotFoundError,
|
||||||
|
HiScoresError,
|
||||||
validateRSN,
|
validateRSN,
|
||||||
PLAYER_NOT_FOUND_ERROR,
|
|
||||||
FORMATTED_SKILL_NAMES,
|
FORMATTED_SKILL_NAMES,
|
||||||
FORMATTED_BH_NAMES,
|
FORMATTED_BH_NAMES,
|
||||||
FORMATTED_CLUE_NAMES,
|
FORMATTED_CLUE_NAMES,
|
||||||
@@ -68,8 +65,12 @@ export async function getOfficialStats(
|
|||||||
try {
|
try {
|
||||||
const response = await httpGet<HiscoresResponse>(url, config);
|
const response = await httpGet<HiscoresResponse>(url, config);
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch {
|
} catch (err) {
|
||||||
throw Error(PLAYER_NOT_FOUND_ERROR);
|
if (!axios.isAxiosError(err)) throw err;
|
||||||
|
|
||||||
|
if (err.response?.status === 404) throw new PlayerNotFoundError();
|
||||||
|
|
||||||
|
throw new HiScoresError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,9 +100,9 @@ export async function getRSNFormat(
|
|||||||
if (anchor) {
|
if (anchor) {
|
||||||
return rsnFromElement(anchor);
|
return rsnFromElement(anchor);
|
||||||
}
|
}
|
||||||
throw Error(PLAYER_NOT_FOUND_ERROR);
|
throw new PlayerNotFoundError();
|
||||||
} catch {
|
} catch {
|
||||||
throw Error(PLAYER_NOT_FOUND_ERROR);
|
throw new HiScoresError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +192,7 @@ export function parseStats(csv: string): Stats {
|
|||||||
splitCSV.length !==
|
splitCSV.length !==
|
||||||
SKILLS.length + BH_MODES.length + CLUES.length + BOSSES.length + 5
|
SKILLS.length + BH_MODES.length + CLUES.length + BOSSES.length + 5
|
||||||
) {
|
) {
|
||||||
throw Error(INVALID_FORMAT_ERROR);
|
throw new InvalidFormatError();
|
||||||
}
|
}
|
||||||
|
|
||||||
const skillObjects: Skill[] = splitCSV
|
const skillObjects: Skill[] = splitCSV
|
||||||
@@ -284,91 +285,77 @@ export async function getStats(
|
|||||||
];
|
];
|
||||||
const shouldGetFormattedRsn = options?.shouldGetFormattedRsn ?? true;
|
const shouldGetFormattedRsn = options?.shouldGetFormattedRsn ?? true;
|
||||||
|
|
||||||
const mainRes = await httpGet<HiscoresResponse>(
|
const main = await getOfficialStats(rsn, 'main', options?.axiosConfigs?.main);
|
||||||
getStatsURL('main', rsn, true),
|
|
||||||
options?.axiosConfigs?.main
|
const getModeStats = async (
|
||||||
);
|
mode: Extract<Gamemode, 'ironman' | 'hardcore' | 'ultimate'>
|
||||||
if (mainRes.status === 200) {
|
): Promise<HiscoresResponse | undefined> =>
|
||||||
const emptyResponse: AxiosResponse<HiscoresResponse> = {
|
otherGamemodes.includes(mode)
|
||||||
status: 404,
|
? getOfficialStats(rsn, mode, options?.axiosConfigs?.[mode])
|
||||||
data: { skills: [], activities: [] },
|
.catch(() => undefined)
|
||||||
statusText: '',
|
|
||||||
headers: {},
|
|
||||||
config: {} as InternalAxiosRequestConfig
|
|
||||||
};
|
|
||||||
const getModeStats = async (
|
|
||||||
mode: Extract<Gamemode, 'ironman' | 'hardcore' | 'ultimate'>
|
|
||||||
): Promise<AxiosResponse<HiscoresResponse>> =>
|
|
||||||
otherGamemodes.includes(mode)
|
|
||||||
? httpGet<HiscoresResponse>(
|
|
||||||
getStatsURL(mode, rsn, true),
|
|
||||||
options?.axiosConfigs?.[mode]
|
|
||||||
).catch((err) => err)
|
|
||||||
: emptyResponse;
|
|
||||||
const formattedName = shouldGetFormattedRsn
|
|
||||||
? await getRSNFormat(rsn, options?.axiosConfigs?.rsn).catch(
|
|
||||||
() => undefined
|
|
||||||
)
|
|
||||||
: undefined;
|
: undefined;
|
||||||
|
const formattedName = shouldGetFormattedRsn
|
||||||
|
? await getRSNFormat(rsn, options?.axiosConfigs?.rsn).catch(
|
||||||
|
() => undefined
|
||||||
|
)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const player: Player = {
|
const player: Player = {
|
||||||
name: formattedName ?? rsn,
|
name: formattedName ?? rsn,
|
||||||
mode: 'main',
|
mode: 'main',
|
||||||
dead: false,
|
dead: false,
|
||||||
deulted: false,
|
deulted: false,
|
||||||
deironed: false
|
deironed: false
|
||||||
};
|
};
|
||||||
player.main = parseJsonStats(mainRes.data);
|
player.main = parseJsonStats(main);
|
||||||
|
|
||||||
const ironRes = await getModeStats('ironman');
|
const iron = await getModeStats('ironman');
|
||||||
if (ironRes.status === 200) {
|
if (iron) {
|
||||||
player.ironman = parseJsonStats(ironRes.data);
|
player.ironman = parseJsonStats(iron);
|
||||||
const hcRes = await getModeStats('hardcore');
|
const hc = await getModeStats('hardcore');
|
||||||
const ultRes = await getModeStats('ultimate');
|
const ult = await getModeStats('ultimate');
|
||||||
if (hcRes.status === 200) {
|
if (hc) {
|
||||||
player.mode = 'hardcore';
|
player.mode = 'hardcore';
|
||||||
player.hardcore = parseJsonStats(hcRes.data);
|
player.hardcore = parseJsonStats(hc);
|
||||||
if (
|
if (
|
||||||
player.ironman.skills.overall.xp !== player.hardcore.skills.overall.xp
|
player.ironman.skills.overall.xp !== player.hardcore.skills.overall.xp
|
||||||
) {
|
) {
|
||||||
player.dead = true;
|
player.dead = true;
|
||||||
player.mode = 'ironman';
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
player.main.skills.overall.xp !== player.ironman.skills.overall.xp
|
|
||||||
) {
|
|
||||||
player.deironed = true;
|
|
||||||
player.mode = 'main';
|
|
||||||
}
|
|
||||||
} else if (ultRes.status === 200) {
|
|
||||||
player.mode = 'ultimate';
|
|
||||||
player.ultimate = parseJsonStats(ultRes.data);
|
|
||||||
if (
|
|
||||||
player.ironman.skills.overall.xp !== player.ultimate.skills.overall.xp
|
|
||||||
) {
|
|
||||||
player.deulted = true;
|
|
||||||
player.mode = 'ironman';
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
player.main.skills.overall.xp !== player.ironman.skills.overall.xp
|
|
||||||
) {
|
|
||||||
player.deironed = true;
|
|
||||||
player.mode = 'main';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
player.mode = 'ironman';
|
player.mode = 'ironman';
|
||||||
if (
|
}
|
||||||
|
if (
|
||||||
|
player.main.skills.overall.xp !== player.ironman.skills.overall.xp
|
||||||
|
) {
|
||||||
|
player.deironed = true;
|
||||||
|
player.mode = 'main';
|
||||||
|
}
|
||||||
|
} else if (ult) {
|
||||||
|
player.mode = 'ultimate';
|
||||||
|
player.ultimate = parseJsonStats(ult);
|
||||||
|
if (
|
||||||
|
player.ironman.skills.overall.xp !== player.ultimate.skills.overall.xp
|
||||||
|
) {
|
||||||
|
player.deulted = true;
|
||||||
|
player.mode = 'ironman';
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
player.main.skills.overall.xp !== player.ironman.skills.overall.xp
|
||||||
|
) {
|
||||||
|
player.deironed = true;
|
||||||
|
player.mode = 'main';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
player.mode = 'ironman';
|
||||||
|
if (
|
||||||
player.main.skills.overall.xp !== player.ironman.skills.overall.xp
|
player.main.skills.overall.xp !== player.ironman.skills.overall.xp
|
||||||
) {
|
) {
|
||||||
player.deironed = true;
|
player.deironed = true;
|
||||||
player.mode = 'main';
|
player.mode = 'main';
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return player;
|
|
||||||
}
|
}
|
||||||
throw Error(PLAYER_NOT_FOUND_ERROR);
|
|
||||||
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -282,3 +282,40 @@ export const FORMATTED_RIFTS_CLOSED = 'Rifts closed';
|
|||||||
|
|
||||||
export const INVALID_FORMAT_ERROR = 'Invalid hiscores format';
|
export const INVALID_FORMAT_ERROR = 'Invalid hiscores format';
|
||||||
export const PLAYER_NOT_FOUND_ERROR = 'Player not found';
|
export const PLAYER_NOT_FOUND_ERROR = 'Player not found';
|
||||||
|
export const HISCORES_ERROR = 'HiScores not responding';
|
||||||
|
|
||||||
|
export class InvalidFormatError extends Error {
|
||||||
|
__proto__ = Error;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(INVALID_FORMAT_ERROR);
|
||||||
|
Object.setPrototypeOf(this, InvalidFormatError.prototype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InvalidRSNError extends Error {
|
||||||
|
__proto__ = Error;
|
||||||
|
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
Object.setPrototypeOf(this, InvalidRSNError.prototype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PlayerNotFoundError extends Error {
|
||||||
|
__proto__ = Error;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(PLAYER_NOT_FOUND_ERROR);
|
||||||
|
Object.setPrototypeOf(this, PlayerNotFoundError.prototype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HiScoresError extends Error {
|
||||||
|
__proto__ = Error;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(HISCORES_ERROR);
|
||||||
|
Object.setPrototypeOf(this, HiScoresError.prototype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import {
|
|||||||
SCORES_URL,
|
SCORES_URL,
|
||||||
SKILLS,
|
SKILLS,
|
||||||
ACTIVITIES,
|
ACTIVITIES,
|
||||||
JSON_STATS_URL
|
JSON_STATS_URL,
|
||||||
|
InvalidRSNError
|
||||||
} from './constants';
|
} from './constants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -119,10 +120,10 @@ export const httpGet = <Response>(
|
|||||||
*/
|
*/
|
||||||
export const validateRSN = (rsn: string) => {
|
export const validateRSN = (rsn: string) => {
|
||||||
if (typeof rsn !== 'string') {
|
if (typeof rsn !== 'string') {
|
||||||
throw Error('RSN must be a string');
|
throw new InvalidRSNError('RSN must be a string');
|
||||||
} else if (!/^[a-zA-Z0-9 _-]+$/.test(rsn)) {
|
} else if (!/^[a-zA-Z0-9 _-]+$/.test(rsn)) {
|
||||||
throw Error('RSN contains invalid character');
|
throw new InvalidRSNError('RSN contains invalid character');
|
||||||
} else if (rsn.length > 12 || rsn.length < 1) {
|
} else if (rsn.length > 12 || rsn.length < 1) {
|
||||||
throw Error('RSN must be between 1 and 12 characters');
|
throw new InvalidRSNError('RSN must be between 1 and 12 characters');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user