Compare commits

..

1 Commits

Author SHA1 Message Date
maxswa
e77737f422 Add Guardians of the Rift. 2022-04-18 15:44:45 -04:00
18 changed files with 6167 additions and 7160 deletions

View File

@@ -10,19 +10,19 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3.3.0 - uses: actions/checkout@v2
- uses: borales/actions-yarn@v4.2.0 - uses: borales/actions-yarn@v2.0.0
with: with:
cmd: install cmd: install
- uses: borales/actions-yarn@v4.2.0 - uses: borales/actions-yarn@v2.0.0
with: with:
cmd: lint cmd: lint
- uses: borales/actions-yarn@v4.2.0 - uses: borales/actions-yarn@v2.0.0
with: with:
cmd: format cmd: format
- uses: borales/actions-yarn@v4.2.0 - uses: borales/actions-yarn@v2.0.0
with: with:
cmd: build cmd: build
- uses: borales/actions-yarn@v4.2.0 - uses: borales/actions-yarn@v2.0.0
with: with:
cmd: test cmd: test

1
.gitignore vendored
View File

@@ -1,4 +1,3 @@
.idea/ .idea/
node_modules node_modules
/lib /lib
.vscode

View File

@@ -3,26 +3,24 @@
[![npm](https://img.shields.io/npm/v/osrs-json-hiscores.svg?style=flat-square)](https://www.npmjs.com/package/osrs-json-hiscores) [![npm](https://img.shields.io/npm/v/osrs-json-hiscores.svg?style=flat-square)](https://www.npmjs.com/package/osrs-json-hiscores)
[![downloads](https://img.shields.io/npm/dm/osrs-json-hiscores.svg?style=flat-square)](https://npm-stat.com/charts.html?package=osrs-json-hiscores) [![downloads](https://img.shields.io/npm/dm/osrs-json-hiscores.svg?style=flat-square)](https://npm-stat.com/charts.html?package=osrs-json-hiscores)
[![types](https://img.shields.io/npm/types/osrs-json-hiscores.svg?style=flat-square)](https://github.com/maxswa/osrs-json-hiscores/blob/master/src/types.ts) [![types](https://img.shields.io/npm/types/osrs-json-hiscores.svg?style=flat-square)](https://github.com/maxswa/osrs-json-hiscores/blob/master/src/types.ts)
[![build](https://img.shields.io/github/actions/workflow/status/maxswa/osrs-json-hiscores/main.yml?style=flat-square&branch=main)](https://github.com/maxswa/osrs-json-hiscores/actions/workflows/main.yml?query=branch%3Amain) [![build](https://img.shields.io/github/workflow/status/maxswa/osrs-json-hiscores/CI?style=flat-square)](https://github.com/maxswa/osrs-json-hiscores/actions/workflows/main.yml?query=branch%3Amain)
**The Old School RuneScape API wrapper that does more!** **The Old School Runescape API wrapper that does more!**
## What it does ## What it does
The official hiscores API for Old School RuneScape (OSRS) can return CSV or a simple JSON array. The official hiscores API for Old School Runescape (OSRS) returns CSV.
This wrapper converts the hiscores data into a more usable JSON object and provides extra information about the given player. By comparing player info it infers the player's game mode, as well as any previous modes (de-ultimated, de-ironed and/or died as a hardcore ironman). This wrapper converts it to json and provides extra information about the given player. By comparing player info it infers the player's game mode, as well as any previous modes (de-ultimated, de-ironed and/or died as a hardcore ironman).
Additional functions are provided that screen-scrape the OSRS leaderboards and return a list of players as json. Also simple utility functions are provided to fetch the raw responses from Jagex's APIs, if desired. Additional functions are provided that screen-scrape the OSRS leaderboards and return a list of players as json.
`osrs-json-hiscores` has TypeScript support, with full definitions for all functions and custom data types. `osrs-json-hiscores` has TypeScript support, with full definitions for all functions and custom data types.
--- ---
### Disclaimer ### Disclaimer
Jagex does not provide `Access-Control-Allow-Origin` headers in their responses. This means that [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) will block all browser requests to their hiscores API. In order to get around this, osrs-json-hiscores needs to be installed on the server side and exposed to the front end via a simple API. Here is an example of this in use: [codesandbox.io/s/osrs-json-hiscores-demo](https://codesandbox.io/s/osrs-json-hiscores-demo-qz656) Jagex does not provide `Access-Control-Allow-Origin` headers in their responses. This means that [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) will block all browser requests to their hiscores API. In order to get around this, osrs-json-hiscores should be installed on the server side and exposed to the front end via a simple API. Here is an example of this in use: [codesandbox.io/s/osrs-json-hiscores-demo](https://codesandbox.io/s/osrs-json-hiscores-demo-qz656)
TLDR: You cannot use this library directly in your client side app e.g. React or Vue, you must set up a server which uses this lib internally and have your client fetch data from your server.
--- ---
@@ -44,14 +42,27 @@ $ yarn add osrs-json-hiscores
Install the package and then import it into your project: Install the package and then import it into your project:
```typescript ```javascript
import { getStatsByGamemode, getSkillPage } from 'osrs-json-hiscores'; const hiscores = require('osrs-json-hiscores');
``` ```
Once you import it you can call the functions asynchronously: Once you import it you can call the functions asynchronously:
```typescript ```javascript
const stats = await getStatsByGamemode('Lynx Titan'); hiscores
.getStats('Lynx Titan')
.then((res) => console.log(res))
.catch((err) => console.error(err));
```
If you are using TypeScript or transpiling your JS you can use ES6 syntax:
```javascript
import hiscores, { getSkillPage } from 'osrs-json-hiscores';
// ...
const stats = await hiscores.getStats('Lynx Titan');
const topPage = await getSkillPage('overall'); const topPage = await getSkillPage('overall');
``` ```
@@ -70,8 +81,9 @@ const topPage = await getSkillPage('overall');
`getSkillPage` and `getActivityPage` require a skill / activity and optionally a gamemode and page: `getSkillPage` and `getActivityPage` require a skill / activity and optionally a gamemode and page:
```typescript ```javascript
getSkillPage('attack', 'main', 1) hiscores
.getSkillPage('attack', 'main', 1)
.then((res) => console.log(res)) .then((res) => console.log(res))
.catch((err) => console.error(err)); .catch((err) => console.error(err));
``` ```
@@ -93,23 +105,18 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses
### Minigames ### Minigames
| Minigame | Param | | Minigame | Param |
| ------------------------------- | :---------------: | | ---------------------- | :---------------: |
| Bounty Hunter (Legacy - Rogue) | `rogueBH` | | Bounty Hunter (Rogue) | `rogueBH` |
| Bounty Hunter (Legacy - Hunter) | `hunterBH` | | Bounty Hunter (Hunter) | `hunterBH` |
| Bounty Hunter (Rogue) | `rogueBHV2` | | Last Man Standing | `lastManStanding` |
| Bounty Hunter (Hunter) | `hunterBHV2` |
| LMS - Rank | `lastManStanding` |
| PvP Arena - Rank | `pvpArena` |
| Soul Wars Zeal | `soulWarsZeal` | | Soul Wars Zeal | `soulWarsZeal` |
| Rifts closed | `riftsClosed` | | Rifts Closed | `riftsClosed` |
| Colosseum Glory | `colosseumGlory` |
### Points ### Leagues
| Activity | Param | | Activity | Param |
| -------------- | :-------------: | | ------------- | :------------: |
| League Points | `leaguePoints` | | League Points | `leaguePoints` |
| Deadman Points | `deadmanPoints` |
### Bosses ### Bosses
@@ -117,11 +124,9 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses
| --------------------------------- | :----------------------------: | | --------------------------------- | :----------------------------: |
| Abyssal Sire | `abyssalSire` | | Abyssal Sire | `abyssalSire` |
| Alchemical Hydra | `alchemicalHydra` | | Alchemical Hydra | `alchemicalHydra` |
| Artio | `artio` |
| Barrows Chests | `barrows` | | Barrows Chests | `barrows` |
| Bryophyta | `bryophyta` | | Bryophyta | `bryophyta` |
| Callisto | `callisto` | | Callisto | `callisto` |
| Calvar'ion | `calvarion` |
| Cerberus | `cerberus` | | Cerberus | `cerberus` |
| Chambers Of Xeric | `chambersOfXeric` | | Chambers Of Xeric | `chambersOfXeric` |
| Chambers Of Xeric: Challenge Mode | `chambersOfXericChallengeMode` | | Chambers Of Xeric: Challenge Mode | `chambersOfXericChallengeMode` |
@@ -134,7 +139,6 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses
| Dagannoth Rex | `dagannothRex` | | Dagannoth Rex | `dagannothRex` |
| Dagannoth Supreme | `dagannothSupreme` | | Dagannoth Supreme | `dagannothSupreme` |
| Deranged Archaeologist | `derangedArchaeologist` | | Deranged Archaeologist | `derangedArchaeologist` |
| Duke Sucellus | `dukeSucellus` |
| General Graardor | `generalGraardor` | | General Graardor | `generalGraardor` |
| Giant Mole | `giantMole` | | Giant Mole | `giantMole` |
| Grotesque Guardians | `grotesqueGuardians` | | Grotesque Guardians | `grotesqueGuardians` |
@@ -144,32 +148,22 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses
| Kraken | `kraken` | | Kraken | `kraken` |
| Kreearra | `kreeArra` | | Kreearra | `kreeArra` |
| K'ril Tsutsaroth | `krilTsutsaroth` | | K'ril Tsutsaroth | `krilTsutsaroth` |
| Lunar Chests | `lunarChests` |
| Mimic | `mimic` | | Mimic | `mimic` |
| Nex | `nex` | | Nex | `nex` |
| Nightmare | `nightmare` | | The Nightmare of Ashihama | `nightmare` |
| Phosani's Nightmare | `phosanisNightmare` | | Phosani's Nightmare | `phosanisNightmare` |
| Obor | `obor` | | Obor | `obor` |
| Phantom Muspah | `phantomMuspah` |
| Sarachnis | `sarachnis` | | Sarachnis | `sarachnis` |
| Scorpia | `scorpia` | | Scorpia | `scorpia` |
| Scurrius | `scurrius` |
| Skotizo | `skotizo` | | Skotizo | `skotizo` |
| Sol Heredit | `solHeredit` |
| Spindel | `spindel` |
| Tempoross | `tempoross` | | Tempoross | `tempoross` |
| The Gauntlet | `gauntlet` | | The Gauntlet | `gauntlet` |
| The Corrupted Gauntlet | `corruptedGauntlet` | | The Corrupted Gauntlet | `corruptedGauntlet` |
| The Leviathan | `leviathan` |
| The Whisperer | `whisperer` |
| Theatre Of Blood | `theatreOfBlood` | | Theatre Of Blood | `theatreOfBlood` |
| Theatre Of Blood: Hard Mode | `theatreOfBloodHardMode` | | Theatre Of Blood: Hard Mode | `theatreOfBloodHardMode` |
| Thermonuclear Smoke Devil | `thermonuclearSmokeDevil` | | Thermonuclear Smoke Devil | `thermonuclearSmokeDevil` |
| Tombs of Amascut | `tombsOfAmascut` |
| Tombs of Amascut: Expert Mode | `tombsOfAmascutExpertMode` |
| TzKal-Zuk | `tzKalZuk` | | TzKal-Zuk | `tzKalZuk` |
| TzTok-Jad | `tzTokJad` | | TzTok-Jad | `tzTokJad` |
| Vardorvis | `vardorvis` |
| Venenatis | `venenatis` | | Venenatis | `venenatis` |
| Vetion | `vetion` | | Vetion | `vetion` |
| Vorkath | `vorkath` | | Vorkath | `vorkath` |
@@ -181,7 +175,7 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses
`getStats` returns a player object that looks like this: `getStats` returns a player object that looks like this:
```typescript ```javascript
{ {
name: 'Lynx Titan', name: 'Lynx Titan',
mode: 'main', mode: 'main',
@@ -199,7 +193,6 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses
leaguePoints: {}, leaguePoints: {},
bountyHunter: {}, bountyHunter: {},
lastManStanding: {}, lastManStanding: {},
pvpArena: {},
soulWarsZeal: {}, soulWarsZeal: {},
riftsClosed: {}, riftsClosed: {},
bosses: {} bosses: {}
@@ -209,7 +202,7 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses
`getSkillPage` returns and array of 25 players (This represents a page on the hiscores): `getSkillPage` returns and array of 25 players (This represents a page on the hiscores):
```typescript ```javascript
[ [
{ rank: 1, name: 'Lynx Titan', level: 2277, xp: 4600000000, dead: false }, { rank: 1, name: 'Lynx Titan', level: 2277, xp: 4600000000, dead: false },
{}, {},
@@ -222,7 +215,7 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses
Get the properly formatted name of any skill, boss, clue or other activity: Get the properly formatted name of any skill, boss, clue or other activity:
```typescript ```javascript
// kril === "K'ril Tsutsaroth" // kril === "K'ril Tsutsaroth"
const kril = FORMATTED_BOSS_NAMES['krilTsutsaroth']; const kril = FORMATTED_BOSS_NAMES['krilTsutsaroth'];
``` ```

View File

@@ -1,87 +1,41 @@
<!doctype html> <!DOCTYPE html>
<!--[if lt IE 7]><html class="no-js lt-ie10 lt-ie9 lt-ie8 lt-ie7" lang="en"><![endif]--> <!-- saved from url=(0079)https://secure.runescape.com/m=hiscore_oldschool/overall?table=1#headerHiscores -->
<!--[if (IE 7)&!(IEMobile)]><html class="no-js lt-ie10 lt-ie9 lt-ie8" lang="en"><![endif]--> <html class="js svg websockets localstorage websocketsbinary sharedworkers webworkers classlist canvas video webgl csspointerevents progressbar meter fileinput inlinesvg supports datalistelem no-touchevents details indexeddb indexeddb-deletedatabase appearance backgroundcliptext borderimage boxsizing cssfilters flexbox flexboxlegacy no-flexboxtweener webp webp-alpha webp-animation webp-lossless videoautoplay dataworkers blobworkers" lang="en" data-whatinput="mouse" data-whatintent="mouse"><!--<![endif]--><head><meta http-equiv="Content-Type" content="text/html; charset=windows-1252"><script src="./attackTopPage_files/sdk.js" async="" crossorigin="anonymous"></script><script async="" src="./attackTopPage_files/gtm.js"></script><script src="./attackTopPage_files/Criciousand-meth-shake-Exit-be-till-in-ches-Shad" async=""></script>
<!--[if (IE 8)&!(IEMobile)]><html class="no-js lt-ie10 lt-ie9" lang="en"><![endif]--> <link href="./attackTopPage_files/rsp777_basic-101.css" rel="stylesheet" type="text/css" media="all">
<!--[if (IE 9)&!(IEMobile)]><html class="no-js lt-ie10" lang="en"><![endif]-->
<!--[if gt IE 9]><!--><!-- x -->
<html class="no-js" lang="en"> <!--<![endif]-->
<head> <script src="./attackTopPage_files/vendor-1.js"></script>
<link href="https://www.runescape.com/css/rsp777_basic-119.css" rel="stylesheet" type="text/css" media="all">
<script data-cookieconsent="ignore">
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag("consent", "default", {
ad_storage: "denied",
analytics_storage: "denied",
functionality_storage: "denied",
personalization_storage: "denied",
security_storage: "granted",
wait_for_update: 500,
});
gtag("set", "ads_data_redaction", true);
</script>
<script data-cookieconsent="ignore">
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag("consent", "default", {
ad_storage: "denied",
analytics_storage: "denied",
functionality_storage: "denied",
personalization_storage: "denied",
security_storage: "granted",
wait_for_update: 500,
});
gtag("set", "ads_data_redaction", true);
</script>
<script id="Cookiebot" src="https://consent.cookiebot.com/uc.js" data-cbid="9147fcf2-a466-4a5d-82ab-43e528833d80"
data-blockingmode="auto"></script>
<script src="https://www.runescape.com/js/c/responsive/vendor-151.js" data-cookieconsent="ignore"></script>
<title>Old School Hiscores</title> <title>Old School Hiscores</title>
<meta name="keywords" <meta name="keywords" content="MMORPG, free RPG games online, online multiplayer, role-playing, massively multiplayer games, mmo, RuneScape, Jagex">
content="MMORPG, free RPG games online, online multiplayer, role-playing, massively multiplayer games, mmo, RuneScape, Jagex" /> <meta name="description" content="If you&#39;re a RuneScape veteran hungry for nostalgia, get stuck right in to Old School RuneScape. Sign up for membership and re-live the adventure.">
<meta name="description"
content="If you're a RuneScape veteran hungry for nostalgia, get stuck right in to Old School RuneScape. Sign up for membership and re-live the adventure." />
<link rel="icon" type="image/vnd.microsoft.icon" href="https://www.runescape.com/img/global/favicon.ico" /> <link rel="icon" type="image/vnd.microsoft.icon" href="https://www.runescape.com/img/global/favicon.ico">
<link rel="SHORTCUT ICON" href="https://www.runescape.com/img/global/favicon.ico" /> <link rel="SHORTCUT ICON" href="https://www.runescape.com/img/global/favicon.ico">
<link rel="apple-touch-icon" href="https://www.runescape.com/img/global/mobile.png"> <link rel="apple-touch-icon" href="https://www.runescape.com/img/global/mobile.png">
<meta property="fb:app_id" content="174823375888281" /> <meta property="fb:app_id" content="174823375888281">
<meta property="og:title" content="Old School Hiscores" /> <meta property="og:title" content="Old School Hiscores">
<meta property="og:type" content="website" /> <meta property="og:type" content="website">
<meta property="og:site_name" content="Old School RuneScape" /> <meta property="og:site_name" content="Old School RuneScape">
<meta property="og:image" content="https://www.runescape.com/img/rsp777/social-share-fb.jpg?1" /> <meta property="og:image" content="https://www.runescape.com/img/rsp777/social-share-fb.jpg?1">
<meta property="og:url" content="https://www.runescape.com" /> <meta property="og:url" content="https://www.runescape.com">
<meta property="og:description" <meta property="og:description" content="If you&#39;re a RuneScape veteran hungry for nostalgia, get stuck right in to Old School RuneScape. Sign up for membership and re-live the adventure.">
content="If you're a RuneScape veteran hungry for nostalgia, get stuck right in to Old School RuneScape. Sign up for membership and re-live the adventure." />
<meta name="google-site-verification" content="RFtGg-G07XweT7nQe032HocvnRPhJAsdZqrq4NX_Op8"> <meta name="google-site-verification" content="RFtGg-G07XweT7nQe032HocvnRPhJAsdZqrq4NX_Op8">
<meta itemprop="name" content="RuneScape"> <meta itemprop="name" content="RuneScape">
<meta itemprop="description" <meta itemprop="description" content="If you&#39;re a RuneScape veteran hungry for nostalgia, get stuck right in to Old School RuneScape. Sign up for membership and re-live the adventure.">
content="If you're a RuneScape veteran hungry for nostalgia, get stuck right in to Old School RuneScape. Sign up for membership and re-live the adventure.">
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:domain" content="Oldschool.RuneScape.com"> <meta name="twitter:domain" content="Oldschool.RuneScape.com">
<meta name="twitter:url" content="https://www.runescape.com"> <meta name="twitter:url" content="https://www.runescape.com">
<meta name="twitter:title" content="Old School Hiscores"> <meta name="twitter:title" content="Old School Hiscores">
<meta name="twitter:description" <meta name="twitter:description" content="If you&#39;re a RuneScape veteran hungry for nostalgia, get stuck right in to Old School RuneScape. Sign up for membership and re-live the adventure.">
content="If you're a RuneScape veteran hungry for nostalgia, get stuck right in to Old School RuneScape. Sign up for membership and re-live the adventure.">
<meta name="twitter:image:src" content="https://www.runescape.com/img/rsp777/social-share.jpg?1"> <meta name="twitter:image:src" content="https://www.runescape.com/img/rsp777/social-share.jpg?1">
<meta name="twitter:site" content="@OldSchoolRS"> <meta name="twitter:site" content="@OldSchoolRS">
<meta name='google-site-verification' content='pUwl5KYHKSZ6tbbgsR2wYNv1kbVmsYral3iFmijHTWM' /> <meta name="google-site-verification" content="pUwl5KYHKSZ6tbbgsR2wYNv1kbVmsYral3iFmijHTWM">
<link href="https://www.runescape.com/css/rsp777_hiscore-119.css" rel="stylesheet" type="text/css" media="all"> <link href="./attackTopPage_files/rsp777_hiscore-105.css" rel="stylesheet" type="text/css" media="all">
<style> <style>
.leaguepoints:before { .leaguepoints:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_leaguepoints.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_leaguepoints.png?2');
} }
.deadmanpoints:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_deadmanpoints.png?2');
}
.bountyhunterhunter:before { .bountyhunterhunter:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_bountyhunterhunter.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_bountyhunterhunter.png?2');
} }
@@ -90,14 +44,6 @@
content: url('https://www.runescape.com/img/rsp777/game_icon_bountyhunterrogue.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_bountyhunterrogue.png?2');
} }
.bountyhunterlegacyhunter:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_bountyhunterlegacyhunter.png?2');
}
.bountyhunterlegacyrogue:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_bountyhunterlegacyrogue.png?2');
}
.cluescrollsall:before { .cluescrollsall:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_cluescrollsall.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_cluescrollsall.png?2');
} }
@@ -130,18 +76,10 @@
content: url('https://www.runescape.com/img/rsp777/game_icon_lmsrank.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_lmsrank.png?2');
} }
.pvparenarank:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_pvparenarank.png?2');
}
.soulwarszeal:before { .soulwarszeal:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_soulwarszeal.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_soulwarszeal.png?2');
} }
.riftsclosed:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_riftsclosed.png?2');
}
.abyssalsire:before { .abyssalsire:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_abyssalsire.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_abyssalsire.png?2');
} }
@@ -150,10 +88,6 @@
content: url('https://www.runescape.com/img/rsp777/game_icon_alchemicalhydra.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_alchemicalhydra.png?2');
} }
.artio:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_artio.png?2');
}
.barrowschests:before { .barrowschests:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_barrowschests.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_barrowschests.png?2');
} }
@@ -166,10 +100,6 @@
content: url('https://www.runescape.com/img/rsp777/game_icon_callisto.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_callisto.png?2');
} }
.calvarion:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_calvarion.png?2');
}
.cerberus:before { .cerberus:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_cerberus.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_cerberus.png?2');
} }
@@ -218,10 +148,6 @@
content: url('https://www.runescape.com/img/rsp777/game_icon_derangedarchaeologist.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_derangedarchaeologist.png?2');
} }
.dukesucellus:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_dukesucellus.png?2');
}
.generalgraardor:before { .generalgraardor:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_generalgraardor.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_generalgraardor.png?2');
} }
@@ -262,26 +188,14 @@
content: url('https://www.runescape.com/img/rsp777/game_icon_mimic.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_mimic.png?2');
} }
.nex:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_nex.png?2');
}
.nightmare:before { .nightmare:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_nightmare.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_nightmare.png?2');
} }
.phosanisnightmare:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_phosanisnightmare.png?2');
}
.obor:before { .obor:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_obor.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_obor.png?2');
} }
.phantommuspah:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_phantommuspah.png?2');
}
.sarachnis:before { .sarachnis:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_sarachnis.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_sarachnis.png?2');
} }
@@ -294,10 +208,6 @@
content: url('https://www.runescape.com/img/rsp777/game_icon_skotizo.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_skotizo.png?2');
} }
.spindel:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_spindel.png?2');
}
.tempoross:before { .tempoross:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_tempoross.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_tempoross.png?2');
} }
@@ -310,34 +220,14 @@
content: url('https://www.runescape.com/img/rsp777/game_icon_thecorruptedgauntlet.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_thecorruptedgauntlet.png?2');
} }
.theleviathan:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_theleviathan.png?2');
}
.thewhisperer:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_thewhisperer.png?2');
}
.theatreofblood:before { .theatreofblood:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_theatreofblood.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_theatreofblood.png?2');
} }
.theatreofbloodhardmode:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_theatreofbloodhardmode.png?2');
}
.thermonuclearsmokedevil:before { .thermonuclearsmokedevil:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_thermonuclearsmokedevil.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_thermonuclearsmokedevil.png?2');
} }
.tombsofamascut:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_tombsofamascut.png?2');
}
.tombsofamascutexpertmode:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_tombsofamascutexpertmode.png?2');
}
.tzkalzuk:before { .tzkalzuk:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_tzkalzuk.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_tzkalzuk.png?2');
} }
@@ -346,10 +236,6 @@
content: url('https://www.runescape.com/img/rsp777/game_icon_tztokjad.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_tztokjad.png?2');
} }
.vardorvis:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_vardorvis.png?2');
}
.venenatis:before { .venenatis:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_venenatis.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_venenatis.png?2');
} }
@@ -373,282 +259,174 @@
.zulrah:before { .zulrah:before {
content: url('https://www.runescape.com/img/rsp777/game_icon_zulrah.png?2'); content: url('https://www.runescape.com/img/rsp777/game_icon_zulrah.png?2');
} }
</style>
</head>
<body id="body"> </style>
<style type="text/css" data-fbcssmodules="css:fb.css.base css:fb.css.dialog css:fb.css.iframewidget css:fb.css.customer_chat_plugin_iframe">.fb_hidden{position:absolute;top:-10000px;z-index:10001}.fb_reposition{overflow:hidden;position:relative}.fb_invisible{display:none}.fb_reset{background:none;border:0;border-spacing:0;color:#000;cursor:auto;direction:ltr;font-family:"lucida grande", tahoma, verdana, arial, sans-serif;font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:1;margin:0;overflow:visible;padding:0;text-align:left;text-decoration:none;text-indent:0;text-shadow:none;text-transform:none;visibility:visible;white-space:normal;word-spacing:normal}.fb_reset>div{overflow:hidden}@keyframes fb_transform{from{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.fb_animate{animation:fb_transform .3s forwards}
.fb_dialog{background:rgba(82, 82, 82, .7);position:absolute;top:-10000px;z-index:10001}.fb_dialog_advanced{border-radius:8px;padding:10px}.fb_dialog_content{background:#fff;color:#373737}.fb_dialog_close_icon{background:url(https://static.xx.fbcdn.net/rsrc.php/v3/yq/r/IE9JII6Z1Ys.png) no-repeat scroll 0 0 transparent;cursor:pointer;display:block;height:15px;position:absolute;right:18px;top:17px;width:15px}.fb_dialog_mobile .fb_dialog_close_icon{left:5px;right:auto;top:5px}.fb_dialog_padding{background-color:transparent;position:absolute;width:1px;z-index:-1}.fb_dialog_close_icon:hover{background:url(https://static.xx.fbcdn.net/rsrc.php/v3/yq/r/IE9JII6Z1Ys.png) no-repeat scroll 0 -15px transparent}.fb_dialog_close_icon:active{background:url(https://static.xx.fbcdn.net/rsrc.php/v3/yq/r/IE9JII6Z1Ys.png) no-repeat scroll 0 -30px transparent}.fb_dialog_iframe{line-height:0}.fb_dialog_content .dialog_title{background:#6d84b4;border:1px solid #365899;color:#fff;font-size:14px;font-weight:bold;margin:0}.fb_dialog_content .dialog_title>span{background:url(https://static.xx.fbcdn.net/rsrc.php/v3/yd/r/Cou7n-nqK52.gif) no-repeat 5px 50%;float:left;padding:5px 0 7px 26px}body.fb_hidden{height:100%;left:0;margin:0;overflow:visible;position:absolute;top:-10000px;transform:none;width:100%}.fb_dialog.fb_dialog_mobile.loading{background:url(https://static.xx.fbcdn.net/rsrc.php/v3/ya/r/3rhSv5V8j3o.gif) white no-repeat 50% 50%;min-height:100%;min-width:100%;overflow:hidden;position:absolute;top:0;z-index:10001}.fb_dialog.fb_dialog_mobile.loading.centered{background:none;height:auto;min-height:initial;min-width:initial;width:auto}.fb_dialog.fb_dialog_mobile.loading.centered #fb_dialog_loader_spinner{width:100%}.fb_dialog.fb_dialog_mobile.loading.centered .fb_dialog_content{background:none}.loading.centered #fb_dialog_loader_close{clear:both;color:#fff;display:block;font-size:18px;padding-top:20px}#fb-root #fb_dialog_ipad_overlay{background:rgba(0, 0, 0, .4);bottom:0;left:0;min-height:100%;position:absolute;right:0;top:0;width:100%;z-index:10000}#fb-root #fb_dialog_ipad_overlay.hidden{display:none}.fb_dialog.fb_dialog_mobile.loading iframe{visibility:hidden}.fb_dialog_mobile .fb_dialog_iframe{position:sticky;top:0}.fb_dialog_content .dialog_header{background:linear-gradient(from(#738aba), to(#2c4987));border-bottom:1px solid;border-color:#043b87;box-shadow:white 0 1px 1px -1px inset;color:#fff;font:bold 14px Helvetica, sans-serif;text-overflow:ellipsis;text-shadow:rgba(0, 30, 84, .296875) 0 -1px 0;vertical-align:middle;white-space:nowrap}.fb_dialog_content .dialog_header table{height:43px;width:100%}.fb_dialog_content .dialog_header td.header_left{font-size:12px;padding-left:5px;vertical-align:middle;width:60px}.fb_dialog_content .dialog_header td.header_right{font-size:12px;padding-right:5px;vertical-align:middle;width:60px}.fb_dialog_content .touchable_button{background:linear-gradient(from(#4267B2), to(#2a4887));background-clip:padding-box;border:1px solid #29487d;border-radius:3px;display:inline-block;line-height:18px;margin-top:3px;max-width:85px;padding:4px 12px;position:relative}.fb_dialog_content .dialog_header .touchable_button input{background:none;border:none;color:#fff;font:bold 12px Helvetica, sans-serif;margin:2px -12px;padding:2px 6px 3px 6px;text-shadow:rgba(0, 30, 84, .296875) 0 -1px 0}.fb_dialog_content .dialog_header .header_center{color:#fff;font-size:16px;font-weight:bold;line-height:18px;text-align:center;vertical-align:middle}.fb_dialog_content .dialog_content{background:url(https://static.xx.fbcdn.net/rsrc.php/v3/y9/r/jKEcVPZFk-2.gif) no-repeat 50% 50%;border:1px solid #4a4a4a;border-bottom:0;border-top:0;height:150px}.fb_dialog_content .dialog_footer{background:#f5f6f7;border:1px solid #4a4a4a;border-top-color:#ccc;height:40px}#fb_dialog_loader_close{float:left}.fb_dialog.fb_dialog_mobile .fb_dialog_close_icon{visibility:hidden}#fb_dialog_loader_spinner{animation:rotateSpinner 1.2s linear infinite;background-color:transparent;background-image:url(https://static.xx.fbcdn.net/rsrc.php/v3/yD/r/t-wz8gw1xG1.png);background-position:50% 50%;background-repeat:no-repeat;height:24px;width:24px}@keyframes rotateSpinner{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}
.fb_iframe_widget{display:inline-block;position:relative}.fb_iframe_widget span{display:inline-block;position:relative;text-align:justify}.fb_iframe_widget iframe{position:absolute}.fb_iframe_widget_fluid_desktop,.fb_iframe_widget_fluid_desktop span,.fb_iframe_widget_fluid_desktop iframe{max-width:100%}.fb_iframe_widget_fluid_desktop iframe{min-width:220px;position:relative}.fb_iframe_widget_lift{z-index:1}.fb_iframe_widget_fluid{display:inline}.fb_iframe_widget_fluid span{width:100%}
.fb_mpn_mobile_landing_page_slide_out{animation-duration:200ms;animation-name:fb_mpn_landing_page_slide_out;transition-timing-function:ease-in}.fb_mpn_mobile_landing_page_slide_out_from_left{animation-duration:200ms;animation-name:fb_mpn_landing_page_slide_out_from_left;transition-timing-function:ease-in}.fb_mpn_mobile_landing_page_slide_up{animation-duration:500ms;animation-name:fb_mpn_landing_page_slide_up;transition-timing-function:ease-in}.fb_mpn_mobile_bounce_in{animation-duration:300ms;animation-name:fb_mpn_bounce_in;transition-timing-function:ease-in}.fb_mpn_mobile_bounce_out{animation-duration:300ms;animation-name:fb_mpn_bounce_out;transition-timing-function:ease-in}.fb_mpn_mobile_bounce_out_v2{animation-duration:300ms;animation-name:fb_mpn_fade_out;transition-timing-function:ease-in}.fb_customer_chat_bounce_in_v2{animation-duration:300ms;animation-name:fb_bounce_in_v2;transition-timing-function:ease-in}.fb_customer_chat_bounce_in_from_left{animation-duration:300ms;animation-name:fb_bounce_in_from_left;transition-timing-function:ease-in}.fb_customer_chat_bounce_out_v2{animation-duration:300ms;animation-name:fb_bounce_out_v2;transition-timing-function:ease-in}.fb_customer_chat_bounce_out_from_left{animation-duration:300ms;animation-name:fb_bounce_out_from_left;transition-timing-function:ease-in}.fb_customer_chat_bubble_animated_no_badge{box-shadow:0 3px 12px rgba(0, 0, 0, .15);transition:box-shadow 150ms linear}.fb_customer_chat_bubble_animated_no_badge:hover{box-shadow:0 5px 24px rgba(0, 0, 0, .3)}.fb_customer_chat_bubble_animated_with_badge{box-shadow:-5px 4px 14px rgba(0, 0, 0, .15);transition:box-shadow 150ms linear}.fb_customer_chat_bubble_animated_with_badge:hover{box-shadow:-5px 8px 24px rgba(0, 0, 0, .2)}.fb_invisible_flow{display:inherit;height:0;overflow-x:hidden;width:0}.fb_new_ui_mobile_overlay_active{overflow:hidden}@keyframes fb_mpn_landing_page_slide_in{0%{border-radius:50%;margin:0 24px;width:60px}40%{border-radius:18px}100%{margin:0 12px;width:100% - 24px}}@keyframes fb_mpn_landing_page_slide_in_from_left{0%{border-radius:50%;left:12px;margin:0 24px;width:60px}40%{border-radius:18px}100%{left:12px;margin:0 12px;width:100% - 24px}}@keyframes fb_mpn_landing_page_slide_out{0%{margin:0 12px;width:100% - 24px}60%{border-radius:18px}100%{border-radius:50%;margin:0 24px;width:60px}}@keyframes fb_mpn_landing_page_slide_out_from_left{0%{left:12px;width:100% - 24px}60%{border-radius:18px}100%{border-radius:50%;left:12px;width:60px}}@keyframes fb_mpn_landing_page_slide_up{0%{bottom:0;opacity:0}100%{bottom:24px;opacity:1}}@keyframes fb_mpn_bounce_in{0%{opacity:.5;top:100%}100%{opacity:1;top:0}}@keyframes fb_mpn_fade_out{0%{bottom:30px;opacity:1}100%{bottom:0;opacity:0}}@keyframes fb_mpn_bounce_out{0%{opacity:1;top:0}100%{opacity:.5;top:100%}}@keyframes fb_bounce_in_v2{0%{opacity:0;transform:scale(0, 0);transform-origin:bottom right}50%{transform:scale(1.03, 1.03);transform-origin:bottom right}100%{opacity:1;transform:scale(1, 1);transform-origin:bottom right}}@keyframes fb_bounce_in_from_left{0%{opacity:0;transform:scale(0, 0);transform-origin:bottom left}50%{transform:scale(1.03, 1.03);transform-origin:bottom left}100%{opacity:1;transform:scale(1, 1);transform-origin:bottom left}}@keyframes fb_bounce_out_v2{0%{opacity:1;transform:scale(1, 1);transform-origin:bottom right}100%{opacity:0;transform:scale(0, 0);transform-origin:bottom right}}@keyframes fb_bounce_out_from_left{0%{opacity:1;transform:scale(1, 1);transform-origin:bottom left}100%{opacity:0;transform:scale(0, 0);transform-origin:bottom left}}@keyframes fb_bounce_out_v2_mobile_chat_started{0%{opacity:1;top:0}100%{opacity:0;top:20px}}@keyframes fb_customer_chat_bubble_bounce_in_animation{0%{bottom:6pt;opacity:0;transform:scale(0, 0);transform-origin:center}70%{bottom:18pt;opacity:1;transform:scale(1.2, 1.2)}100%{transform:scale(1, 1)}}@keyframes slideInFromBottom{0%{opacity:.1;transform:translateY(100%)}100%{opacity:1;transform:translateY(0)}}@keyframes slideInFromBottomDelay{0%{opacity:0;transform:translateY(100%)}97%{opacity:0;transform:translateY(100%)}100%{opacity:1;transform:translateY(0)}}</style></head>
<body id="body"><div id="fb-root" class="174823375888281 fb_reset"><script async="" src="./attackTopPage_files/sdk(1).js"></script><div style="position: absolute; top: -10000px; width: 0px; height: 0px;"><div></div></div></div>
<div class="centerDiv"> <div class="centerDiv">
<div class="frame wide_e"> <div class="frame wide_e">
<span style="float:right;"><a id="loginLink" <span style="float:right;"><a id="loginLink" href="https://secure.runescape.com/m=weblogin/loginform?theme=oldschool&amp;mod=hiscore_oldschool&amp;ssl=1&amp;dest=overall?table=1">Log in</a></span>
href="https://secure.runescape.com/m=weblogin/loginform?theme=oldschool&mod=hiscore_oldschool&amp;ssl=1&amp;dest=overall?table=1">Log
in</a></span>
</div> </div>
<br> <br>
<div class="hiscoretitlebground"> <div class="hiscoretitlebground">
<div class="hiscoretitleframe"> <div class="hiscoretitleframe">
<strong>Old School Hiscores</strong><br /> <strong>Old School Hiscores</strong><br>
<a href="https://oldschool.runescape.com/">Home</a> <a href="https://oldschool.runescape.com/">Home</a>
</div> </div>
</div> </div>
<br /> <br>
<div align="center"> <div align="center">
<div class="ironman-nav"> <div class="ironman-nav">
<span class="ironman-nav__option ironman-nav__option--current">Hiscores</span> <span class="ironman-nav__option ironman-nav__option--current">Hiscores</span>
<div class="ironman-nav__group">
<a class="ironman-nav__option ironman-nav__option--parent">
Ironman
</a>
<div class="ironman-nav__submenu">
<a class="ironman-nav__option" href="https://secure.runescape.com/m=hiscore_oldschool_ironman/">Ironman</a> <a class="ironman-nav__option" href="https://secure.runescape.com/m=hiscore_oldschool_ironman/">Ironman</a>
<a class="ironman-nav__option" href="https://secure.runescape.com/m=hiscore_oldschool_ultimate/">Ultimate <a class="ironman-nav__option" href="https://secure.runescape.com/m=hiscore_oldschool_ultimate/">Ultimate Ironman</a>
Ironman</a> <a class="ironman-nav__option" href="https://secure.runescape.com/m=hiscore_oldschool_hardcore_ironman/">Hardcore Ironman</a>
<a class="ironman-nav__option" <a class="ironman-nav__option" href="https://secure.runescape.com/m=hiscore_oldschool_deadman/">Deadman Mode</a>
href="https://secure.runescape.com/m=hiscore_oldschool_hardcore_ironman/">Hardcore Ironman</a>
</div>
</div>
<div class="ironman-nav__group">
<a class="ironman-nav__option ironman-nav__option--parent">
Seasonal
</a>
<div class="ironman-nav__submenu">
<a class="ironman-nav__option" href="https://secure.runescape.com/m=hiscore_oldschool_deadman/">Deadman
Mode</a>
<a class="ironman-nav__option" href="https://secure.runescape.com/m=hiscore_oldschool_seasonal/">Leagues</a> <a class="ironman-nav__option" href="https://secure.runescape.com/m=hiscore_oldschool_seasonal/">Leagues</a>
<a class="ironman-nav__option" <a class="ironman-nav__option" href="https://secure.runescape.com/m=hiscore_oldschool_tournament/">Tournament</a>
href="https://secure.runescape.com/m=hiscore_oldschool_tournament/">Tournament</a>
</div>
</div>
<div class="ironman-nav__group">
<a class="ironman-nav__option ironman-nav__option--parent">
Group Ironman
</a>
<div class="ironman-nav__submenu">
<a class="ironman-nav__option"
href="https://secure.runescape.com/m=hiscore_oldschool_ironman/group-ironman/">Group Ironman</a>
<a class="ironman-nav__option"
href="https://secure.runescape.com/m=hiscore_oldschool_hardcore_ironman/group-ironman/">Hardcore Group
Ironman</a>
</div>
</div>
<div class="ironman-nav__group">
<a class="ironman-nav__option" href="https://secure.runescape.com/m=hiscore_oldschool_fresh_start/">
Fresh Start Worlds
</a>
</div>
<div class="ironman-nav__group">
<a class="ironman-nav__option ironman-nav__option--parent">
Account Builds
</a>
<div class="ironman-nav__submenu">
<a class="ironman-nav__option" href="https://secure.runescape.com/m=hiscore_oldschool_skiller/">Skillers</a>
<a class="ironman-nav__option" href="https://secure.runescape.com/m=hiscore_oldschool_skiller_defence/">1
Defence</a>
</div>
</div>
</div> </div>
<div class="personal-hiscores"> <div class="personal-hiscores">
<nav class="personal-hiscores__side-banner"> <nav class="personal-hiscores__side-banner">
<div id="col1"> <div id="col1">
<div id="headerCategory"></div> <div id="headerCategory"></div>
<a class="personal-hiscores__scroll-arrow personal-hiscores__scroll-arrow--up" href="#" <a class="personal-hiscores__scroll-arrow personal-hiscores__scroll-arrow--up personal-hiscores__scroll-arrow--disabled" href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=1#" data-js-scroll="up">Up</a>
data-js-scroll="up">Up</a>
<div id="contentCategory"> <div id="contentCategory">
<a href="overall?table=0#headerHiscores" class=" Overall "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=0#headerHiscores" class=" Overall ">
Overall Overall
</a> </a>
<a href="overall?table=1#headerHiscores" class=" Attack "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=1#headerHiscores" class=" Attack ">
Attack Attack
</a> </a>
<a href="overall?table=2#headerHiscores" class=" Defence "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=2#headerHiscores" class=" Defence ">
Defence Defence
</a> </a>
<a href="overall?table=3#headerHiscores" class=" Strength "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=3#headerHiscores" class=" Strength ">
Strength Strength
</a> </a>
<a href="overall?table=4#headerHiscores" class=" Hitpoints "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=4#headerHiscores" class=" Hitpoints ">
Hitpoints Hitpoints
</a> </a>
<a href="overall?table=5#headerHiscores" class=" Ranged "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=5#headerHiscores" class=" Ranged ">
Ranged Ranged
</a> </a>
<a href="overall?table=6#headerHiscores" class=" Prayer "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=6#headerHiscores" class=" Prayer ">
Prayer Prayer
</a> </a>
<a href="overall?table=7#headerHiscores" class=" Magic "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=7#headerHiscores" class=" Magic ">
Magic Magic
</a> </a>
<a href="overall?table=8#headerHiscores" class=" Cooking "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=8#headerHiscores" class=" Cooking ">
Cooking Cooking
</a> </a>
<a href="overall?table=9#headerHiscores" class=" Woodcutting "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=9#headerHiscores" class=" Woodcutting ">
Woodcutting Woodcutting
</a> </a>
<a href="overall?table=10#headerHiscores" class=" Fletching "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=10#headerHiscores" class=" Fletching ">
Fletching Fletching
</a> </a>
<a href="overall?table=11#headerHiscores" class=" Fishing "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=11#headerHiscores" class=" Fishing ">
Fishing Fishing
</a> </a>
<a href="overall?table=12#headerHiscores" class=" Firemaking "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=12#headerHiscores" class=" Firemaking ">
Firemaking Firemaking
</a> </a>
<a href="overall?table=13#headerHiscores" class=" Crafting "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=13#headerHiscores" class=" Crafting ">
Crafting Crafting
</a> </a>
<a href="overall?table=14#headerHiscores" class=" Smithing "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=14#headerHiscores" class=" Smithing ">
Smithing Smithing
</a> </a>
<a href="overall?table=15#headerHiscores" class=" Mining "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=15#headerHiscores" class=" Mining ">
Mining Mining
</a> </a>
<a href="overall?table=16#headerHiscores" class=" Herblore "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=16#headerHiscores" class=" Herblore ">
Herblore Herblore
</a> </a>
<a href="overall?table=17#headerHiscores" class=" Agility "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=17#headerHiscores" class=" Agility ">
Agility Agility
</a> </a>
<a href="overall?table=18#headerHiscores" class=" Thieving "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=18#headerHiscores" class=" Thieving ">
Thieving Thieving
</a> </a>
<a href="overall?table=19#headerHiscores" class=" Slayer "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=19#headerHiscores" class=" Slayer ">
Slayer Slayer
</a> </a>
<a href="overall?table=20#headerHiscores" class=" Farming "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=20#headerHiscores" class=" Farming ">
Farming Farming
</a> </a>
<a href="overall?table=21#headerHiscores" class=" Runecraft "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=21#headerHiscores" class=" Runecraft ">
Runecraft Runecraft
</a> </a>
<a href="overall?table=22#headerHiscores" class=" Hunter "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=22#headerHiscores" class=" Hunter ">
Hunter Hunter
</a> </a>
<a href="overall?table=23#headerHiscores" class=" Construction "> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=23#headerHiscores" class=" Construction ">
Construction Construction
</a> </a>
<span style="color: #d9c27e;display: block;text-align: center;">----</span> <span style="color: #d9c27e;display: block;text-align: center;">----</span>
<a href="overall?category_type=1&table=2#headerHiscores" class="activity-link bountyhunterhunter">Bounty <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=1#headerHiscores" class="activity-link bountyhunterhunter">Bounty Hunter - Hunter</a>
Hunter - Hunter</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=2#headerHiscores" class="activity-link bountyhunterrogue">Bounty Hunter - Rogue</a>
<a href="overall?category_type=1&table=3#headerHiscores" class="activity-link bountyhunterrogue">Bounty <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=3#headerHiscores" class="activity-link cluescrollsall">Clue Scrolls (all)</a>
Hunter - Rogue</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=4#headerHiscores" class="activity-link cluescrollsbeginner">Clue Scrolls (beginner)</a>
<a href="overall?category_type=1&table=4#headerHiscores" <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=5#headerHiscores" class="activity-link cluescrollseasy">Clue Scrolls (easy)</a>
class="activity-link bountyhunterlegacyhunter">Bounty Hunter (Legacy) - Hunter</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=6#headerHiscores" class="activity-link cluescrollsmedium">Clue Scrolls (medium)</a>
<a href="overall?category_type=1&table=5#headerHiscores" <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=7#headerHiscores" class="activity-link cluescrollshard">Clue Scrolls (hard)</a>
class="activity-link bountyhunterlegacyrogue">Bounty Hunter (Legacy) - Rogue</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=8#headerHiscores" class="activity-link cluescrollselite">Clue Scrolls (elite)</a>
<a href="overall?category_type=1&table=6#headerHiscores" class="activity-link cluescrollsall">Clue Scrolls <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=9#headerHiscores" class="activity-link cluescrollsmaster">Clue Scrolls (master)</a>
(all)</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=10#headerHiscores" class="activity-link lmsrank">LMS - Rank</a>
<a href="overall?category_type=1&table=7#headerHiscores" class="activity-link cluescrollsbeginner">Clue <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=11#headerHiscores" class="activity-link soulwarszeal">Soul Wars Zeal</a>
Scrolls (beginner)</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=12#headerHiscores" class="activity-link abyssalsire">Abyssal Sire</a>
<a href="overall?category_type=1&table=8#headerHiscores" class="activity-link cluescrollseasy">Clue <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=13#headerHiscores" class="activity-link alchemicalhydra">Alchemical Hydra</a>
Scrolls (easy)</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=14#headerHiscores" class="activity-link barrowschests">Barrows Chests</a>
<a href="overall?category_type=1&table=9#headerHiscores" class="activity-link cluescrollsmedium">Clue <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=15#headerHiscores" class="activity-link bryophyta">Bryophyta</a>
Scrolls (medium)</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=16#headerHiscores" class="activity-link callisto">Callisto</a>
<a href="overall?category_type=1&table=10#headerHiscores" class="activity-link cluescrollshard">Clue <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=17#headerHiscores" class="activity-link cerberus">Cerberus</a>
Scrolls (hard)</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=18#headerHiscores" class="activity-link chambersofxeric">Chambers of Xeric</a>
<a href="overall?category_type=1&table=11#headerHiscores" class="activity-link cluescrollselite">Clue <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=19#headerHiscores" class="activity-link chambersofxericchallengemode">Chambers of Xeric: Challenge Mode</a>
Scrolls (elite)</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=20#headerHiscores" class="activity-link chaoselemental">Chaos Elemental</a>
<a href="overall?category_type=1&table=12#headerHiscores" class="activity-link cluescrollsmaster">Clue <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=21#headerHiscores" class="activity-link chaosfanatic">Chaos Fanatic</a>
Scrolls (master)</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=22#headerHiscores" class="activity-link commanderzilyana">Commander Zilyana</a>
<a href="overall?category_type=1&table=13#headerHiscores" class="activity-link lmsrank">LMS - Rank</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=23#headerHiscores" class="activity-link corporealbeast">Corporeal Beast</a>
<a href="overall?category_type=1&table=14#headerHiscores" class="activity-link pvparenarank">PvP Arena - <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=24#headerHiscores" class="activity-link crazyarchaeologist">Crazy Archaeologist</a>
Rank</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=25#headerHiscores" class="activity-link dagannothprime">Dagannoth Prime</a>
<a href="overall?category_type=1&table=15#headerHiscores" class="activity-link soulwarszeal">Soul Wars <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=26#headerHiscores" class="activity-link dagannothrex">Dagannoth Rex</a>
Zeal</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=27#headerHiscores" class="activity-link dagannothsupreme">Dagannoth Supreme</a>
<a href="overall?category_type=1&table=16#headerHiscores" class="activity-link riftsclosed">Rifts <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=28#headerHiscores" class="activity-link derangedarchaeologist">Deranged Archaeologist</a>
closed</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=29#headerHiscores" class="activity-link generalgraardor">General Graardor</a>
<a href="overall?category_type=1&table=17#headerHiscores" class="activity-link abyssalsire">Abyssal <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=30#headerHiscores" class="activity-link giantmole">Giant Mole</a>
Sire</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=31#headerHiscores" class="activity-link grotesqueguardians">Grotesque Guardians</a>
<a href="overall?category_type=1&table=18#headerHiscores" class="activity-link alchemicalhydra">Alchemical <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=32#headerHiscores" class="activity-link hespori">Hespori</a>
Hydra</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=33#headerHiscores" class="activity-link kalphitequeen">Kalphite Queen</a>
<a href="overall?category_type=1&table=19#headerHiscores" class="activity-link artio">Artio</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=34#headerHiscores" class="activity-link kingblackdragon">King Black Dragon</a>
<a href="overall?category_type=1&table=20#headerHiscores" class="activity-link barrowschests">Barrows <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=35#headerHiscores" class="activity-link kraken">Kraken</a>
Chests</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=36#headerHiscores" class="activity-link kreearra">Kree'Arra</a>
<a href="overall?category_type=1&table=21#headerHiscores" class="activity-link bryophyta">Bryophyta</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=37#headerHiscores" class="activity-link kriltsutsaroth">K'ril Tsutsaroth</a>
<a href="overall?category_type=1&table=22#headerHiscores" class="activity-link callisto">Callisto</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=38#headerHiscores" class="activity-link mimic">Mimic</a>
<a href="overall?category_type=1&table=23#headerHiscores" class="activity-link calvarion">Calvar'ion</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=39#headerHiscores" class="activity-link nightmare">Nightmare</a>
<a href="overall?category_type=1&table=24#headerHiscores" class="activity-link cerberus">Cerberus</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=40#headerHiscores" class="activity-link obor">Obor</a>
<a href="overall?category_type=1&table=25#headerHiscores" class="activity-link chambersofxeric">Chambers <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=41#headerHiscores" class="activity-link sarachnis">Sarachnis</a>
of Xeric</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=42#headerHiscores" class="activity-link scorpia">Scorpia</a>
<a href="overall?category_type=1&table=26#headerHiscores" <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=43#headerHiscores" class="activity-link skotizo">Skotizo</a>
class="activity-link chambersofxericchallengemode">Chambers of Xeric: Challenge Mode</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=44#headerHiscores" class="activity-link tempoross">Tempoross</a>
<a href="overall?category_type=1&table=27#headerHiscores" class="activity-link chaoselemental">Chaos <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=45#headerHiscores" class="activity-link thegauntlet">The Gauntlet</a>
Elemental</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=46#headerHiscores" class="activity-link thecorruptedgauntlet">The Corrupted Gauntlet</a>
<a href="overall?category_type=1&table=28#headerHiscores" class="activity-link chaosfanatic">Chaos <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=47#headerHiscores" class="activity-link theatreofblood">Theatre of Blood</a>
Fanatic</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=48#headerHiscores" class="activity-link thermonuclearsmokedevil">Thermonuclear Smoke Devil</a>
<a href="overall?category_type=1&table=29#headerHiscores" class="activity-link commanderzilyana">Commander <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=49#headerHiscores" class="activity-link tzkalzuk">TzKal-Zuk</a>
Zilyana</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=50#headerHiscores" class="activity-link tztokjad">TzTok-Jad</a>
<a href="overall?category_type=1&table=30#headerHiscores" class="activity-link corporealbeast">Corporeal <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=51#headerHiscores" class="activity-link venenatis">Venenatis</a>
Beast</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=52#headerHiscores" class="activity-link vetion">Vet'ion</a>
<a href="overall?category_type=1&table=31#headerHiscores" class="activity-link crazyarchaeologist">Crazy <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=53#headerHiscores" class="activity-link vorkath">Vorkath</a>
Archaeologist</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=54#headerHiscores" class="activity-link wintertodt">Wintertodt</a>
<a href="overall?category_type=1&table=32#headerHiscores" class="activity-link dagannothprime">Dagannoth <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=55#headerHiscores" class="activity-link zalcano">Zalcano</a>
Prime</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=1&amp;table=56#headerHiscores" class="activity-link zulrah">Zulrah</a>
<a href="overall?category_type=1&table=33#headerHiscores" class="activity-link dagannothrex">Dagannoth
Rex</a>
<a href="overall?category_type=1&table=34#headerHiscores" class="activity-link dagannothsupreme">Dagannoth
Supreme</a>
<a href="overall?category_type=1&table=35#headerHiscores"
class="activity-link derangedarchaeologist">Deranged Archaeologist</a>
<a href="overall?category_type=1&table=36#headerHiscores" class="activity-link dukesucellus">Duke
Sucellus</a>
<a href="overall?category_type=1&table=37#headerHiscores" class="activity-link generalgraardor">General
Graardor</a>
<a href="overall?category_type=1&table=38#headerHiscores" class="activity-link giantmole">Giant Mole</a>
<a href="overall?category_type=1&table=39#headerHiscores"
class="activity-link grotesqueguardians">Grotesque Guardians</a>
<a href="overall?category_type=1&table=40#headerHiscores" class="activity-link hespori">Hespori</a>
<a href="overall?category_type=1&table=41#headerHiscores" class="activity-link kalphitequeen">Kalphite
Queen</a>
<a href="overall?category_type=1&table=42#headerHiscores" class="activity-link kingblackdragon">King Black
Dragon</a>
<a href="overall?category_type=1&table=43#headerHiscores" class="activity-link kraken">Kraken</a>
<a href="overall?category_type=1&table=44#headerHiscores" class="activity-link kreearra">Kree'Arra</a>
<a href="overall?category_type=1&table=45#headerHiscores" class="activity-link kriltsutsaroth">K'ril
Tsutsaroth</a>
<a href="overall?category_type=1&table=46#headerHiscores" class="activity-link mimic">Mimic</a>
<a href="overall?category_type=1&table=47#headerHiscores" class="activity-link nex">Nex</a>
<a href="overall?category_type=1&table=48#headerHiscores" class="activity-link nightmare">Nightmare</a>
<a href="overall?category_type=1&table=49#headerHiscores"
class="activity-link phosanisnightmare">Phosani's Nightmare</a>
<a href="overall?category_type=1&table=50#headerHiscores" class="activity-link obor">Obor</a>
<a href="overall?category_type=1&table=51#headerHiscores" class="activity-link phantommuspah">Phantom
Muspah</a>
<a href="overall?category_type=1&table=52#headerHiscores" class="activity-link sarachnis">Sarachnis</a>
<a href="overall?category_type=1&table=53#headerHiscores" class="activity-link scorpia">Scorpia</a>
<a href="overall?category_type=1&table=54#headerHiscores" class="activity-link skotizo">Skotizo</a>
<a href="overall?category_type=1&table=55#headerHiscores" class="activity-link spindel">Spindel</a>
<a href="overall?category_type=1&table=56#headerHiscores" class="activity-link tempoross">Tempoross</a>
<a href="overall?category_type=1&table=57#headerHiscores" class="activity-link thegauntlet">The
Gauntlet</a>
<a href="overall?category_type=1&table=58#headerHiscores" class="activity-link thecorruptedgauntlet">The
Corrupted Gauntlet</a>
<a href="overall?category_type=1&table=59#headerHiscores" class="activity-link theleviathan">The
Leviathan</a>
<a href="overall?category_type=1&table=60#headerHiscores" class="activity-link thewhisperer">The
Whisperer</a>
<a href="overall?category_type=1&table=61#headerHiscores" class="activity-link theatreofblood">Theatre of
Blood</a>
<a href="overall?category_type=1&table=62#headerHiscores"
class="activity-link theatreofbloodhardmode">Theatre of Blood: Hard Mode</a>
<a href="overall?category_type=1&table=63#headerHiscores"
class="activity-link thermonuclearsmokedevil">Thermonuclear Smoke Devil</a>
<a href="overall?category_type=1&table=64#headerHiscores" class="activity-link tombsofamascut">Tombs of
Amascut</a>
<a href="overall?category_type=1&table=65#headerHiscores"
class="activity-link tombsofamascutexpertmode">Tombs of Amascut: Expert Mode</a>
<a href="overall?category_type=1&table=66#headerHiscores" class="activity-link tzkalzuk">TzKal-Zuk</a>
<a href="overall?category_type=1&table=67#headerHiscores" class="activity-link tztokjad">TzTok-Jad</a>
<a href="overall?category_type=1&table=68#headerHiscores" class="activity-link vardorvis">Vardorvis</a>
<a href="overall?category_type=1&table=69#headerHiscores" class="activity-link venenatis">Venenatis</a>
<a href="overall?category_type=1&table=70#headerHiscores" class="activity-link vetion">Vet'ion</a>
<a href="overall?category_type=1&table=71#headerHiscores" class="activity-link vorkath">Vorkath</a>
<a href="overall?category_type=1&table=72#headerHiscores" class="activity-link wintertodt">Wintertodt</a>
<a href="overall?category_type=1&table=73#headerHiscores" class="activity-link zalcano">Zalcano</a>
<a href="overall?category_type=1&table=74#headerHiscores" class="activity-link zulrah">Zulrah</a>
</div> </div>
<a class="personal-hiscores__scroll-arrow personal-hiscores__scroll-arrow--down" href="#" <a class="personal-hiscores__scroll-arrow personal-hiscores__scroll-arrow--down" href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=1#" data-js-scroll="down">Down</a>
data-js-scroll="down">Down</a>
<div id="footerCategory"></div> <div id="footerCategory"></div>
</div> </div>
</nav> </nav>
@@ -659,7 +437,7 @@
<div id="contentHiscores"> <div id="contentHiscores">
<table> <table>
<caption> <caption>
<img class="miniimg" src="https://www.runescape.com/img/rsp777/hiscores/skill_icon_attack1.gif" /> <img class="miniimg" src="./attackTopPage_files/skill_icon_attack1.gif">
&nbsp; &nbsp;
Attack Attack
@@ -669,23 +447,21 @@
<tr> <tr>
<th class="right">Rank</th> <th class="right">Rank</th>
<th class="left">Name</th> <th class="left">Name</th>
<th>Level</th> <th>Level</th><th class="right">XP</th>
<th class="right">XP</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td width="60"></td> <td width="60"></td>
<td width="120"></td> <td width="120"></td>
<td width="70"></td> <td width="70"></td><td width="100"></td>
<td width="100"></td>
</tr> </tr>
<tr class="personal-hiscores__row"> <tr class="personal-hiscores__row">
<td class="right"> <td class="right">
1 1
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=Tai">Tai</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=Heur">Heur</a>
</td> </td>
<td> <td>
99 99
@@ -699,7 +475,7 @@
2 2
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=Unohdettu2">Unohdettu2</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=Unohdettu2">Unohdettu2</a>
</td> </td>
<td> <td>
99 99
@@ -713,7 +489,7 @@
3 3
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=Drakon">Drakon</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=Drakon">Drakon</a>
</td> </td>
<td> <td>
99 99
@@ -727,7 +503,7 @@
4 4
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=Ame Hara">Ame Hara</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=Ame%A0Umehara">Ame&nbsp;Umehara</a>
</td> </td>
<td> <td>
99 99
@@ -741,7 +517,7 @@
5 5
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=Jakee">Jakee</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=Jakee">Jakee</a>
</td> </td>
<td> <td>
99 99
@@ -755,7 +531,7 @@
6 6
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=Aravt">Aravt</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=L00OO00OO00l">L00OO00OO00l</a>
</td> </td>
<td> <td>
99 99
@@ -769,7 +545,7 @@
7 7
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=Howson">Howson</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=Howson">Howson</a>
</td> </td>
<td> <td>
99 99
@@ -783,7 +559,7 @@
8 8
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=Dr PFAFF">Dr PFAFF</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=Dr%A0PFAFF">Dr&nbsp;PFAFF</a>
</td> </td>
<td> <td>
99 99
@@ -797,7 +573,7 @@
9 9
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=Lickeys">Lickeys</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=Malt%A0Lickeys">Malt&nbsp;Lickeys</a>
</td> </td>
<td> <td>
99 99
@@ -811,7 +587,7 @@
10 10
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=Burned">Burned</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=Burned">Burned</a>
</td> </td>
<td> <td>
99 99
@@ -825,7 +601,7 @@
11 11
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=Blue Limes">Blue Limes</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=Blue%A0Limes">Blue&nbsp;Limes</a>
</td> </td>
<td> <td>
99 99
@@ -839,7 +615,7 @@
12 12
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=Mini Aurelia">Mini Aurelia</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=Mini%A0Finbarr">Mini&nbsp;Finbarr</a>
</td> </td>
<td> <td>
99 99
@@ -853,7 +629,7 @@
13 13
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=Unohdettu3">Unohdettu3</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=Unohdettu3">Unohdettu3</a>
</td> </td>
<td> <td>
99 99
@@ -867,7 +643,7 @@
14 14
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=Eslihero">Eslihero</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=Eslihero">Eslihero</a>
</td> </td>
<td> <td>
99 99
@@ -881,7 +657,7 @@
15 15
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=Lynx Titan">Lynx Titan</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=Lynx%A0Titan">Lynx&nbsp;Titan</a>
</td> </td>
<td> <td>
99 99
@@ -895,7 +671,7 @@
16 16
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=AndrewWigins">AndrewWigins</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=AndrewWigins">AndrewWigins</a>
</td> </td>
<td> <td>
99 99
@@ -909,7 +685,7 @@
17 17
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=iMelee">iMelee</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=iMelee">iMelee</a>
</td> </td>
<td> <td>
99 99
@@ -923,7 +699,7 @@
18 18
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=Portuguese">Portuguese</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=Portuguese">Portuguese</a>
</td> </td>
<td> <td>
99 99
@@ -937,7 +713,7 @@
19 19
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=MarkoOSRS">MarkoOSRS</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=MarkoOSRS">MarkoOSRS</a>
</td> </td>
<td> <td>
99 99
@@ -951,7 +727,7 @@
20 20
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=Cairo">Cairo</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=Cairo">Cairo</a>
</td> </td>
<td> <td>
99 99
@@ -965,7 +741,7 @@
21 21
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=Hey Jase">Hey Jase</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=Hey%A0Jase">Hey&nbsp;Jase</a>
</td> </td>
<td> <td>
99 99
@@ -979,7 +755,7 @@
22 22
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=H D M P">H D M P</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=H%A0D%A0M%A0P">H&nbsp;D&nbsp;M&nbsp;P</a>
</td> </td>
<td> <td>
99 99
@@ -993,7 +769,7 @@
23 23
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=Yumemi">Yumemi</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=Yumemi">Yumemi</a>
</td> </td>
<td> <td>
99 99
@@ -1007,7 +783,7 @@
24 24
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=Fiiggy">Fiiggy</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=Fiiggy">Fiiggy</a>
</td> </td>
<td> <td>
99 99
@@ -1021,7 +797,7 @@
25 25
</td> </td>
<td class="left"> <td class="left">
<a href="hiscorepersonal?user1=Edgecrusher">Edgecrusher</a> <a href="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal?user1=Edgecrusher">Edgecrusher</a>
</td> </td>
<td> <td>
99 99
@@ -1033,9 +809,8 @@
</tbody> </tbody>
</table> </table>
<nav class="personal-hiscores__pagination"> <nav class="personal-hiscores__pagination">
<a class="personal-hiscores__pagination-arrow personal-hiscores__pagination-arrow--down" <a class="personal-hiscores__pagination-arrow personal-hiscores__pagination-arrow--down" href="https://secure.runescape.com/m=hiscore_oldschool/overall?table=1&amp;page=2" title="Scroll down table">
href="overall?table=1&page=2" title="Scroll down table"> <img src="./attackTopPage_files/arrow_down.gif" alt="Scroll Down">
<img src="https://www.runescape.com/img/rsp777/hiscores/arrow_down.gif" alt="Scroll Down" />
</a> </a>
</nav> </nav>
</div> </div>
@@ -1045,21 +820,21 @@
</div> </div>
<div class="personal-hiscores__side-input"> <div class="personal-hiscores__side-input">
<td valign="top">
<form action="hiscorepersonal" method="post"> <form action="https://secure.runescape.com/m=hiscore_oldschool/hiscorepersonal" method="post">
<div class="smallBox"> <div class="smallBox">
<p> <p>
<b>Search by name</b> <b>Search by name</b>
<input class="text" maxlength="12" type="text" name="user1" value=""> <input class="text" maxlength="12" type="text" name="user1" value="" autocomplete="off" style="background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAkCAYAAADo6zjiAAAAAXNSR0IArs4c6QAAAr1JREFUWAndVzuLIkEQrhmVVUHROwPvzkTWDRbF28BgjW7xVxwY+gfEYCNBA6PL/AsGx8UbLQayZqKJayIILshytyiogbIY+LiquelGx3F80INwDUNXV1V/9fWrukcCLIVC4ct8Pv+B4t1qtfpMOlFFkqQ/iPVktVrvs9nsby2upAZ/xsAftUaRbSQyRBJftSRkGrnZwWkgFEOd5Y1xydi629CY29iKJYtecyP+erFoBs5a/m8C19fXkMlkgOpdxdQZCIfD4Ha7gepdxVQCePaVuKzWI2EqAb2AWt0GgYuLC7i8vASv16v1g1NtW0AahZW1PR4PpFIpcLlcsFwu4eHhAZrNpmI+1cawjWo+A7e3t0pwcpZlGRKJBO93qo0DGAicgIGPqSZOoFarwWQyUYLRElQqFR74VBsHMBCkXC63YnbaaIFAAEajEYzHY6ZWaiPbhuOeBh7JN3Spqu+D1w0Ce/oKNSOREZK44UsgFP0AMLwZP9D74GwEVI7feB44gLShSz6fN7STsVQqwcvLC/fDWfh07hkAYTOgPTU0TLvdDg6HQxnxYrGAwWDAR88EYQSKxSLD5HUymYSrqyul3W63YTqdchsTZDwO9G4XXnw+H4RCIY7baDS4vC7QHnhaV4iS4/E4sHdAv9+HXq+nCy1TRkLHoa71RKXT6YRoNMp71+t1LmsFmf5U6I8FSfwUtRyxWAxsNpsSazabQavV0sbl7X9vJt7cL6zfHXreFosF0uk0v9rpInt8fNRzVXTC80AkEuHBMdHArs3HGAknQJuPlW63C8Oh8fYSSiAYDILf72fx946eHI9ORLhR3yiH8yhrAiUdlu3e39+h0+msWbdFwjqaAMJU8fu+DQdQLpeVT8+2Q1c9egnUvDHaAXiwGkdPD5L7owlg3njFjjcI8Ium8OCIqiP1ob6EQVh/AVA3+XEhdOWMAAAAAElFTkSuQmCC&quot;); background-repeat: no-repeat; background-attachment: scroll; background-size: 16px 18px; background-position: 98% 50%;">
<input maxlength="12" type="submit" name="submit" value="Search"> <input maxlength="12" type="submit" name="submit" value="Search">
</p> </p>
</div> </div>
</form> </form>
<form action="overall?category_type=0" method="post"> <form action="https://secure.runescape.com/m=hiscore_oldschool/overall?category_type=0" method="post">
<div class="smallBox"> <div class="smallBox">
<p> <p>
<b>Search by rank</b> <b>Search by rank</b>
<input class="text" maxlength="12" type="text" name="rank" value=""> <input class="text" maxlength="12" type="text" name="rank" value="" autocomplete="off" style="background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAkCAYAAADo6zjiAAAAAXNSR0IArs4c6QAAAr1JREFUWAndVzuLIkEQrhmVVUHROwPvzkTWDRbF28BgjW7xVxwY+gfEYCNBA6PL/AsGx8UbLQayZqKJayIILshytyiogbIY+LiquelGx3F80INwDUNXV1V/9fWrukcCLIVC4ct8Pv+B4t1qtfpMOlFFkqQ/iPVktVrvs9nsby2upAZ/xsAftUaRbSQyRBJftSRkGrnZwWkgFEOd5Y1xydi629CY29iKJYtecyP+erFoBs5a/m8C19fXkMlkgOpdxdQZCIfD4Ha7gepdxVQCePaVuKzWI2EqAb2AWt0GgYuLC7i8vASv16v1g1NtW0AahZW1PR4PpFIpcLlcsFwu4eHhAZrNpmI+1cawjWo+A7e3t0pwcpZlGRKJBO93qo0DGAicgIGPqSZOoFarwWQyUYLRElQqFR74VBsHMBCkXC63YnbaaIFAAEajEYzHY6ZWaiPbhuOeBh7JN3Spqu+D1w0Ce/oKNSOREZK44UsgFP0AMLwZP9D74GwEVI7feB44gLShSz6fN7STsVQqwcvLC/fDWfh07hkAYTOgPTU0TLvdDg6HQxnxYrGAwWDAR88EYQSKxSLD5HUymYSrqyul3W63YTqdchsTZDwO9G4XXnw+H4RCIY7baDS4vC7QHnhaV4iS4/E4sHdAv9+HXq+nCy1TRkLHoa71RKXT6YRoNMp71+t1LmsFmf5U6I8FSfwUtRyxWAxsNpsSazabQavV0sbl7X9vJt7cL6zfHXreFosF0uk0v9rpInt8fNRzVXTC80AkEuHBMdHArs3HGAknQJuPlW63C8Oh8fYSSiAYDILf72fx946eHI9ORLhR3yiH8yhrAiUdlu3e39+h0+msWbdFwjqaAMJU8fu+DQdQLpeVT8+2Q1c9egnUvDHaAXiwGkdPD5L7owlg3njFjjcI8Ium8OCIqiP1ob6EQVh/AVA3+XEhdOWMAAAAAElFTkSuQmCC&quot;); background-repeat: no-repeat; background-attachment: scroll; background-size: 16px 18px; background-position: 98% 50%; cursor: auto;">
<input type="hidden" name="table" value="1"> <input type="hidden" name="table" value="1">
<input type="submit" name="submit" value="Search"> <input type="submit" name="submit" value="Search">
</p> </p>
@@ -1069,10 +844,8 @@
<div class="largeBox"> <div class="largeBox">
<p> <p>
<b>Compare Users</b> <b>Compare Users</b>
<input class="text" maxlength="12" type="text" name="user1" value="" <input class="text" maxlength="12" type="text" name="user1" value="" pattern="^[a-zA-Z0-9]+([ _-&amp;nbsp;]{0,10}[0-9a-zA-Z]+)*$" title="player name" required="" autocomplete="off" style="background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAkCAYAAADo6zjiAAAAAXNSR0IArs4c6QAAAr1JREFUWAndVzuLIkEQrhmVVUHROwPvzkTWDRbF28BgjW7xVxwY+gfEYCNBA6PL/AsGx8UbLQayZqKJayIILshytyiogbIY+LiquelGx3F80INwDUNXV1V/9fWrukcCLIVC4ct8Pv+B4t1qtfpMOlFFkqQ/iPVktVrvs9nsby2upAZ/xsAftUaRbSQyRBJftSRkGrnZwWkgFEOd5Y1xydi629CY29iKJYtecyP+erFoBs5a/m8C19fXkMlkgOpdxdQZCIfD4Ha7gepdxVQCePaVuKzWI2EqAb2AWt0GgYuLC7i8vASv16v1g1NtW0AahZW1PR4PpFIpcLlcsFwu4eHhAZrNpmI+1cawjWo+A7e3t0pwcpZlGRKJBO93qo0DGAicgIGPqSZOoFarwWQyUYLRElQqFR74VBsHMBCkXC63YnbaaIFAAEajEYzHY6ZWaiPbhuOeBh7JN3Spqu+D1w0Ce/oKNSOREZK44UsgFP0AMLwZP9D74GwEVI7feB44gLShSz6fN7STsVQqwcvLC/fDWfh07hkAYTOgPTU0TLvdDg6HQxnxYrGAwWDAR88EYQSKxSLD5HUymYSrqyul3W63YTqdchsTZDwO9G4XXnw+H4RCIY7baDS4vC7QHnhaV4iS4/E4sHdAv9+HXq+nCy1TRkLHoa71RKXT6YRoNMp71+t1LmsFmf5U6I8FSfwUtRyxWAxsNpsSazabQavV0sbl7X9vJt7cL6zfHXreFosF0uk0v9rpInt8fNRzVXTC80AkEuHBMdHArs3HGAknQJuPlW63C8Oh8fYSSiAYDILf72fx946eHI9ORLhR3yiH8yhrAiUdlu3e39+h0+msWbdFwjqaAMJU8fu+DQdQLpeVT8+2Q1c9egnUvDHaAXiwGkdPD5L7owlg3njFjjcI8Ium8OCIqiP1ob6EQVh/AVA3+XEhdOWMAAAAAElFTkSuQmCC&quot;); background-repeat: no-repeat; background-attachment: scroll; background-size: 16px 18px; background-position: 98% 50%;">
pattern="^[a-zA-Z0-9]+([ _-&amp;nbsp;]{0,10}[0-9a-zA-Z]+)*$" title="player name" required> <input class="text" maxlength="12" type="text" name="user2" value="" pattern="^[a-zA-Z0-9]+([ _-&amp;nbsp;]{0,10}[0-9a-zA-Z]+)*$" title="player name" required="" autocomplete="off" style="background-image: url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAkCAYAAADo6zjiAAAAAXNSR0IArs4c6QAAAr1JREFUWAndVzuLIkEQrhmVVUHROwPvzkTWDRbF28BgjW7xVxwY+gfEYCNBA6PL/AsGx8UbLQayZqKJayIILshytyiogbIY+LiquelGx3F80INwDUNXV1V/9fWrukcCLIVC4ct8Pv+B4t1qtfpMOlFFkqQ/iPVktVrvs9nsby2upAZ/xsAftUaRbSQyRBJftSRkGrnZwWkgFEOd5Y1xydi629CY29iKJYtecyP+erFoBs5a/m8C19fXkMlkgOpdxdQZCIfD4Ha7gepdxVQCePaVuKzWI2EqAb2AWt0GgYuLC7i8vASv16v1g1NtW0AahZW1PR4PpFIpcLlcsFwu4eHhAZrNpmI+1cawjWo+A7e3t0pwcpZlGRKJBO93qo0DGAicgIGPqSZOoFarwWQyUYLRElQqFR74VBsHMBCkXC63YnbaaIFAAEajEYzHY6ZWaiPbhuOeBh7JN3Spqu+D1w0Ce/oKNSOREZK44UsgFP0AMLwZP9D74GwEVI7feB44gLShSz6fN7STsVQqwcvLC/fDWfh07hkAYTOgPTU0TLvdDg6HQxnxYrGAwWDAR88EYQSKxSLD5HUymYSrqyul3W63YTqdchsTZDwO9G4XXnw+H4RCIY7baDS4vC7QHnhaV4iS4/E4sHdAv9+HXq+nCy1TRkLHoa71RKXT6YRoNMp71+t1LmsFmf5U6I8FSfwUtRyxWAxsNpsSazabQavV0sbl7X9vJt7cL6zfHXreFosF0uk0v9rpInt8fNRzVXTC80AkEuHBMdHArs3HGAknQJuPlW63C8Oh8fYSSiAYDILf72fx946eHI9ORLhR3yiH8yhrAiUdlu3e39+h0+msWbdFwjqaAMJU8fu+DQdQLpeVT8+2Q1c9egnUvDHaAXiwGkdPD5L7owlg3njFjjcI8Ium8OCIqiP1ob6EQVh/AVA3+XEhdOWMAAAAAElFTkSuQmCC&quot;); background-repeat: no-repeat; background-attachment: scroll; background-size: 16px 18px; background-position: 98% 50%;">
<input class="text" maxlength="12" type="text" name="user2" value=""
pattern="^[a-zA-Z0-9]+([ _-&amp;nbsp;]{0,10}[0-9a-zA-Z]+)*$" title="player name" required>
<input type="submit" name="submit" value="Compare"> <input type="submit" name="submit" value="Compare">
</p> </p>
</div> </div>
@@ -1084,28 +857,19 @@
hiscores and compare<br> hiscores and compare<br>
yourself to your friends<br> yourself to your friends<br>
</div> </div>
<a href="https://secure.runescape.com/m=weblogin/oldschool_login?mod=hiscore_oldschool&amp;ssl=0&amp;dest=hiscorefriends_all" <a href="https://secure.runescape.com/m=weblogin/oldschool_login?mod=hiscore_oldschool&amp;ssl=0&amp;dest=hiscorefriends_all" class="linkToFriends">Click here</a>
class="linkToFriends">Click here</a>
</div> </div>
</td>
</div> </div>
</div> </div>
</div> </div>
<div class="tandc"> <div class="tandc">
<a href="http://www.jagex.com" target="_top"><img class="footer__jagex-img" <a href="http://www.jagex.com/" target="_top"><img class="footer__jagex-img" src="./attackTopPage_files/jagex-logo-landscape.svg" alt="Jagex Software"></a>
src="https://www.runescape.com/img/rsp777/jagex-logo-landscape.svg" alt="Jagex Software"></a> <p class="tandc__copy">This website and its contents are copyright <20> 1999 - 2021 Jagex Ltd, 220 Science Park, Cambridge, CB4 0WA, United Kingdom.<br>Use of this website is subject to our <a href="https://www.jagex.com/terms/terms.ws" target="_blank">Terms &amp; Conditions</a> and <a href="https://www.jagex.com/privacy/privacy.ws" target="_blank">Privacy Policy</a>. <br><a href="https://www.jagex.com/terms/rules-of-old-school-runescape">Rules of Old School RuneScape</a> | <a href="https://www.jagex.com/terms/cookies.ws">Change Cookie Settings</a></p>
<p class="tandc__copy">This website and its contents are copyright &copy; 1999 - 2023 Jagex Ltd, 220 Science Park, <a class="osrsRSS" target="_BLANK" href="https://secure.runescape.com/m=news/latest_news.rss?oldschool=true"><img src="./attackTopPage_files/rss.png" title="OSRS RSS Feed" alt="OSRS RSS Feed"></a>
Cambridge, CB4 0WA, United Kingdom.<br />Use of this website is subject to our <a </div> <script src="./attackTopPage_files/modernizr_3_0_0_min-100.js"></script>
href="https://www.jagex.com/en-GB/terms/terms.ws" target="_blank">Terms &amp; Conditions</a> and <a <script src="./attackTopPage_files/rsp777_basic-105.js"></script>
href="https://www.jagex.com/en-GB/privacy/privacy.ws" target="_blank">Privacy Policy</a>. <br><a <script src="./attackTopPage_files/rsp777_twitter-100.js"></script>
href="https://www.jagex.com/en-GB/terms/rules-of-old-school-runescape">Rules of Old School RuneScape</a> | <a
href="https://www.jagex.com/en-GB/terms/cookies">Change Cookie Settings</a></p>
<a class="osrsRSS" target="_BLANK" href="https://secure.runescape.com/m=news/latest_news.rss?oldschool=true"><img
src="https://www.runescape.com/img/rsp777/rss.png" title="OSRS RSS Feed" alt="OSRS RSS Feed"></a>
</div>
<script src="https://www.runescape.com/js/c/rs3/modernizr_3_0_0_min-100.js"></script>
<script src="https://www.runescape.com/js/rsp777_basic-105.js"></script>
<script src="https://www.runescape.com/js/rsp777_twitter-100.js"></script>
<script type="text/javascript"> <script type="text/javascript">
if ($("#fb-root").length == 0){ if ($("#fb-root").length == 0){
@@ -1171,8 +935,7 @@
return false; return false;
} }
</script> </script>
<script type="text/javascript" src="https://www.runescape.com/js/rs3/plugins-100.js" <script type="text/javascript" src="./attackTopPage_files/plugins-100.js"></script>
data-cookieconsent="ignore"></script>
<script> <script>
var RESPONSIVE = RESPONSIVE || {}; var RESPONSIVE = RESPONSIVE || {};
RESPONSIVE.constant = RESPONSIVE.constant || {}; RESPONSIVE.constant = RESPONSIVE.constant || {};
@@ -1184,9 +947,7 @@
}; };
</script> </script>
<script type="text/javascript" src="https://www.runescape.com/js/osrs/gtm-100.js" <script type="text/javascript" src="./attackTopPage_files/gtm-100.js"></script>
data-cookieconsent="ignore"></script>
</div> </div>
</body>
</html> </body></html>

File diff suppressed because it is too large Load Diff

View File

@@ -1,103 +0,0 @@
2910,2277,943515557
24996,99,30606377
4759,99,40283500
1523,99,171953134
2244,99,147696928
5929,99,118171038
4560,99,15170201
2503,99,71406360
141823,99,13354119
22348,99,18680181
56125,99,13456850
23026,99,16655688
18672,99,19123987
48108,99,14072560
10956,99,16552060
27980,99,14967150
40661,99,13252766
7114,99,15760818
53839,99,14058762
5352,99,34669574
2456,99,84892847
4849,99,22734071
16368,99,17968711
1147,99,18027875
-1,-1
-1,-1
28384,10
14076,3
148183,8
102684,7
338,9641
1002,825
114,6000
2246,1606
44261,298
603,460
1238,452
15827,2521
-1,-1
7898,14780
5421,1060
7,24522
2902,2192
94883,594
34000,143
3834,1830
218428,5
36717,293
74959,58
19421,1905
186551,34
2571,515
85921,50
154580,26
54677,287
2586,2000
153371,41
33154,707
43433,698
33088,714
81968,25
12249,731
20424,1228
70188,474
2248,2140
11128,226
2117,2046
50095,580
120438,1908
10997,920
2326,1444
-1,-1
700,40
31471,626
4119,749
4349,500
1078,217
20126,301
70523,119
114632,26
2945,1245
385801,9
-1,-1
2412,1569
43814,273
207949,7
86335,201
7905,601
732,1783
21039,310
4949,192
7584,3066
109360,36
160,1500
6733,7
131888,8
3128,2113
209,7096
75591,51
15167,2780
911570,62
173436,25
233645,275
Can't render this file because it has a wrong number of fields in line 25.

View File

@@ -1,205 +0,0 @@
{
"skills": [
{
"id": 0,
"name": "Overall",
"rank": 2910,
"level": 2277,
"xp": 943515557
},
{ "id": 1, "name": "Attack", "rank": 24996, "level": 99, "xp": 30606377 },
{ "id": 2, "name": "Defence", "rank": 4759, "level": 99, "xp": 40283500 },
{ "id": 3, "name": "Strength", "rank": 1523, "level": 99, "xp": 171953134 },
{
"id": 4,
"name": "Hitpoints",
"rank": 2244,
"level": 99,
"xp": 147696928
},
{ "id": 5, "name": "Ranged", "rank": 5929, "level": 99, "xp": 118171038 },
{ "id": 6, "name": "Prayer", "rank": 4560, "level": 99, "xp": 15170201 },
{ "id": 7, "name": "Magic", "rank": 2503, "level": 99, "xp": 71406360 },
{ "id": 8, "name": "Cooking", "rank": 141823, "level": 99, "xp": 13354119 },
{
"id": 9,
"name": "Woodcutting",
"rank": 22348,
"level": 99,
"xp": 18680181
},
{
"id": 10,
"name": "Fletching",
"rank": 56125,
"level": 99,
"xp": 13456850
},
{ "id": 11, "name": "Fishing", "rank": 23026, "level": 99, "xp": 16655688 },
{
"id": 12,
"name": "Firemaking",
"rank": 18672,
"level": 99,
"xp": 19123987
},
{
"id": 13,
"name": "Crafting",
"rank": 48108,
"level": 99,
"xp": 14072560
},
{
"id": 14,
"name": "Smithing",
"rank": 10956,
"level": 99,
"xp": 16552060
},
{ "id": 15, "name": "Mining", "rank": 27980, "level": 99, "xp": 14967150 },
{
"id": 16,
"name": "Herblore",
"rank": 40661,
"level": 99,
"xp": 13252766
},
{ "id": 17, "name": "Agility", "rank": 7114, "level": 99, "xp": 15760818 },
{
"id": 18,
"name": "Thieving",
"rank": 53839,
"level": 99,
"xp": 14058762
},
{ "id": 19, "name": "Slayer", "rank": 5352, "level": 99, "xp": 34669574 },
{ "id": 20, "name": "Farming", "rank": 2456, "level": 99, "xp": 84892847 },
{
"id": 21,
"name": "Runecraft",
"rank": 4849,
"level": 99,
"xp": 22734071
},
{ "id": 22, "name": "Hunter", "rank": 16368, "level": 99, "xp": 17968711 },
{
"id": 23,
"name": "Construction",
"rank": 1147,
"level": 99,
"xp": 18027875
}
],
"activities": [
{ "id": 0, "name": "League Points", "rank": -1, "score": -1 },
{ "id": 1, "name": "Deadman Points", "rank": -1, "score": -1 },
{ "id": 2, "name": "Bounty Hunter - Hunter", "rank": 28384, "score": 10 },
{ "id": 3, "name": "Bounty Hunter - Rogue", "rank": 14076, "score": 3 },
{
"id": 4,
"name": "Bounty Hunter (Legacy) - Hunter",
"rank": 148183,
"score": 8
},
{
"id": 5,
"name": "Bounty Hunter (Legacy) - Rogue",
"rank": 102684,
"score": 7
},
{ "id": 6, "name": "Clue Scrolls (all)", "rank": 338, "score": 9641 },
{ "id": 7, "name": "Clue Scrolls (beginner)", "rank": 1002, "score": 825 },
{ "id": 8, "name": "Clue Scrolls (easy)", "rank": 114, "score": 6000 },
{ "id": 9, "name": "Clue Scrolls (medium)", "rank": 2246, "score": 1606 },
{ "id": 10, "name": "Clue Scrolls (hard)", "rank": 44261, "score": 298 },
{ "id": 11, "name": "Clue Scrolls (elite)", "rank": 603, "score": 460 },
{ "id": 12, "name": "Clue Scrolls (master)", "rank": 1238, "score": 452 },
{ "id": 13, "name": "LMS - Rank", "rank": 15827, "score": 2521 },
{ "id": 14, "name": "PvP Arena - Rank", "rank": -1, "score": -1 },
{ "id": 15, "name": "Soul Wars Zeal", "rank": 7898, "score": 14780 },
{ "id": 16, "name": "Rifts closed", "rank": 5421, "score": 1060 },
{ "id": 17, "name": "Colosseum Glory", "rank": 7, "score": 24522 },
{ "id": 18, "name": "Abyssal Sire", "rank": 2902, "score": 2192 },
{ "id": 19, "name": "Alchemical Hydra", "rank": 94883, "score": 594 },
{ "id": 20, "name": "Artio", "rank": 34000, "score": 143 },
{ "id": 21, "name": "Barrows Chests", "rank": 3834, "score": 1830 },
{ "id": 22, "name": "Bryophyta", "rank": 218428, "score": 5 },
{ "id": 23, "name": "Callisto", "rank": 36717, "score": 293 },
{ "id": 24, "name": "Calvar'ion", "rank": 74959, "score": 58 },
{ "id": 25, "name": "Cerberus", "rank": 19421, "score": 1905 },
{ "id": 26, "name": "Chambers of Xeric", "rank": 186551, "score": 34 },
{
"id": 27,
"name": "Chambers of Xeric: Challenge Mode",
"rank": 2571,
"score": 515
},
{ "id": 28, "name": "Chaos Elemental", "rank": 85921, "score": 50 },
{ "id": 29, "name": "Chaos Fanatic", "rank": 154580, "score": 26 },
{ "id": 30, "name": "Commander Zilyana", "rank": 54677, "score": 287 },
{ "id": 31, "name": "Corporeal Beast", "rank": 2586, "score": 2000 },
{ "id": 32, "name": "Crazy Archaeologist", "rank": 153371, "score": 41 },
{ "id": 33, "name": "Dagannoth Prime", "rank": 33154, "score": 707 },
{ "id": 34, "name": "Dagannoth Rex", "rank": 43433, "score": 698 },
{ "id": 35, "name": "Dagannoth Supreme", "rank": 33088, "score": 714 },
{ "id": 36, "name": "Deranged Archaeologist", "rank": 81968, "score": 25 },
{ "id": 37, "name": "Duke Sucellus", "rank": 12249, "score": 731 },
{ "id": 38, "name": "General Graardor", "rank": 20424, "score": 1228 },
{ "id": 39, "name": "Giant Mole", "rank": 70188, "score": 474 },
{ "id": 40, "name": "Grotesque Guardians", "rank": 2248, "score": 2140 },
{ "id": 41, "name": "Hespori", "rank": 11128, "score": 226 },
{ "id": 42, "name": "Kalphite Queen", "rank": 2117, "score": 2046 },
{ "id": 43, "name": "King Black Dragon", "rank": 50095, "score": 580 },
{ "id": 44, "name": "Kraken", "rank": 120438, "score": 1908 },
{ "id": 45, "name": "Kree'Arra", "rank": 10997, "score": 920 },
{ "id": 46, "name": "K'ril Tsutsaroth", "rank": 2326, "score": 1444 },
{ "id": 47, "name": "Lunar Chests", "rank": -1, "score": -1 },
{ "id": 48, "name": "Mimic", "rank": 700, "score": 40 },
{ "id": 49, "name": "Nex", "rank": 31471, "score": 626 },
{ "id": 50, "name": "Nightmare", "rank": 4119, "score": 749 },
{ "id": 51, "name": "Phosani's Nightmare", "rank": 4349, "score": 500 },
{ "id": 52, "name": "Obor", "rank": 1078, "score": 217 },
{ "id": 53, "name": "Phantom Muspah", "rank": 20126, "score": 301 },
{ "id": 54, "name": "Sarachnis", "rank": 70523, "score": 119 },
{ "id": 55, "name": "Scorpia", "rank": 114632, "score": 26 },
{ "id": 56, "name": "Scurrius", "rank": 2945, "score": 1245 },
{ "id": 57, "name": "Skotizo", "rank": 385801, "score": 9 },
{ "id": 58, "name": "Sol Heredit", "rank": -1, "score": -1 },
{ "id": 59, "name": "Spindel", "rank": 2412, "score": 1569 },
{ "id": 60, "name": "Tempoross", "rank": 43814, "score": 273 },
{ "id": 61, "name": "The Gauntlet", "rank": 207949, "score": 7 },
{ "id": 62, "name": "The Corrupted Gauntlet", "rank": 86335, "score": 201 },
{ "id": 63, "name": "The Leviathan", "rank": 7905, "score": 601 },
{ "id": 64, "name": "The Whisperer", "rank": 732, "score": 1783 },
{ "id": 65, "name": "Theatre of Blood", "rank": 21039, "score": 310 },
{
"id": 66,
"name": "Theatre of Blood: Hard Mode",
"rank": 4949,
"score": 192
},
{
"id": 67,
"name": "Thermonuclear Smoke Devil",
"rank": 7584,
"score": 3066
},
{ "id": 68, "name": "Tombs of Amascut", "rank": 109360, "score": 36 },
{
"id": 69,
"name": "Tombs of Amascut: Expert Mode",
"rank": 160,
"score": 1500
},
{ "id": 70, "name": "TzKal-Zuk", "rank": 6733, "score": 7 },
{ "id": 71, "name": "TzTok-Jad", "rank": 131888, "score": 8 },
{ "id": 72, "name": "Vardorvis", "rank": 3128, "score": 2113 },
{ "id": 73, "name": "Venenatis", "rank": 209, "score": 7096 },
{ "id": 74, "name": "Vet'ion", "rank": 75591, "score": 51 },
{ "id": 75, "name": "Vorkath", "rank": 15167, "score": 2780 },
{ "id": 76, "name": "Wintertodt", "rank": 911570, "score": 62 },
{ "id": 77, "name": "Zalcano", "rank": 173436, "score": 25 },
{ "id": 78, "name": "Zulrah", "rank": 233645, "score": 275 }
]
}

View File

@@ -12,10 +12,7 @@ import {
getSkillPageURL, getSkillPageURL,
getStatsURL, getStatsURL,
BOSSES, BOSSES,
InvalidFormatError, INVALID_FORMAT_ERROR
BH_MODES,
parseJsonStats,
HiscoresResponse
} from '../src/index'; } from '../src/index';
const B0ATY_NAME = 'B0ATY'; const B0ATY_NAME = 'B0ATY';
@@ -27,13 +24,7 @@ const LYNX_TITAN_FORMATTED_NAME = 'Lynx Titan';
const attackTopPage = readFileSync(`${__dirname}/attackTopPage.html`, 'utf8'); const attackTopPage = readFileSync(`${__dirname}/attackTopPage.html`, 'utf8');
const b0atyNamePage = readFileSync(`${__dirname}/b0atyNamePage.html`, 'utf8'); const b0atyNamePage = readFileSync(`${__dirname}/b0atyNamePage.html`, 'utf8');
const b0atyStatsCsv = readFileSync(`${__dirname}/b0atyStats.csv`, 'utf8'); const lynxTitanStats = readFileSync(`${__dirname}/lynxTitanStats.csv`, 'utf8');
const b0atyStatsJson: HiscoresResponse = JSON.parse(
readFileSync(`${__dirname}/b0atyStats.json`, 'utf8')
);
const lynxTitanStats = JSON.parse(
readFileSync(`${__dirname}/lynxTitanStats.json`, 'utf8')
);
const lynxTitanNamePage = readFileSync( const lynxTitanNamePage = readFileSync(
`${__dirname}/lynxTitanNamePage.html`, `${__dirname}/lynxTitanNamePage.html`,
'utf8' 'utf8'
@@ -54,7 +45,7 @@ jest.spyOn(axios, 'get').mockImplementation((url) => {
if (getSkillPageURL('main', 'attack', 1) === url) { if (getSkillPageURL('main', 'attack', 1) === url) {
return Promise.resolve({ data: attackTopPage }); return Promise.resolve({ data: attackTopPage });
} }
if (getStatsURL('main', LYNX_TITAN_FORMATTED_NAME, true) === url) { if (getStatsURL('main', LYNX_TITAN_FORMATTED_NAME) === url) {
return Promise.resolve({ status: 200, data: lynxTitanStats }); return Promise.resolve({ status: 200, data: lynxTitanStats });
} }
throw new Error(`No mock response for URL: ${url}`); throw new Error(`No mock response for URL: ${url}`);
@@ -86,9 +77,6 @@ test('Parse CSV to json', () => {
810,99,37688883 810,99,37688883
92,99,32005622 92,99,32005622
23423,478 23423,478
89554,301
89914,35
99834,25
99831,23 99831,23
89912,37 89912,37
32,12148 32,12148
@@ -99,17 +87,13 @@ test('Parse CSV to json', () => {
392,250 392,250
1,6143 1,6143
4814,898 4814,898
13,4057
37,225 37,225
25,1110 23,467
7827,43249
382,2780 382,2780
944,3000 944,3000
704,892
1981,1452 1981,1452
4981,23 4981,23
888,1046 888,1046
444,792
613,4856 613,4856
102,4038 102,4038
156,334 156,334
@@ -122,7 +106,6 @@ test('Parse CSV to json', () => {
4342,1655 4342,1655
966,2951 966,2951
10151,1 10151,1
1289,2477
1288,2407 1288,2407
377,4669 377,4669
545,1567 545,1567
@@ -132,32 +115,22 @@ test('Parse CSV to json', () => {
41643,1477 41643,1477
625,2391 625,2391
120,2981 120,2981
54,2958
1,109 1,109
99,39002 99,39002
3,22666 3,22666
2,84 2,84
26,323 26,323
44,6233
201,1101 201,1101
82,3404 82,3404
555,7079
5085,61 5085,61
8731,1423
678,903
18823,23 18823,23
63,375 63,375
2870,6 2870,6
2871,7
2872,8
6984,138 6984,138
23,923141 23,923141
4043,2000 4043,2000
4073,1020
289,13070
489,8 489,8
967,47 967,47
968,48
11155,223 11155,223
1940,272 1940,272
8623,1340 8623,1340
@@ -193,18 +166,13 @@ test('Parse CSV to json', () => {
construction: { rank: 92, level: 99, xp: 32005622 } construction: { rank: 92, level: 99, xp: 32005622 }
}, },
leaguePoints: { rank: 23423, score: 478 }, leaguePoints: { rank: 23423, score: 478 },
deadmanPoints: { rank: 89554, score: 301 },
bountyHunter: { bountyHunter: {
hunterV2: { rank: 89914, score: 35 },
rogueV2: { rank: 99834, score: 25 },
hunter: { rank: 99831, score: 23 }, hunter: { rank: 99831, score: 23 },
rogue: { rank: 89912, score: 37 } rogue: { rank: 89912, score: 37 }
}, },
lastManStanding: { rank: 4814, score: 898 }, lastManStanding: { rank: 4814, score: 898 },
pvpArena: { rank: 13, score: 4057 },
soulWarsZeal: { rank: 37, score: 225 }, soulWarsZeal: { rank: 37, score: 225 },
riftsClosed: { rank: 25, score: 1110 }, riftsClosed: { rank: 23, score: 467 },
colosseumGlory: { rank: 7827, score: 43249 },
clues: { clues: {
all: { rank: 32, score: 12148 }, all: { rank: 32, score: 12148 },
beginner: { rank: 3105, score: 76 }, beginner: { rank: 3105, score: 76 },
@@ -217,11 +185,9 @@ test('Parse CSV to json', () => {
bosses: { bosses: {
abyssalSire: { rank: 382, score: 2780 }, abyssalSire: { rank: 382, score: 2780 },
alchemicalHydra: { rank: 944, score: 3000 }, alchemicalHydra: { rank: 944, score: 3000 },
artio: { rank: 704, score: 892 },
barrows: { rank: 1981, score: 1452 }, barrows: { rank: 1981, score: 1452 },
bryophyta: { rank: 4981, score: 23 }, bryophyta: { rank: 4981, score: 23 },
callisto: { rank: 888, score: 1046 }, callisto: { rank: 888, score: 1046 },
calvarion: { rank: 444, score: 792 },
cerberus: { rank: 613, score: 4856 }, cerberus: { rank: 613, score: 4856 },
chambersOfXeric: { rank: 102, score: 4038 }, chambersOfXeric: { rank: 102, score: 4038 },
chambersOfXericChallengeMode: { rank: 156, score: 334 }, chambersOfXericChallengeMode: { rank: 156, score: 334 },
@@ -234,7 +200,6 @@ test('Parse CSV to json', () => {
dagannothRex: { rank: 4342, score: 1655 }, dagannothRex: { rank: 4342, score: 1655 },
dagannothSupreme: { rank: 966, score: 2951 }, dagannothSupreme: { rank: 966, score: 2951 },
derangedArchaeologist: { rank: 10151, score: 1 }, derangedArchaeologist: { rank: 10151, score: 1 },
dukeSucellus: { rank: 1289, score: 2477 },
generalGraardor: { rank: 1288, score: 2407 }, generalGraardor: { rank: 1288, score: 2407 },
giantMole: { rank: 377, score: 4669 }, giantMole: { rank: 377, score: 4669 },
grotesqueGuardians: { rank: 545, score: 1567 }, grotesqueGuardians: { rank: 545, score: 1567 },
@@ -244,32 +209,22 @@ test('Parse CSV to json', () => {
kraken: { rank: 41643, score: 1477 }, kraken: { rank: 41643, score: 1477 },
kreeArra: { rank: 625, score: 2391 }, kreeArra: { rank: 625, score: 2391 },
krilTsutsaroth: { rank: 120, score: 2981 }, krilTsutsaroth: { rank: 120, score: 2981 },
lunarChests: { rank: 54, score: 2958 },
mimic: { rank: 1, score: 109 }, mimic: { rank: 1, score: 109 },
nex: { rank: 99, score: 39002 }, nex: { rank: 99, score: 39002 },
nightmare: { rank: 3, score: 22666 }, nightmare: { rank: 3, score: 22666 },
phosanisNightmare: { rank: 2, score: 84 }, phosanisNightmare: { rank: 2, score: 84 },
obor: { rank: 26, score: 323 }, obor: { rank: 26, score: 323 },
phantomMuspah: { rank: 44, score: 6233 },
sarachnis: { rank: 201, score: 1101 }, sarachnis: { rank: 201, score: 1101 },
scorpia: { rank: 82, score: 3404 }, scorpia: { rank: 82, score: 3404 },
scurrius: { rank: 555, score: 7079 },
skotizo: { rank: 5085, score: 61 }, skotizo: { rank: 5085, score: 61 },
solHeredit: { rank: 8731, score: 1423 },
spindel: { rank: 678, score: 903 },
tempoross: { rank: 18823, score: 23 }, tempoross: { rank: 18823, score: 23 },
gauntlet: { rank: 63, score: 375 }, gauntlet: { rank: 63, score: 375 },
corruptedGauntlet: { rank: 2870, score: 6 }, corruptedGauntlet: { rank: 2870, score: 6 },
leviathan: { rank: 2871, score: 7 },
whisperer: { rank: 2872, score: 8 },
theatreOfBlood: { rank: 6984, score: 138 }, theatreOfBlood: { rank: 6984, score: 138 },
theatreOfBloodHardMode: { rank: 23, score: 923141 }, theatreOfBloodHardMode: { rank: 23, score: 923141 },
thermonuclearSmokeDevil: { rank: 4043, score: 2000 }, thermonuclearSmokeDevil: { rank: 4043, score: 2000 },
tombsOfAmascut: { rank: 4073, score: 1020 },
tombsOfAmascutExpertMode: { rank: 289, score: 13070 },
tzKalZuk: { rank: 489, score: 8 }, tzKalZuk: { rank: 489, score: 8 },
tzTokJad: { rank: 967, score: 47 }, tzTokJad: { rank: 967, score: 47 },
vardorvis: { rank: 968, score: 48 },
venenatis: { rank: 11155, score: 223 }, venenatis: { rank: 11155, score: 223 },
vetion: { rank: 1940, score: 272 }, vetion: { rank: 1940, score: 272 },
vorkath: { rank: 8623, score: 1340 }, vorkath: { rank: 8623, score: 1340 },
@@ -286,12 +241,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(
InvalidFormatError INVALID_FORMAT_ERROR
); );
}); });
test('Parse invalid CSV', () => { test('Parse invalid CSV', () => {
expect(() => parseStats('invalid')).toThrow(InvalidFormatError); expect(() => parseStats('invalid')).toThrow(INVALID_FORMAT_ERROR);
}); });
describe('Get name format', () => { describe('Get name format', () => {
@@ -506,7 +461,7 @@ test('Get non-existent player', async () => {
}); });
test('Get stats by gamemode', async () => { test('Get stats by gamemode', async () => {
const { skills, bosses, bountyHunter } = await getStatsByGamemode( const { skills, bosses } = await getStatsByGamemode(
LYNX_TITAN_FORMATTED_NAME LYNX_TITAN_FORMATTED_NAME
); );
@@ -539,67 +494,6 @@ test('Get stats by gamemode', async () => {
const bossKeys = Object.keys(bosses); const bossKeys = Object.keys(bosses);
expect(bossKeys).toStrictEqual(BOSSES); expect(bossKeys).toStrictEqual(BOSSES);
const bountyHunterKeys = Object.keys(bountyHunter);
expect(bountyHunterKeys).toStrictEqual(BH_MODES);
expect.assertions(3); expect.assertions(2);
});
describe('Get stats options', () => {
const rsn = 'player';
let axiosMock: jest.Mock;
beforeEach(() => {
axios.get = jest.fn(
(url) =>
new Promise<any>((resolve) => {
resolve(
url === getPlayerTableURL('main', rsn)
? { data: lynxTitanNamePage }
: { status: 200, data: lynxTitanStats }
);
})
);
axiosMock = axios.get as any;
axiosMock.mockClear();
});
it('fetches all gamemodes and formatted RSN when no options provided', async () => {
await getStats(rsn);
expect(axiosMock.mock.calls.map((val) => val[0])).toEqual([
getStatsURL('main', rsn, true),
getPlayerTableURL('main', rsn),
getStatsURL('ironman', rsn, true),
getStatsURL('hardcore', rsn, true),
getStatsURL('ultimate', rsn, true)
]);
});
it('skips fetching formatted RSN when option is provided', async () => {
await getStats(rsn, { shouldGetFormattedRsn: false });
expect(
axiosMock.mock.calls.some(
(val) => val[0] === getPlayerTableURL('main', rsn)
)
).toBeFalsy();
});
it('skips fetching game mode when option is provided', async () => {
await getStats(rsn, {
otherGamemodes: ['ironman', 'ultimate']
});
expect(
axiosMock.mock.calls.some(
(val) => val[0] === getStatsURL('hardcore', rsn)
)
).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 () => {
const csvOutput = parseStats(b0atyStatsCsv);
const jsonOutput = parseJsonStats(b0atyStatsJson);
expect(csvOutput).toEqual(jsonOutput);
}); });

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,85 @@
1,2277,4600000000
15,99,200000000
28,99,200000000
18,99,200000000
7,99,200000000
8,99,200000000
11,99,200000000
32,99,200000000
159,99,200000000
15,99,200000000
12,99,200000000
9,99,200000000
48,99,200000000
4,99,200000000
3,99,200000000
25,99,200000000
5,99,200000000
23,99,200000000
12,99,200000000
2,99,200000000
19,99,200000000
7,99,200000000
4,99,200000000
4,99,200000000
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
347584,22
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
Can't render this file because it has a wrong number of fields in line 25.

View File

@@ -1,152 +0,0 @@
{
"skills": [
{ "id": 0, "name": "Overall", "rank": 1, "level": 2277, "xp": 4600000000 },
{ "id": 1, "name": "Attack", "rank": 15, "level": 99, "xp": 200000000 },
{ "id": 2, "name": "Defence", "rank": 28, "level": 99, "xp": 200000000 },
{ "id": 3, "name": "Strength", "rank": 18, "level": 99, "xp": 200000000 },
{ "id": 4, "name": "Hitpoints", "rank": 7, "level": 99, "xp": 200000000 },
{ "id": 5, "name": "Ranged", "rank": 8, "level": 99, "xp": 200000000 },
{ "id": 6, "name": "Prayer", "rank": 11, "level": 99, "xp": 200000000 },
{ "id": 7, "name": "Magic", "rank": 30, "level": 99, "xp": 200000000 },
{ "id": 8, "name": "Cooking", "rank": 154, "level": 99, "xp": 200000000 },
{
"id": 9,
"name": "Woodcutting",
"rank": 15,
"level": 99,
"xp": 200000000
},
{ "id": 10, "name": "Fletching", "rank": 12, "level": 99, "xp": 200000000 },
{ "id": 11, "name": "Fishing", "rank": 9, "level": 99, "xp": 200000000 },
{
"id": 12,
"name": "Firemaking",
"rank": 48,
"level": 99,
"xp": 200000000
},
{ "id": 13, "name": "Crafting", "rank": 4, "level": 99, "xp": 200000000 },
{ "id": 14, "name": "Smithing", "rank": 3, "level": 99, "xp": 200000000 },
{ "id": 15, "name": "Mining", "rank": 24, "level": 99, "xp": 200000000 },
{ "id": 16, "name": "Herblore", "rank": 5, "level": 99, "xp": 200000000 },
{ "id": 17, "name": "Agility", "rank": 24, "level": 99, "xp": 200000000 },
{ "id": 18, "name": "Thieving", "rank": 12, "level": 99, "xp": 200000000 },
{ "id": 19, "name": "Slayer", "rank": 2, "level": 99, "xp": 200000000 },
{ "id": 20, "name": "Farming", "rank": 19, "level": 99, "xp": 200000000 },
{ "id": 21, "name": "Runecraft", "rank": 6, "level": 99, "xp": 200000000 },
{ "id": 22, "name": "Hunter", "rank": 4, "level": 99, "xp": 200000000 },
{
"id": 23,
"name": "Construction",
"rank": 4,
"level": 99,
"xp": 200000000
}
],
"activities": [
{ "id": 0, "name": "League Points", "rank": -1, "score": -1 },
{ "id": 1, "name": "Deadman Points", "rank": -1, "score": -1 },
{ "id": 2, "name": "Bounty Hunter - Hunter", "rank": -1, "score": -1 },
{ "id": 3, "name": "Bounty Hunter - Rogue", "rank": -1, "score": -1 },
{
"id": 4,
"name": "Bounty Hunter (Legacy) - Hunter",
"rank": -1,
"score": -1
},
{
"id": 5,
"name": "Bounty Hunter (Legacy) - Rogue",
"rank": -1,
"score": -1
},
{ "id": 6, "name": "Clue Scrolls (all)", "rank": 832420, "score": 22 },
{ "id": 7, "name": "Clue Scrolls (beginner)", "rank": -1, "score": -1 },
{ "id": 8, "name": "Clue Scrolls (easy)", "rank": -1, "score": -1 },
{ "id": 9, "name": "Clue Scrolls (medium)", "rank": -1, "score": -1 },
{ "id": 10, "name": "Clue Scrolls (hard)", "rank": 517091, "score": 22 },
{ "id": 11, "name": "Clue Scrolls (elite)", "rank": -1, "score": -1 },
{ "id": 12, "name": "Clue Scrolls (master)", "rank": -1, "score": -1 },
{ "id": 13, "name": "LMS - Rank", "rank": -1, "score": -1 },
{ "id": 14, "name": "PvP Arena - Rank", "rank": -1, "score": -1 },
{ "id": 15, "name": "Soul Wars Zeal", "rank": -1, "score": -1 },
{ "id": 16, "name": "Rifts closed", "rank": -1, "score": -1 },
{ "id": 17, "name": "Colosseum Glory", "rank": -1, "score": -1 },
{ "id": 18, "name": "Abyssal Sire", "rank": -1, "score": -1 },
{ "id": 19, "name": "Alchemical Hydra", "rank": -1, "score": -1 },
{ "id": 20, "name": "Artio", "rank": -1, "score": -1 },
{ "id": 21, "name": "Barrows Chests", "rank": -1, "score": -1 },
{ "id": 22, "name": "Bryophyta", "rank": -1, "score": -1 },
{ "id": 23, "name": "Callisto", "rank": -1, "score": -1 },
{ "id": 24, "name": "Calvar'ion", "rank": -1, "score": -1 },
{ "id": 25, "name": "Cerberus", "rank": -1, "score": -1 },
{ "id": 26, "name": "Chambers of Xeric", "rank": -1, "score": -1 },
{
"id": 27,
"name": "Chambers of Xeric: Challenge Mode",
"rank": -1,
"score": -1
},
{ "id": 28, "name": "Chaos Elemental", "rank": -1, "score": -1 },
{ "id": 29, "name": "Chaos Fanatic", "rank": -1, "score": -1 },
{ "id": 30, "name": "Commander Zilyana", "rank": -1, "score": -1 },
{ "id": 31, "name": "Corporeal Beast", "rank": -1, "score": -1 },
{ "id": 32, "name": "Crazy Archaeologist", "rank": -1, "score": -1 },
{ "id": 33, "name": "Dagannoth Prime", "rank": -1, "score": -1 },
{ "id": 34, "name": "Dagannoth Rex", "rank": -1, "score": -1 },
{ "id": 35, "name": "Dagannoth Supreme", "rank": -1, "score": -1 },
{ "id": 36, "name": "Deranged Archaeologist", "rank": -1, "score": -1 },
{ "id": 37, "name": "Duke Sucellus", "rank": -1, "score": -1 },
{ "id": 38, "name": "General Graardor", "rank": -1, "score": -1 },
{ "id": 39, "name": "Giant Mole", "rank": -1, "score": -1 },
{ "id": 40, "name": "Grotesque Guardians", "rank": -1, "score": -1 },
{ "id": 41, "name": "Hespori", "rank": -1, "score": -1 },
{ "id": 42, "name": "Kalphite Queen", "rank": -1, "score": -1 },
{ "id": 43, "name": "King Black Dragon", "rank": -1, "score": -1 },
{ "id": 44, "name": "Kraken", "rank": -1, "score": -1 },
{ "id": 45, "name": "Kree'Arra", "rank": -1, "score": -1 },
{ "id": 46, "name": "K'ril Tsutsaroth", "rank": -1, "score": -1 },
{ "id": 47, "name": "Lunar Chests", "rank": -1, "score": -1 },
{ "id": 48, "name": "Mimic", "rank": -1, "score": -1 },
{ "id": 49, "name": "Nex", "rank": -1, "score": -1 },
{ "id": 50, "name": "Nightmare", "rank": -1, "score": -1 },
{ "id": 51, "name": "Phosani's Nightmare", "rank": -1, "score": -1 },
{ "id": 52, "name": "Obor", "rank": -1, "score": -1 },
{ "id": 53, "name": "Phantom Muspah", "rank": -1, "score": -1 },
{ "id": 54, "name": "Sarachnis", "rank": -1, "score": -1 },
{ "id": 55, "name": "Scorpia", "rank": -1, "score": -1 },
{ "id": 56, "name": "Scurrius", "rank": -1, "score": -1 },
{ "id": 57, "name": "Skotizo", "rank": -1, "score": -1 },
{ "id": 58, "name": "Sol Heredit", "rank": -1, "score": -1 },
{ "id": 59, "name": "Spindel", "rank": -1, "score": -1 },
{ "id": 60, "name": "Tempoross", "rank": -1, "score": -1 },
{ "id": 61, "name": "The Gauntlet", "rank": -1, "score": -1 },
{ "id": 62, "name": "The Corrupted Gauntlet", "rank": -1, "score": -1 },
{ "id": 63, "name": "The Leviathan", "rank": -1, "score": -1 },
{ "id": 64, "name": "The Whisperer", "rank": -1, "score": -1 },
{ "id": 65, "name": "Theatre of Blood", "rank": -1, "score": -1 },
{
"id": 66,
"name": "Theatre of Blood: Hard Mode",
"rank": -1,
"score": -1
},
{ "id": 67, "name": "Thermonuclear Smoke Devil", "rank": -1, "score": -1 },
{ "id": 68, "name": "Tombs of Amascut", "rank": -1, "score": -1 },
{
"id": 69,
"name": "Tombs of Amascut: Expert Mode",
"rank": -1,
"score": -1
},
{ "id": 70, "name": "TzKal-Zuk", "rank": -1, "score": -1 },
{ "id": 71, "name": "TzTok-Jad", "rank": 251, "score": 186 },
{ "id": 72, "name": "Vardorvis", "rank": -1, "score": -1 },
{ "id": 73, "name": "Venenatis", "rank": -1, "score": -1 },
{ "id": 74, "name": "Vet'ion", "rank": -1, "score": -1 },
{ "id": 75, "name": "Vorkath", "rank": -1, "score": -1 },
{ "id": 76, "name": "Wintertodt", "rank": -1, "score": -1 },
{ "id": 77, "name": "Zalcano", "rank": -1, "score": -1 },
{ "id": 78, "name": "Zulrah", "rank": -1, "score": -1 }
]
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "osrs-json-hiscores", "name": "osrs-json-hiscores",
"version": "2.18.0", "version": "2.7.0",
"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",
@@ -30,9 +30,6 @@
"stats", "stats",
"skills" "skills"
], ],
"publishConfig": {
"registry": "https://registry.npmjs.org"
},
"author": "maxswa", "author": "maxswa",
"license": "ISC", "license": "ISC",
"bugs": { "bugs": {
@@ -61,23 +58,18 @@
"@typescript-eslint" "@typescript-eslint"
], ],
"extends": [ "extends": [
"airbnb-base", "eslint-config-airbnb-typescript",
"airbnb-typescript/base",
"prettier" "prettier"
], ],
"ignorePatterns": [ "ignorePatterns": [
"**/@types/*" "**/@types/*"
], ]
"rules": {
"max-classes-per-file": "off"
}
}, },
"prettier": { "prettier": {
"trailingComma": "none", "trailingComma": "none",
"tabWidth": 2, "tabWidth": 2,
"semi": true, "semi": true,
"singleQuote": true, "singleQuote": true
"endOfLine": "auto"
}, },
"jest": { "jest": {
"transform": { "transform": {
@@ -95,25 +87,28 @@
"testEnvironment": "node" "testEnvironment": "node"
}, },
"dependencies": { "dependencies": {
"axios": "^1.6.2", "axios": "^0.21.1",
"jsdom": "^22.1.0", "jsdom": "^16.3.0",
"useragent-generator": "^1.1.0" "useragent-generator": "^1.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^29.5.3", "@types/jest": "^26.0.21",
"@types/jsdom": "^21.1.1", "@types/jsdom": "^16.2.3",
"@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/eslint-plugin": "^4.19.0",
"@typescript-eslint/parser": "^6.0.0", "@typescript-eslint/parser": "^4.19.0",
"eslint": "^8.44.0", "eslint": "^7.22.0",
"eslint-config-airbnb-typescript": "^17.1.0", "eslint-config-airbnb-typescript": "^12.3.1",
"eslint-config-prettier": "^8.8.0", "eslint-config-prettier": "^8.1.0",
"eslint-plugin-import": "^2.27.5", "eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-react": "^7.23.1",
"eslint-plugin-react-hooks": "^4.2.0",
"husky": "^5.2.0", "husky": "^5.2.0",
"jest": "^29.6.1", "jest": "^26.6.3",
"lint-staged": "^13.2.3", "lint-staged": "^10.5.4",
"np": "^7.7.0", "np": "6.5.0",
"prettier": "^3.0.0", "prettier": "^2.2.1",
"ts-jest": "^29.1.1", "ts-jest": "^26.5.4",
"typescript": "^5.1.6" "typescript": "^4.2.3"
} }
} }

View File

@@ -1,5 +1,4 @@
import axios, { AxiosRequestConfig } from 'axios'; import { JSDOM } from 'jsdom';
import { BinaryData, JSDOM } from 'jsdom';
import { import {
Player, Player,
Activity, Activity,
@@ -13,9 +12,7 @@ import {
PlayerSkillRow, PlayerSkillRow,
ActivityName, ActivityName,
PlayerActivityRow, PlayerActivityRow,
Bosses, Bosses
GetStatsOptions,
HiscoresResponse
} from './types'; } from './types';
import { import {
getStatsURL, getStatsURL,
@@ -31,157 +28,42 @@ import {
getActivityPageURL, getActivityPageURL,
httpGet, httpGet,
BOSSES, BOSSES,
InvalidFormatError, INVALID_FORMAT_ERROR,
PlayerNotFoundError, EXTRA_ACTIVITY_COUNT
HiScoresError,
validateRSN,
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,
FORMATTED_DEADMAN_POINTS,
FORMATTED_COLOSSEUM_GLORY
} from './utils'; } 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 (err) {
if (!axios.isAxiosError(err)) throw err;
if (err.response?.status === 404) throw new PlayerNotFoundError();
throw new HiScoresError();
}
}
/** /**
* Screen scrapes the hiscores to get the formatted rsn of a player. * Screen scrapes the hiscores to get the formatted rsn of a player.
* *
* @param rsn Username of the player. * @param rsn Username of the player.
* @param config Optional axios request config object.
* @returns Formatted version of the rsn. * @returns Formatted version of the rsn.
*/ */
export async function getRSNFormat( export async function getRSNFormat(rsn: string): Promise<string> {
rsn: string, if (typeof rsn !== 'string') {
config?: AxiosRequestConfig throw Error('RSN must be a string');
): Promise<string> { } else if (!/^[a-zA-Z0-9 _-]+$/.test(rsn)) {
validateRSN(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); const url = getPlayerTableURL('main', rsn);
try { try {
const response = await httpGet<string | Buffer | BinaryData | undefined>( const response = await httpGet(url);
url,
config
);
const dom = new JSDOM(response.data); const dom = new JSDOM(response.data);
const anchor = dom.window.document.querySelector( const spans = dom.window.document.querySelectorAll(
'.personal-hiscores__row.personal-hiscores__row--type-highlight a' 'span[style="color:#AA0022;"]'
); );
if (anchor) { if (spans.length >= 2) {
return rsnFromElement(anchor); const nameSpan = spans[1];
return rsnFromElement(nameSpan);
} }
throw new PlayerNotFoundError(); throw Error('Player not found');
} catch { } catch {
throw new HiScoresError(); throw Error('Player not found');
} }
} }
/**
* 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.toLowerCase() === formattedName.toLowerCase()
);
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.toLowerCase() === FORMATTED_SKILL_NAMES[skillName].toLowerCase()
);
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 deadmanPoints = getActivity(FORMATTED_DEADMAN_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 colosseumGlory = getActivity(FORMATTED_COLOSSEUM_GLORY);
const stats: Stats = {
skills,
leaguePoints,
deadmanPoints,
bountyHunter,
lastManStanding,
pvpArena,
soulWarsZeal,
riftsClosed,
colosseumGlory,
clues,
bosses
};
return stats;
}
/** /**
* Parses CSV string of raw stats and returns a stats object. * Parses CSV string of raw stats and returns a stats object.
* *
@@ -194,8 +76,15 @@ export function parseStats(csv: string): Stats {
.filter((entry) => !!entry) .filter((entry) => !!entry)
.map((stat) => stat.split(',')); .map((stat) => stat.split(','));
if (splitCSV.length !== SKILLS.length + ACTIVITIES.length) { if (
throw new InvalidFormatError(); splitCSV.length !==
SKILLS.length +
BH_MODES.length +
CLUES.length +
BOSSES.length +
EXTRA_ACTIVITY_COUNT
) {
throw Error(INVALID_FORMAT_ERROR);
} }
const skillObjects: Skill[] = splitCSV const skillObjects: Skill[] = splitCSV
@@ -221,11 +110,13 @@ export function parseStats(csv: string): Stats {
return activity; return activity;
}); });
const [leaguePoints, deadmanPoints] = activityObjects.splice(0, 2); const [leaguePoints] = activityObjects.splice(0, 1);
const bhObjects = activityObjects.splice(0, BH_MODES.length); const bhObjects = activityObjects.splice(0, BH_MODES.length);
const clueObjects = activityObjects.splice(0, CLUES.length); const clueObjects = activityObjects.splice(0, CLUES.length);
const [lastManStanding, pvpArena, soulWarsZeal, riftsClosed, colosseumGlory] = const [lastManStanding, soulWarsZeal, riftsClosed] = activityObjects.splice(
activityObjects.splice(0, 5); 0,
3
);
const bossObjects = activityObjects.splice(0, BOSSES.length); const bossObjects = activityObjects.splice(0, BOSSES.length);
const skills: Skills = skillObjects.reduce<Skills>((prev, curr, index) => { const skills: Skills = skillObjects.reduce<Skills>((prev, curr, index) => {
@@ -255,13 +146,10 @@ export function parseStats(csv: string): Stats {
const stats: Stats = { const stats: Stats = {
skills, skills,
leaguePoints, leaguePoints,
deadmanPoints,
bountyHunter, bountyHunter,
lastManStanding, lastManStanding,
pvpArena,
soulWarsZeal, soulWarsZeal,
riftsClosed, riftsClosed,
colosseumGlory,
clues, clues,
bosses bosses
}; };
@@ -278,31 +166,25 @@ export function parseStats(csv: string): Stats {
* @param rsn Username of the player. * @param rsn Username of the player.
* @returns Player object. * @returns Player object.
*/ */
export async function getStats( export async function getStats(rsn: string): Promise<Player> {
rsn: string, if (typeof rsn !== 'string') {
options?: GetStatsOptions throw Error('RSN must be a string');
): Promise<Player> { } else if (!/^[a-zA-Z0-9 _]+$/.test(rsn)) {
validateRSN(rsn); throw Error('RSN contains invalid character');
const otherGamemodes = options?.otherGamemodes ?? [ } else if (rsn.length > 12 || rsn.length < 1) {
'ironman', throw Error('RSN must be between 1 and 12 characters');
'hardcore', }
'ultimate'
];
const shouldGetFormattedRsn = options?.shouldGetFormattedRsn ?? true;
const main = await getOfficialStats(rsn, 'main', options?.axiosConfigs?.main); 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)
]);
const getModeStats = async ( const [ironRes, hcRes, ultRes, formattedName] = otherResponses;
mode: Extract<Gamemode, 'ironman' | 'hardcore' | 'ultimate'>
): Promise<HiscoresResponse | undefined> =>
otherGamemodes.includes(mode)
? getOfficialStats(rsn, mode, options?.axiosConfigs?.[mode]).catch(
() => 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,
@@ -311,42 +193,45 @@ export async function getStats(
deulted: false, deulted: false,
deironed: false deironed: false
}; };
player.main = parseJsonStats(main); player.main = parseStats(mainRes.data);
const iron = await getModeStats('ironman'); if (ironRes.status === 200) {
if (iron) { player.ironman = parseStats(ironRes.data);
player.ironman = parseJsonStats(iron); if (hcRes.status === 200) {
const hc = await getModeStats('hardcore');
const ult = await getModeStats('ultimate');
if (hc) {
player.mode = 'hardcore'; player.mode = 'hardcore';
player.hardcore = parseJsonStats(hc); player.hardcore = parseStats(hcRes.data);
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'; player.mode = 'ironman';
} }
if (player.main.skills.overall.xp !== player.ironman.skills.overall.xp) { if (
player.main.skills.overall.xp !== player.ironman.skills.overall.xp
) {
player.deironed = true; player.deironed = true;
player.mode = 'main'; player.mode = 'main';
} }
} else if (ult) { } else if (ultRes.status === 200) {
player.mode = 'ultimate'; player.mode = 'ultimate';
player.ultimate = parseJsonStats(ult); player.ultimate = parseStats(ultRes.data);
if ( if (
player.ironman.skills.overall.xp !== player.ultimate.skills.overall.xp player.ironman.skills.overall.xp !== player.ultimate.skills.overall.xp
) { ) {
player.deulted = true; player.deulted = true;
player.mode = 'ironman'; player.mode = 'ironman';
} }
if (player.main.skills.overall.xp !== player.ironman.skills.overall.xp) { if (
player.main.skills.overall.xp !== player.ironman.skills.overall.xp
) {
player.deironed = true; player.deironed = true;
player.mode = 'main'; player.mode = 'main';
} }
} else { } else {
player.mode = 'ironman'; player.mode = 'ironman';
if (player.main.skills.overall.xp !== player.ironman.skills.overall.xp) { if (
player.main.skills.overall.xp !== player.ironman.skills.overall.xp
) {
player.deironed = true; player.deironed = true;
player.mode = 'main'; player.mode = 'main';
} }
@@ -355,26 +240,34 @@ export async function getStats(
return player; return player;
} }
throw Error('Player not found');
}
/** /**
* Fetches stats from the OSRS API and returns them as an object. * Fetches stats from the OSRS API and returns them as an object.
* *
* @param rsn Username of the player. * @param rsn Username of the player.
* @param mode Gamemode to fetch ranks for. * @param mode Gamemode to fetch ranks for.
* @param config Optional axios request config object.
* @returns Stats object. * @returns Stats object.
*/ */
export async function getStatsByGamemode( export async function getStatsByGamemode(
rsn: string, rsn: string,
mode: Gamemode = 'main', mode: Gamemode = 'main'
config?: AxiosRequestConfig
): Promise<Stats> { ): Promise<Stats> {
validateRSN(rsn); if (typeof rsn !== 'string') {
if (!GAMEMODES.includes(mode)) { 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 (!GAMEMODES.includes(mode)) {
throw Error('Invalid game mode'); throw Error('Invalid game mode');
} }
const response = await getOfficialStats(rsn, mode, config); const response = await httpGet(getStatsURL(mode, rsn));
const stats = parseJsonStats(response); if (response.status !== 200) {
throw Error('Player not found');
}
const stats = parseStats(response.data);
return stats; return stats;
} }
@@ -382,8 +275,7 @@ export async function getStatsByGamemode(
export async function getSkillPage( export async function getSkillPage(
skill: SkillName, skill: SkillName,
mode: Gamemode = 'main', mode: Gamemode = 'main',
page: number = 1, page: number = 1
config?: AxiosRequestConfig
): Promise<PlayerSkillRow[]> { ): Promise<PlayerSkillRow[]> {
if (!GAMEMODES.includes(mode)) { if (!GAMEMODES.includes(mode)) {
throw Error('Invalid game mode'); throw Error('Invalid game mode');
@@ -394,10 +286,7 @@ export async function getSkillPage(
} }
const url = getSkillPageURL(mode, skill, page); const url = getSkillPageURL(mode, skill, page);
const response = await httpGet<string | Buffer | BinaryData | undefined>( const response = await httpGet(url);
url,
config
);
const dom = new JSDOM(response.data); const dom = new JSDOM(response.data);
const playersHTML = dom.window.document.querySelectorAll( const playersHTML = dom.window.document.querySelectorAll(
'.personal-hiscores__row' '.personal-hiscores__row'
@@ -429,14 +318,12 @@ export async function getSkillPage(
* @param activity Name of the activity or boss to fetch hiscores for. * @param activity Name of the activity or boss to fetch hiscores for.
* @param mode Gamemode to fetch ranks for. * @param mode Gamemode to fetch ranks for.
* @param page Page number. * @param page Page number.
* @param config Optional axios request config object.
* @returns Array of `PlayerActivityRow` objects. * @returns Array of `PlayerActivityRow` objects.
*/ */
export async function getActivityPage( export async function getActivityPage(
activity: ActivityName, activity: ActivityName,
mode: Gamemode = 'main', mode: Gamemode = 'main',
page: number = 1, page: number = 1
config?: AxiosRequestConfig
): Promise<PlayerActivityRow[]> { ): Promise<PlayerActivityRow[]> {
if (!GAMEMODES.includes(mode)) { if (!GAMEMODES.includes(mode)) {
throw Error('Invalid game mode'); throw Error('Invalid game mode');
@@ -447,10 +334,7 @@ export async function getActivityPage(
} }
const url = getActivityPageURL(mode, activity, page); const url = getActivityPageURL(mode, activity, page);
const response = await httpGet<string | Buffer | BinaryData | undefined>( const response = await httpGet(url);
url,
config
);
const dom = new JSDOM(response.data); const dom = new JSDOM(response.data);
const playersHTML = dom.window.document.querySelectorAll( const playersHTML = dom.window.document.querySelectorAll(
'.personal-hiscores__row' '.personal-hiscores__row'

View File

@@ -1,5 +1,3 @@
import { AxiosRequestConfig } from 'axios';
export type Gamemode = export type Gamemode =
| 'main' | 'main'
| 'ironman' | 'ironman'
@@ -7,10 +5,7 @@ export type Gamemode =
| 'hardcore' | 'hardcore'
| 'deadman' | 'deadman'
| 'seasonal' | 'seasonal'
| 'tournament' | 'tournament';
| 'skiller'
| 'oneDefence'
| 'freshStart';
export interface Skill { export interface Skill {
rank: number; rank: number;
@@ -62,18 +57,16 @@ export type ClueType =
export type Clues = { [Type in ClueType]: Activity }; export type Clues = { [Type in ClueType]: Activity };
export type BHType = 'rogue' | 'hunter' | 'rogueV2' | 'hunterV2'; export type BHType = 'rogue' | 'hunter';
export type BH = { [Type in BHType]: Activity }; export type BH = { [Type in BHType]: Activity };
export type Boss = export type Boss =
| 'abyssalSire' | 'abyssalSire'
| 'alchemicalHydra' | 'alchemicalHydra'
| 'artio'
| 'barrows' | 'barrows'
| 'bryophyta' | 'bryophyta'
| 'callisto' | 'callisto'
| 'calvarion'
| 'cerberus' | 'cerberus'
| 'chambersOfXeric' | 'chambersOfXeric'
| 'chambersOfXericChallengeMode' | 'chambersOfXericChallengeMode'
@@ -86,7 +79,6 @@ export type Boss =
| 'dagannothRex' | 'dagannothRex'
| 'dagannothSupreme' | 'dagannothSupreme'
| 'derangedArchaeologist' | 'derangedArchaeologist'
| 'dukeSucellus'
| 'generalGraardor' | 'generalGraardor'
| 'giantMole' | 'giantMole'
| 'grotesqueGuardians' | 'grotesqueGuardians'
@@ -96,32 +88,22 @@ export type Boss =
| 'kraken' | 'kraken'
| 'kreeArra' | 'kreeArra'
| 'krilTsutsaroth' | 'krilTsutsaroth'
| 'lunarChests'
| 'mimic' | 'mimic'
| 'nex' | 'nex'
| 'nightmare' | 'nightmare'
| 'phosanisNightmare' | 'phosanisNightmare'
| 'obor' | 'obor'
| 'phantomMuspah'
| 'sarachnis' | 'sarachnis'
| 'scorpia' | 'scorpia'
| 'scurrius'
| 'skotizo' | 'skotizo'
| 'solHeredit'
| 'spindel'
| 'tempoross' | 'tempoross'
| 'gauntlet' | 'gauntlet'
| 'corruptedGauntlet' | 'corruptedGauntlet'
| 'leviathan'
| 'whisperer'
| 'theatreOfBlood' | 'theatreOfBlood'
| 'theatreOfBloodHardMode' | 'theatreOfBloodHardMode'
| 'thermonuclearSmokeDevil' | 'thermonuclearSmokeDevil'
| 'tombsOfAmascut'
| 'tombsOfAmascutExpertMode'
| 'tzKalZuk' | 'tzKalZuk'
| 'tzTokJad' | 'tzTokJad'
| 'vardorvis'
| 'venenatis' | 'venenatis'
| 'vetion' | 'vetion'
| 'vorkath' | 'vorkath'
@@ -133,13 +115,9 @@ export type Bosses = { [Type in Boss]: Activity };
export type ActivityName = export type ActivityName =
| 'leaguePoints' | 'leaguePoints'
| 'deadmanPoints'
| 'hunterBHV2'
| 'rogueBHV2'
| 'hunterBH' | 'hunterBH'
| 'rogueBH' | 'rogueBH'
| 'lastManStanding' | 'lastManStanding'
| 'pvpArena'
| 'soulWarsZeal' | 'soulWarsZeal'
| 'riftsClosed' | 'riftsClosed'
| 'allClues' | 'allClues'
@@ -149,26 +127,16 @@ export type ActivityName =
| 'hardClues' | 'hardClues'
| 'eliteClues' | 'eliteClues'
| 'masterClues' | 'masterClues'
| 'colosseumGlory'
| Boss; | Boss;
export interface Stats { export interface Stats {
skills: Skills; skills: Skills;
clues: Clues; clues: Clues;
/**
* Will only contain rank and score data for the `seasonal` gamemode.
*/
leaguePoints: Activity; leaguePoints: Activity;
/**
* Will only contain rank and score data for the `tournament` gamemode.
*/
deadmanPoints: Activity;
bountyHunter: BH; bountyHunter: BH;
lastManStanding: Activity; lastManStanding: Activity;
pvpArena: Activity;
soulWarsZeal: Activity; soulWarsZeal: Activity;
riftsClosed: Activity; riftsClosed: Activity;
colosseumGlory: Activity;
bosses: Bosses; bosses: Bosses;
} }
export type Modes = { [M in Gamemode]?: Stats }; export type Modes = { [M in Gamemode]?: Stats };
@@ -190,63 +158,3 @@ export interface PlayerActivityRow extends Activity {
name: string; name: string;
dead: boolean; dead: boolean;
} }
export interface GetStatsOptions {
/**
* Other game modes to fetch ranks for.
* @defaultvalue `['ironman', 'hardcore', 'ultimate']`
*/
otherGamemodes?: Extract<Gamemode, 'ironman' | 'hardcore' | 'ultimate'>[];
/**
* If true, the formatted RSN will be fetched. Otherwise it will return the provided, unformatted RSN.
* @defaultvalue `true`
*/
shouldGetFormattedRsn?: boolean;
/**
* Map of configs for each requests that can take place in the `getStats` function.
*/
axiosConfigs?: Partial<Record<Gamemode, AxiosRequestConfig>> & {
/**
* The axios request config object to use for the RSN format request.
*/
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[];
}

View File

@@ -9,7 +9,6 @@ import {
export const BASE_URL = 'https://secure.runescape.com/m=hiscore_oldschool'; export const BASE_URL = 'https://secure.runescape.com/m=hiscore_oldschool';
export const STATS_URL = 'index_lite.ws?player='; export const STATS_URL = 'index_lite.ws?player=';
export const JSON_STATS_URL = 'index_lite.json?player=';
export const SCORES_URL = 'overall.ws?'; export const SCORES_URL = 'overall.ws?';
export type GamemodeUrl = { export type GamemodeUrl = {
@@ -23,10 +22,7 @@ export const GAMEMODE_URL: GamemodeUrl = {
ultimate: `${BASE_URL}_ultimate/`, ultimate: `${BASE_URL}_ultimate/`,
deadman: `${BASE_URL}_deadman/`, deadman: `${BASE_URL}_deadman/`,
seasonal: `${BASE_URL}_seasonal/`, seasonal: `${BASE_URL}_seasonal/`,
tournament: `${BASE_URL}_tournament/`, tournament: `${BASE_URL}_tournament/`
skiller: `${BASE_URL}_skiller/`,
oneDefence: `${BASE_URL}_skiller_defence/`,
freshStart: `${BASE_URL}_fresh_start/`
}; };
export const SKILLS: SkillName[] = [ export const SKILLS: SkillName[] = [
'overall', 'overall',
@@ -63,7 +59,7 @@ export const CLUES: ClueType[] = [
'elite', 'elite',
'master' 'master'
]; ];
export const BH_MODES: BHType[] = ['hunterV2', 'rogueV2', 'hunter', 'rogue']; export const BH_MODES: BHType[] = ['hunter', 'rogue'];
export const GAMEMODES: Gamemode[] = [ export const GAMEMODES: Gamemode[] = [
'main', 'main',
'ironman', 'ironman',
@@ -76,11 +72,9 @@ export const GAMEMODES: Gamemode[] = [
export const BOSSES: Boss[] = [ export const BOSSES: Boss[] = [
'abyssalSire', 'abyssalSire',
'alchemicalHydra', 'alchemicalHydra',
'artio',
'barrows', 'barrows',
'bryophyta', 'bryophyta',
'callisto', 'callisto',
'calvarion',
'cerberus', 'cerberus',
'chambersOfXeric', 'chambersOfXeric',
'chambersOfXericChallengeMode', 'chambersOfXericChallengeMode',
@@ -93,7 +87,6 @@ export const BOSSES: Boss[] = [
'dagannothRex', 'dagannothRex',
'dagannothSupreme', 'dagannothSupreme',
'derangedArchaeologist', 'derangedArchaeologist',
'dukeSucellus',
'generalGraardor', 'generalGraardor',
'giantMole', 'giantMole',
'grotesqueGuardians', 'grotesqueGuardians',
@@ -103,32 +96,22 @@ export const BOSSES: Boss[] = [
'kraken', 'kraken',
'kreeArra', 'kreeArra',
'krilTsutsaroth', 'krilTsutsaroth',
'lunarChests',
'mimic', 'mimic',
'nex', 'nex',
'nightmare', 'nightmare',
'phosanisNightmare', 'phosanisNightmare',
'obor', 'obor',
'phantomMuspah',
'sarachnis', 'sarachnis',
'scorpia', 'scorpia',
'scurrius',
'skotizo', 'skotizo',
'solHeredit',
'spindel',
'tempoross', 'tempoross',
'gauntlet', 'gauntlet',
'corruptedGauntlet', 'corruptedGauntlet',
'leviathan',
'whisperer',
'theatreOfBlood', 'theatreOfBlood',
'theatreOfBloodHardMode', 'theatreOfBloodHardMode',
'thermonuclearSmokeDevil', 'thermonuclearSmokeDevil',
'tombsOfAmascut',
'tombsOfAmascutExpertMode',
'tzKalZuk', 'tzKalZuk',
'tzTokJad', 'tzTokJad',
'vardorvis',
'venenatis', 'venenatis',
'vetion', 'vetion',
'vorkath', 'vorkath',
@@ -138,9 +121,6 @@ export const BOSSES: Boss[] = [
]; ];
export const ACTIVITIES: ActivityName[] = [ export const ACTIVITIES: ActivityName[] = [
'leaguePoints', 'leaguePoints',
'deadmanPoints',
'hunterBHV2',
'rogueBHV2',
'hunterBH', 'hunterBH',
'rogueBH', 'rogueBH',
'allClues', 'allClues',
@@ -151,10 +131,8 @@ export const ACTIVITIES: ActivityName[] = [
'eliteClues', 'eliteClues',
'masterClues', 'masterClues',
'lastManStanding', 'lastManStanding',
'pvpArena',
'soulWarsZeal', 'soulWarsZeal',
'riftsClosed', 'riftsClosed',
'colosseumGlory',
...BOSSES ...BOSSES
]; ];
@@ -165,11 +143,9 @@ export type FormattedBossNames = {
export const FORMATTED_BOSS_NAMES: FormattedBossNames = { export const FORMATTED_BOSS_NAMES: FormattedBossNames = {
abyssalSire: 'Abyssal Sire', abyssalSire: 'Abyssal Sire',
alchemicalHydra: 'Alchemical Hydra', alchemicalHydra: 'Alchemical Hydra',
artio: 'Artio',
barrows: 'Barrows Chests', barrows: 'Barrows Chests',
bryophyta: 'Bryophyta', bryophyta: 'Bryophyta',
callisto: 'Callisto', callisto: 'Callisto',
calvarion: "Calvar'ion",
cerberus: 'Cerberus', cerberus: 'Cerberus',
chambersOfXeric: 'Chambers of Xeric', chambersOfXeric: 'Chambers of Xeric',
chambersOfXericChallengeMode: 'Chambers of Xeric: Challenge Mode', chambersOfXericChallengeMode: 'Chambers of Xeric: Challenge Mode',
@@ -182,7 +158,6 @@ export const FORMATTED_BOSS_NAMES: FormattedBossNames = {
dagannothRex: 'Dagannoth Rex', dagannothRex: 'Dagannoth Rex',
dagannothSupreme: 'Dagannoth Supreme', dagannothSupreme: 'Dagannoth Supreme',
derangedArchaeologist: 'Deranged Archaeologist', derangedArchaeologist: 'Deranged Archaeologist',
dukeSucellus: 'Duke Sucellus',
generalGraardor: 'General Graardor', generalGraardor: 'General Graardor',
giantMole: 'Giant Mole', giantMole: 'Giant Mole',
grotesqueGuardians: 'Grotesque Guardians', grotesqueGuardians: 'Grotesque Guardians',
@@ -192,32 +167,22 @@ export const FORMATTED_BOSS_NAMES: FormattedBossNames = {
kraken: 'Kraken', kraken: 'Kraken',
kreeArra: "Kree'Arra", kreeArra: "Kree'Arra",
krilTsutsaroth: "K'ril Tsutsaroth", krilTsutsaroth: "K'ril Tsutsaroth",
lunarChests: 'Lunar Chests',
mimic: 'Mimic', mimic: 'Mimic',
nex: 'Nex', nex: 'Nex',
nightmare: 'Nightmare', nightmare: 'The Nightmare of Ashihama',
phosanisNightmare: "Phosani's Nightmare", phosanisNightmare: "Phosani's Nightmare",
obor: 'Obor', obor: 'Obor',
phantomMuspah: 'Phantom Muspah',
sarachnis: 'Sarachnis', sarachnis: 'Sarachnis',
scorpia: 'Scorpia', scorpia: 'Scorpia',
scurrius: 'Scurrius',
skotizo: 'Skotizo', skotizo: 'Skotizo',
solHeredit: 'Sol Heredit',
spindel: 'Spindel',
tempoross: 'Tempoross', tempoross: 'Tempoross',
gauntlet: 'The Gauntlet', gauntlet: 'The Gauntlet',
corruptedGauntlet: 'The Corrupted Gauntlet', corruptedGauntlet: 'The Corrupted Gauntlet',
leviathan: 'The Leviathan',
whisperer: 'The Whisperer',
theatreOfBlood: 'Theatre of Blood', theatreOfBlood: 'Theatre of Blood',
theatreOfBloodHardMode: 'Theatre of Blood: Hard Mode', theatreOfBloodHardMode: 'Theatre of Blood: Hard Mode',
thermonuclearSmokeDevil: 'Thermonuclear Smoke Devil', thermonuclearSmokeDevil: 'Thermonuclear Smoke Devil',
tombsOfAmascut: 'Tombs of Amascut',
tombsOfAmascutExpertMode: 'Tombs of Amascut: Expert Mode',
tzKalZuk: 'TzKal-Zuk', tzKalZuk: 'TzKal-Zuk',
tzTokJad: 'TzTok-Jad', tzTokJad: 'TzTok-Jad',
vardorvis: 'Vardorvis',
venenatis: 'Venenatis', venenatis: 'Venenatis',
vetion: "Vet'ion", vetion: "Vet'ion",
vorkath: 'Vorkath', vorkath: 'Vorkath',
@@ -276,56 +241,18 @@ export type FormattedBHNames = {
}; };
export const FORMATTED_BH_NAMES: FormattedBHNames = { export const FORMATTED_BH_NAMES: FormattedBHNames = {
rogue: 'Bounty Hunter (Legacy) - Rogue', rogue: 'Bounty Hunter - Rogue',
hunter: 'Bounty Hunter (Legacy) - Hunter', hunter: 'Bounty Hunter - Hunter'
rogueV2: 'Bounty Hunter - Rogue',
hunterV2: 'Bounty Hunter - Hunter'
}; };
export const FORMATTED_LMS = 'LMS - Rank'; export const FORMATTED_LMS = 'Last Man Standing';
export const FORMATTED_PVP_ARENA = 'PvP Arena - Rank';
export const FORMATTED_SOUL_WARS = 'Soul Wars Zeal'; export const FORMATTED_SOUL_WARS = 'Soul Wars Zeal';
export const FORMATTED_LEAGUE_POINTS = 'League Points'; export const FORMATTED_LEAGUE_POINTS = 'League Points';
export const FORMATTED_DEADMAN_POINTS = 'Deadman Points'; export const FORMATTED_RIFTS_CLOSED = 'Rifts Closed';
export const FORMATTED_RIFTS_CLOSED = 'Rifts closed';
export const FORMATTED_COLOSSEUM_GLORY = 'Colosseum Glory'; /**
* Count of activities not including bosses, bounty hunter, or clues.
*/
export const EXTRA_ACTIVITY_COUNT = 4;
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 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);
}
}

View File

@@ -1,4 +1,4 @@
import axios, { AxiosRequestConfig } from 'axios'; import axios from 'axios';
import * as ua from 'useragent-generator'; import * as ua from 'useragent-generator';
import { Gamemode, SkillName, ActivityName } from '../types'; import { Gamemode, SkillName, ActivityName } from '../types';
import { import {
@@ -6,9 +6,7 @@ import {
STATS_URL, STATS_URL,
SCORES_URL, SCORES_URL,
SKILLS, SKILLS,
ACTIVITIES, ACTIVITIES
JSON_STATS_URL,
InvalidRSNError
} from './constants'; } from './constants';
/** /**
@@ -16,13 +14,10 @@ import {
* *
* @param gamemode Gamemode to fetch ranks for. * @param gamemode Gamemode to fetch ranks for.
* @param rsn Username of the player. * @param rsn Username of the player.
* @param json If the JSON endpoint is desired instead of CSV.
* @returns Encoded stats URL. * @returns Encoded stats URL.
*/ */
export const getStatsURL = (gamemode: Gamemode, rsn: string, json = false) => export const getStatsURL = (gamemode: Gamemode, rsn: string) =>
`${GAMEMODE_URL[gamemode]}${ `${GAMEMODE_URL[gamemode]}${STATS_URL}${encodeURIComponent(rsn)}`;
json ? JSON_STATS_URL : STATS_URL
}${encodeURIComponent(rsn)}`;
/** /**
* Will generate a player table URL for the official OSRS hiscores website. * Will generate a player table URL for the official OSRS hiscores website.
@@ -101,29 +96,10 @@ export const rsnFromElement = (el: Element | null) => {
* @param url URL to run a `GET` request against. * @param url URL to run a `GET` request against.
* @returns Axios response. * @returns Axios response.
*/ */
export const httpGet = <Response>( export const httpGet = (url: string) =>
url: string, axios.get(url, {
config: AxiosRequestConfig = {}
) =>
axios.get<Response>(url, {
headers: { headers: {
// without User-Agent header requests may be rejected by DDoS protection mechanism // without User-Agent header requests may be rejected by DDoS protection mechanism
'User-Agent': ua.firefox(80) 'User-Agent': ua.firefox(80)
},
...config
});
/**
* Validates that a provided RSN has the same username restrictions as Jagex.
* @param rsn Username to validate.
* @throws Error if the RSN fails validation.
*/
export const validateRSN = (rsn: string) => {
if (typeof rsn !== 'string') {
throw new InvalidRSNError('RSN must be a string');
} else if (!/^[a-zA-Z0-9 _-]+$/.test(rsn)) {
throw new InvalidRSNError('RSN contains invalid character');
} else if (rsn.length > 12 || rsn.length < 1) {
throw new InvalidRSNError('RSN must be between 1 and 12 characters');
} }
}; });

View File

@@ -6,7 +6,10 @@
"outDir": "./lib", "outDir": "./lib",
"strict": true, "strict": true,
"lib": ["ES2015", "DOM", "DOM.Iterable"], "lib": ["ES2015", "DOM", "DOM.Iterable"],
"typeRoots": ["./node_modules/@types", "./src/@types"] "typeRoots": [
"./node_modules/@types",
"./src/@types"
]
}, },
"include": ["src"], "include": ["src"],
"exclude": ["node_modules", "**/__tests__/*"] "exclude": ["node_modules", "**/__tests__/*"]

5470
yarn.lock

File diff suppressed because it is too large Load Diff