mirror of
https://github.com/maxswa/osrs-json-hiscores.git
synced 2025-10-15 10:19:04 +00:00
Swap tslint for eslint.
This commit is contained in:
122
src/@types/useragent-generator.d.ts
vendored
122
src/@types/useragent-generator.d.ts
vendored
@@ -1,51 +1,109 @@
|
||||
declare module 'useragent-generator' {
|
||||
/********************
|
||||
/** *****************
|
||||
* Google Chrome *
|
||||
/*******************/
|
||||
export function chrome(opt: number | string | { version: string, os?: string }): string;
|
||||
/****************** */
|
||||
export function chrome(
|
||||
opt: number | string | { version: string; os?: string }
|
||||
): string;
|
||||
export namespace chrome {
|
||||
function androidPhone(opt: number | string | { version: string, androidVersion?: string, device?: string }): string;
|
||||
function androidTablet(opt: number | string | { version: string, androidVersion?: string, device?: string })
|
||||
: string;
|
||||
function androidWebview(opt: number | string | { androidVersion: string, chromeVersion?: string, device?: string })
|
||||
: string;
|
||||
function androidPhone(
|
||||
opt:
|
||||
| number
|
||||
| string
|
||||
| { version: string; androidVersion?: string; device?: string }
|
||||
): string;
|
||||
function androidTablet(
|
||||
opt:
|
||||
| number
|
||||
| string
|
||||
| { version: string; androidVersion?: string; device?: string }
|
||||
): string;
|
||||
function androidWebview(
|
||||
opt:
|
||||
| number
|
||||
| string
|
||||
| { androidVersion: string; chromeVersion?: string; device?: string }
|
||||
): string;
|
||||
function chromecast(opt: number | string | { version: string }): string;
|
||||
function iOS(opt: number | string | { iOSVersion: string, chromeVersion?: string, device?: string }): string;
|
||||
function iOS(
|
||||
opt:
|
||||
| number
|
||||
| string
|
||||
| { iOSVersion: string; chromeVersion?: string; device?: string }
|
||||
): string;
|
||||
}
|
||||
export function chromium(opt: number | string | { version: string, os?: string }): string;
|
||||
/***************
|
||||
export function chromium(
|
||||
opt: number | string | { version: string; os?: string }
|
||||
): string;
|
||||
/** ************
|
||||
* Firefox *
|
||||
/*************/
|
||||
export function firefox(opt: number | string | { version: string, os?: string }): string;
|
||||
/************* */
|
||||
export function firefox(
|
||||
opt: number | string | { version: string; os?: string }
|
||||
): string;
|
||||
export namespace firefox {
|
||||
function androidPhone(opt: number | string | { version: string, androidVersion?: string, device?: string }): string;
|
||||
function androidTablet(opt: number | string | { version: string, androidVersion?: string, device?: string })
|
||||
: string;
|
||||
function iOS(opt: number | string | { iOSVersion: string, device?: string }): string;
|
||||
function androidPhone(
|
||||
opt:
|
||||
| number
|
||||
| string
|
||||
| { version: string; androidVersion?: string; device?: string }
|
||||
): string;
|
||||
function androidTablet(
|
||||
opt:
|
||||
| number
|
||||
| string
|
||||
| { version: string; androidVersion?: string; device?: string }
|
||||
): string;
|
||||
function iOS(
|
||||
opt: number | string | { iOSVersion: string; device?: string }
|
||||
): string;
|
||||
}
|
||||
/**************
|
||||
/** ***********
|
||||
* Safari *
|
||||
/************/
|
||||
export function safari(opt: number | string | { version: string, os?: string }): string;
|
||||
/************ */
|
||||
export function safari(
|
||||
opt: number | string | { version: string; os?: string }
|
||||
): string;
|
||||
export namespace safari {
|
||||
function iOS(opt: number | string | { iOSVersion: string, safariVersion?: string, device?: string }): string;
|
||||
function iOSWebview(opt: number | string | { iOSVersion: string, safariVersion?: string, device?: string }): string;
|
||||
function iOS(
|
||||
opt:
|
||||
| number
|
||||
| string
|
||||
| { iOSVersion: string; safariVersion?: string; device?: string }
|
||||
): string;
|
||||
function iOSWebview(
|
||||
opt:
|
||||
| number
|
||||
| string
|
||||
| { iOSVersion: string; safariVersion?: string; device?: string }
|
||||
): string;
|
||||
}
|
||||
/***********************
|
||||
/** ********************
|
||||
* Internet Explorer *
|
||||
/*********************/
|
||||
export function ie(opt: number | string | { version: string, os?: string }): string;
|
||||
/********************* */
|
||||
export function ie(
|
||||
opt: number | string | { version: string; os?: string }
|
||||
): string;
|
||||
export namespace ie {
|
||||
function windowsPhone(opt: number | string | { version: string, device?: string }): string;
|
||||
function windowsPhone(
|
||||
opt: number | string | { version: string; device?: string }
|
||||
): string;
|
||||
}
|
||||
/**********************
|
||||
/** *******************
|
||||
* Microsoft Edge *
|
||||
/********************/
|
||||
export function edge(opt: number | string | { version: string, chromeVersion?: string, os?: string }): string;
|
||||
/************************
|
||||
/******************** */
|
||||
export function edge(
|
||||
opt:
|
||||
| number
|
||||
| string
|
||||
| { version: string; chromeVersion?: string; os?: string }
|
||||
): string;
|
||||
/** *********************
|
||||
* Search Engine Bots *
|
||||
/**********************/
|
||||
export function googleBot(opt?: number | string | { version?: string }): string;
|
||||
/********************** */
|
||||
export function googleBot(
|
||||
opt?: number | string | { version?: string }
|
||||
): string;
|
||||
export function bingBot(opt?: number | string | { version?: string }): string;
|
||||
export function yahooBot(): string;
|
||||
}
|
||||
|
||||
222
src/hiscores.ts
222
src/hiscores.ts
@@ -1,3 +1,4 @@
|
||||
import { JSDOM } from 'jsdom';
|
||||
import {
|
||||
Player,
|
||||
Activity,
|
||||
@@ -11,7 +12,7 @@ import {
|
||||
PlayerSkillRow,
|
||||
ActivityName,
|
||||
PlayerActivityRow,
|
||||
Bosses,
|
||||
Bosses
|
||||
} from './types';
|
||||
import {
|
||||
getStatsURL,
|
||||
@@ -26,9 +27,106 @@ import {
|
||||
rsnFromElement,
|
||||
getActivityPageURL,
|
||||
httpGet,
|
||||
BOSSES,
|
||||
BOSSES
|
||||
} from './utils';
|
||||
import { JSDOM } from 'jsdom';
|
||||
|
||||
export async function getRSNFormat(rsn: string): Promise<string> {
|
||||
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');
|
||||
}
|
||||
|
||||
const url = getPlayerTableURL('main', rsn);
|
||||
try {
|
||||
const response = await httpGet(url);
|
||||
const dom = new JSDOM(response.data);
|
||||
const spans = dom.window.document.querySelectorAll(
|
||||
'span[style="color:#AA0022;"]'
|
||||
);
|
||||
if (spans.length >= 2) {
|
||||
const nameSpan = spans[1];
|
||||
return rsnFromElement(nameSpan);
|
||||
}
|
||||
throw Error('Player not found');
|
||||
} catch {
|
||||
throw Error('Player not found');
|
||||
}
|
||||
}
|
||||
|
||||
export function 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 [rank, level, xp] = stat;
|
||||
const skill: Skill = {
|
||||
rank: parseInt(rank, 10),
|
||||
level: parseInt(level, 10),
|
||||
xp: parseInt(xp, 10)
|
||||
};
|
||||
return skill;
|
||||
});
|
||||
|
||||
const activityObjects: Activity[] = splitCSV
|
||||
.filter((stat) => stat.length === 2)
|
||||
.map((stat) => {
|
||||
const [rank, score] = stat;
|
||||
const activity: Activity = {
|
||||
rank: parseInt(rank, 10),
|
||||
score: parseInt(score, 10)
|
||||
};
|
||||
return activity;
|
||||
});
|
||||
|
||||
const [leaguePoints] = activityObjects.splice(0, 1);
|
||||
const bhObjects = activityObjects.splice(0, BH_MODES.length);
|
||||
const clueObjects = activityObjects.splice(0, CLUES.length);
|
||||
const [lastManStanding, soulWarsZeal] = activityObjects.splice(0, 2);
|
||||
const bossObjects = activityObjects.splice(0, BOSSES.length);
|
||||
|
||||
const skills: Skills = skillObjects.reduce<Skills>((prev, curr, index) => {
|
||||
const newSkills = { ...prev };
|
||||
newSkills[SKILLS[index]] = curr;
|
||||
return newSkills;
|
||||
}, {} as Skills);
|
||||
|
||||
const bountyHunter: BH = bhObjects.reduce<BH>((prev, curr, index) => {
|
||||
const newBH = { ...prev };
|
||||
newBH[BH_MODES[index]] = curr;
|
||||
return newBH;
|
||||
}, {} as BH);
|
||||
|
||||
const clues: Clues = clueObjects.reduce<Clues>((prev, curr, index) => {
|
||||
const newClues = { ...prev };
|
||||
newClues[CLUES[index]] = curr;
|
||||
return newClues;
|
||||
}, {} as Clues);
|
||||
|
||||
const bosses: Bosses = bossObjects.reduce<Bosses>((prev, curr, index) => {
|
||||
const newBosses = { ...prev };
|
||||
newBosses[BOSSES[index]] = curr;
|
||||
return newBosses;
|
||||
}, {} as Bosses);
|
||||
|
||||
const stats: Stats = {
|
||||
skills,
|
||||
leaguePoints,
|
||||
bountyHunter,
|
||||
lastManStanding,
|
||||
soulWarsZeal,
|
||||
clues,
|
||||
bosses
|
||||
};
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
export async function getStats(rsn: string): Promise<Player> {
|
||||
if (typeof rsn !== 'string') {
|
||||
@@ -42,20 +140,20 @@ export async function getStats(rsn: string): Promise<Player> {
|
||||
const mainRes = await httpGet(getStatsURL('main', rsn));
|
||||
if (mainRes.status === 200) {
|
||||
const otherResponses = await Promise.all([
|
||||
httpGet(getStatsURL('ironman', rsn)).catch(err => err),
|
||||
httpGet(getStatsURL('hardcore', rsn)).catch(err => err),
|
||||
httpGet(getStatsURL('ultimate', rsn)).catch(err => err),
|
||||
getRSNFormat(rsn).catch(() => undefined),
|
||||
httpGet(getStatsURL('ironman', rsn)).catch((err) => err),
|
||||
httpGet(getStatsURL('hardcore', rsn)).catch((err) => err),
|
||||
httpGet(getStatsURL('ultimate', rsn)).catch((err) => err),
|
||||
getRSNFormat(rsn).catch(() => undefined)
|
||||
]);
|
||||
|
||||
const [ironRes, hcRes, ultRes, formattedName] = otherResponses;
|
||||
|
||||
const player: Player = {
|
||||
name: formattedName || rsn,
|
||||
name: formattedName ?? rsn,
|
||||
mode: 'main',
|
||||
dead: false,
|
||||
deulted: false,
|
||||
deironed: false,
|
||||
deironed: false
|
||||
};
|
||||
player.main = parseStats(mainRes.data);
|
||||
|
||||
@@ -150,7 +248,7 @@ export async function getSkillPage(
|
||||
);
|
||||
|
||||
const players: PlayerSkillRow[] = [];
|
||||
playersHTML.forEach(row => {
|
||||
playersHTML.forEach((row) => {
|
||||
const rankEl = row.querySelector('td');
|
||||
const nameEl = row.querySelector('td a');
|
||||
const levelEl = row.querySelector('td.left + td');
|
||||
@@ -162,7 +260,7 @@ export async function getSkillPage(
|
||||
rank: numberFromElement(rankEl),
|
||||
level: numberFromElement(levelEl),
|
||||
xp: numberFromElement(xpEl),
|
||||
dead: isDead,
|
||||
dead: isDead
|
||||
});
|
||||
});
|
||||
|
||||
@@ -190,7 +288,7 @@ export async function getActivityPage(
|
||||
);
|
||||
|
||||
const players: PlayerActivityRow[] = [];
|
||||
playersHTML.forEach(row => {
|
||||
playersHTML.forEach((row) => {
|
||||
const rankEl = row.querySelector('td');
|
||||
const nameEl = row.querySelector('td a');
|
||||
const scoreEl = row.querySelector('td.left + td');
|
||||
@@ -200,107 +298,9 @@ export async function getActivityPage(
|
||||
name: rsnFromElement(nameEl),
|
||||
rank: numberFromElement(rankEl),
|
||||
score: numberFromElement(scoreEl),
|
||||
dead: isDead,
|
||||
dead: isDead
|
||||
});
|
||||
});
|
||||
|
||||
return players;
|
||||
}
|
||||
|
||||
export async function getRSNFormat(rsn: string): Promise<string> {
|
||||
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');
|
||||
}
|
||||
|
||||
const url = getPlayerTableURL('main', rsn);
|
||||
try {
|
||||
const response = await httpGet(url);
|
||||
const dom = new JSDOM(response.data);
|
||||
const spans = dom.window.document.querySelectorAll(
|
||||
'span[style="color:#AA0022;"]'
|
||||
);
|
||||
if (spans.length >= 2) {
|
||||
const nameSpan = spans[1];
|
||||
return rsnFromElement(nameSpan);
|
||||
}
|
||||
throw Error('Player not found');
|
||||
} catch {
|
||||
throw Error('Player not found');
|
||||
}
|
||||
}
|
||||
|
||||
export function 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 [rank, level, xp] = stat;
|
||||
const skill: Skill = {
|
||||
rank: parseInt(rank, 10),
|
||||
level: parseInt(level, 10),
|
||||
xp: parseInt(xp, 10),
|
||||
};
|
||||
return skill;
|
||||
});
|
||||
|
||||
const activityObjects: Activity[] = splitCSV
|
||||
.filter(stat => stat.length === 2)
|
||||
.map(stat => {
|
||||
const [rank, score] = stat;
|
||||
const activity: Activity = {
|
||||
rank: parseInt(rank, 10),
|
||||
score: parseInt(score, 10),
|
||||
};
|
||||
return activity;
|
||||
});
|
||||
|
||||
const [leaguePoints] = activityObjects.splice(0, 1);
|
||||
const bhObjects = activityObjects.splice(0, BH_MODES.length);
|
||||
const clueObjects = activityObjects.splice(0, CLUES.length);
|
||||
const [lastManStanding, soulWarsZeal] = activityObjects.splice(0, 2);
|
||||
const bossObjects = activityObjects.splice(0, BOSSES.length);
|
||||
|
||||
const skills: Skills = skillObjects.reduce<Skills>((prev, curr, index) => {
|
||||
const newSkills = { ...prev };
|
||||
newSkills[SKILLS[index]] = curr;
|
||||
return newSkills;
|
||||
}, {} as Skills);
|
||||
|
||||
const bountyHunter: BH = bhObjects.reduce<BH>((prev, curr, index) => {
|
||||
const newBH = { ...prev };
|
||||
newBH[BH_MODES[index]] = curr;
|
||||
return newBH;
|
||||
}, {} as BH);
|
||||
|
||||
const clues: Clues = clueObjects.reduce<Clues>((prev, curr, index) => {
|
||||
const newClues = { ...prev };
|
||||
newClues[CLUES[index]] = curr;
|
||||
return newClues;
|
||||
}, {} as Clues);
|
||||
|
||||
const bosses: Bosses = bossObjects.reduce<Bosses>((prev, curr, index) => {
|
||||
const newBosses = { ...prev };
|
||||
newBosses[BOSSES[index]] = curr;
|
||||
return newBosses;
|
||||
}, {} as Bosses);
|
||||
|
||||
const stats: Stats = {
|
||||
skills,
|
||||
leaguePoints,
|
||||
bountyHunter,
|
||||
lastManStanding,
|
||||
soulWarsZeal,
|
||||
clues,
|
||||
bosses,
|
||||
};
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
ClueType,
|
||||
Gamemode,
|
||||
SkillName,
|
||||
ActivityName,
|
||||
ActivityName
|
||||
} from '../types';
|
||||
|
||||
export const BASE_URL = 'https://secure.runescape.com/m=hiscore_oldschool';
|
||||
@@ -22,7 +22,7 @@ export const GAMEMODE_URL: GamemodeUrl = {
|
||||
ultimate: `${BASE_URL}_ultimate/`,
|
||||
deadman: `${BASE_URL}_deadman/`,
|
||||
seasonal: `${BASE_URL}_seasonal/`,
|
||||
tournament: `${BASE_URL}_tournament/`,
|
||||
tournament: `${BASE_URL}_tournament/`
|
||||
};
|
||||
export const SKILLS: SkillName[] = [
|
||||
'overall',
|
||||
@@ -48,7 +48,7 @@ export const SKILLS: SkillName[] = [
|
||||
'farming',
|
||||
'runecraft',
|
||||
'hunter',
|
||||
'construction',
|
||||
'construction'
|
||||
];
|
||||
export const CLUES: ClueType[] = [
|
||||
'all',
|
||||
@@ -57,7 +57,7 @@ export const CLUES: ClueType[] = [
|
||||
'medium',
|
||||
'hard',
|
||||
'elite',
|
||||
'master',
|
||||
'master'
|
||||
];
|
||||
export const BH_MODES: BHType[] = ['hunter', 'rogue'];
|
||||
export const GAMEMODES: Gamemode[] = [
|
||||
@@ -67,7 +67,7 @@ export const GAMEMODES: Gamemode[] = [
|
||||
'ultimate',
|
||||
'deadman',
|
||||
'seasonal',
|
||||
'tournament',
|
||||
'tournament'
|
||||
];
|
||||
export const BOSSES: Boss[] = [
|
||||
'abyssalSire',
|
||||
@@ -114,7 +114,7 @@ export const BOSSES: Boss[] = [
|
||||
'vorkath',
|
||||
'wintertodt',
|
||||
'zalcano',
|
||||
'zulrah',
|
||||
'zulrah'
|
||||
];
|
||||
export const ACTIVITIES: ActivityName[] = [
|
||||
'leaguePoints',
|
||||
@@ -129,7 +129,7 @@ export const ACTIVITIES: ActivityName[] = [
|
||||
'masterClues',
|
||||
'lastManStanding',
|
||||
'soulWarsZeal',
|
||||
...BOSSES,
|
||||
...BOSSES
|
||||
];
|
||||
|
||||
export type FormattedBossNames = {
|
||||
@@ -181,7 +181,7 @@ export const FORMATTED_BOSS_NAMES: FormattedBossNames = {
|
||||
vorkath: 'Vorkath',
|
||||
wintertodt: 'Wintertodt',
|
||||
zalcano: 'Zalcano',
|
||||
zulrah: 'Zulrah',
|
||||
zulrah: 'Zulrah'
|
||||
};
|
||||
|
||||
export type FormattedSkillNames = {
|
||||
@@ -212,7 +212,7 @@ export const FORMATTED_SKILL_NAMES: FormattedSkillNames = {
|
||||
farming: 'Farming',
|
||||
runecraft: 'Runecraft',
|
||||
hunter: 'Hunter',
|
||||
construction: 'Construction',
|
||||
construction: 'Construction'
|
||||
};
|
||||
|
||||
export type FormattedClueNames = {
|
||||
@@ -226,7 +226,7 @@ export const FORMATTED_CLUE_NAMES: FormattedClueNames = {
|
||||
medium: 'Clue Scrolls (medium)',
|
||||
hard: 'Clue Scrolls (hard)',
|
||||
elite: 'Clue Scrolls (elite)',
|
||||
master: 'Clue Scrolls (master)',
|
||||
master: 'Clue Scrolls (master)'
|
||||
};
|
||||
|
||||
export type FormattedBHNames = {
|
||||
@@ -235,7 +235,7 @@ export type FormattedBHNames = {
|
||||
|
||||
export const FORMATTED_BH_NAMES: FormattedBHNames = {
|
||||
rogue: 'Bounty Hunter - Rogue',
|
||||
hunter: 'Bounty Hunter - Hunter',
|
||||
hunter: 'Bounty Hunter - Hunter'
|
||||
};
|
||||
|
||||
export const FORMATTED_LMS = 'Last Man Standing';
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
STATS_URL,
|
||||
SCORES_URL,
|
||||
SKILLS,
|
||||
ACTIVITIES,
|
||||
ACTIVITIES
|
||||
} from './constants';
|
||||
|
||||
export const getStatsURL = (gamemode: Gamemode, rsn: string) =>
|
||||
@@ -48,11 +48,10 @@ export const rsnFromElement = (el: Element | null) => {
|
||||
return innerHTML?.replace(/\uFFFD/g, ' ') || '';
|
||||
};
|
||||
|
||||
export const httpGet = (url: string) => {
|
||||
return axios.get(url, {
|
||||
export const httpGet = (url: string) =>
|
||||
axios.get(url, {
|
||||
headers: {
|
||||
// without User-Agent header requests may be rejected by DDoS protection mechanism
|
||||
'User-Agent': ua.firefox(80)
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user