mirror of
https://github.com/maxswa/osrs-json-hiscores.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
76 Commits
fix-gh-wor
...
v2.19.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9c56e44344 | ||
![]() |
e5b3075ebd | ||
![]() |
8bb50b73b2 | ||
![]() |
cb482ad720 | ||
![]() |
0fbd85aa07 | ||
![]() |
e634b107b1 | ||
![]() |
29db21493c | ||
![]() |
b117cfcf41 | ||
![]() |
d895326d9f | ||
![]() |
1f7a25d421 | ||
![]() |
057812681b | ||
![]() |
ee510ec69a | ||
![]() |
6adcb02545 | ||
![]() |
dacbd90189 | ||
![]() |
3b502cb7cf | ||
![]() |
b3f81e66eb | ||
![]() |
01150b6234 | ||
![]() |
ca4d75f2c7 | ||
![]() |
03b2ef42d2 | ||
![]() |
7e2603b235 | ||
![]() |
92452e240c | ||
![]() |
d01b36422e | ||
![]() |
3bc1352838 | ||
![]() |
91066b5366 | ||
![]() |
4872d04301 | ||
![]() |
55312ba0fd | ||
![]() |
331a448503 | ||
![]() |
84c704a846 | ||
![]() |
561495632c | ||
![]() |
bdfdf1eb14 | ||
![]() |
e349776060 | ||
![]() |
546bc5acf7 | ||
![]() |
66528cd9d9 | ||
![]() |
99ea3fb722 | ||
![]() |
7f16d26e3c | ||
![]() |
ed9fcf47b3 | ||
![]() |
bb935bb6c9 | ||
![]() |
d046fffa9e | ||
![]() |
5483e54d78 | ||
![]() |
97532b0c03 | ||
![]() |
f9a56e2b3a | ||
![]() |
9f43f0c39f | ||
![]() |
d67ffa3e20 | ||
![]() |
7bdde9f5d1 | ||
![]() |
b4a50e69c8 | ||
![]() |
7935c5a523 | ||
![]() |
f1e2155428 | ||
![]() |
dd90fe1260 | ||
![]() |
12c4e530a2 | ||
![]() |
5a2b0d9ad8 | ||
![]() |
c43858eac1 | ||
![]() |
0d0081f4f6 | ||
![]() |
3570f0f07d | ||
![]() |
3ddda54e02 | ||
![]() |
388f01522a | ||
![]() |
24b3de2a29 | ||
![]() |
fc7897d4c7 | ||
![]() |
72ea31490f | ||
![]() |
66825955c1 | ||
![]() |
4b4bf8c702 | ||
![]() |
53981e7468 | ||
![]() |
a90e5b98dd | ||
![]() |
16a975f10b | ||
![]() |
c800501188 | ||
![]() |
8a320df015 | ||
![]() |
169462c446 | ||
![]() |
317a972314 | ||
![]() |
c558f8402c | ||
![]() |
41c6da4d5c | ||
![]() |
99ad5eb662 | ||
![]() |
a8cf1713af | ||
![]() |
11391c90f2 | ||
![]() |
e4d2b10488 | ||
![]() |
0cfd0e2471 | ||
![]() |
9c284ad4b0 | ||
![]() |
ec60a72c08 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
.idea/
|
.idea/
|
||||||
node_modules
|
node_modules
|
||||||
/lib
|
/lib
|
||||||
|
.vscode
|
86
README.md
86
README.md
@@ -5,22 +5,24 @@
|
|||||||
[](https://github.com/maxswa/osrs-json-hiscores/blob/master/src/types.ts)
|
[](https://github.com/maxswa/osrs-json-hiscores/blob/master/src/types.ts)
|
||||||
[](https://github.com/maxswa/osrs-json-hiscores/actions/workflows/main.yml?query=branch%3Amain)
|
[](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) returns CSV.
|
The official hiscores API for Old School RuneScape (OSRS) can return CSV or a simple JSON array.
|
||||||
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).
|
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).
|
||||||
|
|
||||||
Additional functions are provided that screen-scrape the OSRS leaderboards and return a list of players as json.
|
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.
|
||||||
|
|
||||||
`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 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)
|
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)
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -42,27 +44,14 @@ $ 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:
|
||||||
|
|
||||||
```javascript
|
```typescript
|
||||||
const hiscores = require('osrs-json-hiscores');
|
import { getStatsByGamemode, getSkillPage } from 'osrs-json-hiscores';
|
||||||
```
|
```
|
||||||
|
|
||||||
Once you import it you can call the functions asynchronously:
|
Once you import it you can call the functions asynchronously:
|
||||||
|
|
||||||
```javascript
|
```typescript
|
||||||
hiscores
|
const stats = await getStatsByGamemode('Lynx Titan');
|
||||||
.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');
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -81,9 +70,8 @@ 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:
|
||||||
|
|
||||||
```javascript
|
```typescript
|
||||||
hiscores
|
getSkillPage('attack', 'main', 1)
|
||||||
.getSkillPage('attack', 'main', 1)
|
|
||||||
.then((res) => console.log(res))
|
.then((res) => console.log(res))
|
||||||
.catch((err) => console.error(err));
|
.catch((err) => console.error(err));
|
||||||
```
|
```
|
||||||
@@ -104,20 +92,24 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses
|
|||||||
|
|
||||||
### Minigames
|
### Minigames
|
||||||
|
|
||||||
| Minigame | Param |
|
| Minigame | Param |
|
||||||
| ---------------------- | :---------------: |
|
| ------------------------------- | :---------------: |
|
||||||
| Bounty Hunter (Rogue) | `rogueBH` |
|
| Bounty Hunter (Legacy - Rogue) | `rogueBH` |
|
||||||
| Bounty Hunter (Hunter) | `hunterBH` |
|
| Bounty Hunter (Legacy - Hunter) | `hunterBH` |
|
||||||
| Last Man Standing | `lastManStanding` |
|
| Bounty Hunter (Rogue) | `rogueBHV2` |
|
||||||
| PvP Arena | `pvpArena` |
|
| Bounty Hunter (Hunter) | `hunterBHV2` |
|
||||||
| Soul Wars Zeal | `soulWarsZeal` |
|
| LMS - Rank | `lastManStanding` |
|
||||||
| Rifts Closed | `riftsClosed` |
|
| PvP Arena - Rank | `pvpArena` |
|
||||||
|
| Soul Wars Zeal | `soulWarsZeal` |
|
||||||
|
| Rifts closed | `riftsClosed` |
|
||||||
|
| Colosseum Glory | `colosseumGlory` |
|
||||||
|
|
||||||
### Leagues
|
### Points
|
||||||
|
|
||||||
| Activity | Param |
|
| Activity | Param |
|
||||||
| ------------- | :------------: |
|
| -------------- | :-------------: |
|
||||||
| League Points | `leaguePoints` |
|
| League Points | `leaguePoints` |
|
||||||
|
| Deadman Points | `deadmanPoints` |
|
||||||
|
|
||||||
### Bosses
|
### Bosses
|
||||||
|
|
||||||
@@ -125,11 +117,12 @@ 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` |
|
||||||
|
| Araxxor | `araxxor` |
|
||||||
| Artio | `artio` |
|
| Artio | `artio` |
|
||||||
| Barrows Chests | `barrows` |
|
| Barrows Chests | `barrows` |
|
||||||
| Bryophyta | `bryophyta` |
|
| Bryophyta | `bryophyta` |
|
||||||
| Callisto | `callisto` |
|
| Callisto | `callisto` |
|
||||||
| Cal'varion | `calvarion` |
|
| 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` |
|
||||||
@@ -142,6 +135,7 @@ 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` |
|
||||||
@@ -151,19 +145,24 @@ 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` |
|
||||||
| The Nightmare of Ashihama | `nightmare` |
|
| Nightmare | `nightmare` |
|
||||||
| Phosani's Nightmare | `phosanisNightmare` |
|
| Phosani's Nightmare | `phosanisNightmare` |
|
||||||
| Obor | `obor` |
|
| Obor | `obor` |
|
||||||
| Phantom Muspah | `phantomMuspah` |
|
| Phantom Muspah | `phantomMuspah` |
|
||||||
| Sarachnis | `sarachnis` |
|
| Sarachnis | `sarachnis` |
|
||||||
| Scorpia | `scorpia` |
|
| Scorpia | `scorpia` |
|
||||||
|
| Scurrius | `scurrius` |
|
||||||
| Skotizo | `skotizo` |
|
| Skotizo | `skotizo` |
|
||||||
|
| Sol Heredit | `solHeredit` |
|
||||||
| Spindel | `spindel` |
|
| 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` |
|
||||||
@@ -171,6 +170,7 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses
|
|||||||
| Tombs of Amascut: Expert Mode | `tombsOfAmascutExpertMode` |
|
| 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` |
|
||||||
@@ -182,7 +182,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:
|
||||||
|
|
||||||
```javascript
|
```typescript
|
||||||
{
|
{
|
||||||
name: 'Lynx Titan',
|
name: 'Lynx Titan',
|
||||||
mode: 'main',
|
mode: 'main',
|
||||||
@@ -210,7 +210,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):
|
||||||
|
|
||||||
```javascript
|
```typescript
|
||||||
[
|
[
|
||||||
{ rank: 1, name: 'Lynx Titan', level: 2277, xp: 4600000000, dead: false },
|
{ rank: 1, name: 'Lynx Titan', level: 2277, xp: 4600000000, dead: false },
|
||||||
{},
|
{},
|
||||||
@@ -223,7 +223,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:
|
||||||
|
|
||||||
```javascript
|
```typescript
|
||||||
// kril === "K'ril Tsutsaroth"
|
// kril === "K'ril Tsutsaroth"
|
||||||
const kril = FORMATTED_BOSS_NAMES['krilTsutsaroth'];
|
const kril = FORMATTED_BOSS_NAMES['krilTsutsaroth'];
|
||||||
```
|
```
|
||||||
|
2517
__tests__/attackTopPage.html
vendored
2517
__tests__/attackTopPage.html
vendored
File diff suppressed because it is too large
Load Diff
2551
__tests__/b0atyNamePage.html
vendored
2551
__tests__/b0atyNamePage.html
vendored
File diff suppressed because it is too large
Load Diff
104
__tests__/b0atyStats.csv
Normal file
104
__tests__/b0atyStats.csv
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
2130,2277,1102428578
|
||||||
|
22624,99,32956104
|
||||||
|
3574,99,47147957
|
||||||
|
1226,99,200000000
|
||||||
|
1454,99,180249986
|
||||||
|
3112,99,155438899
|
||||||
|
2533,99,17520811
|
||||||
|
2078,99,84629930
|
||||||
|
150276,99,13378420
|
||||||
|
23833,99,19182079
|
||||||
|
61715,99,13456926
|
||||||
|
24604,99,16672148
|
||||||
|
19900,99,19161337
|
||||||
|
53450,99,14082613
|
||||||
|
12063,99,16553015
|
||||||
|
32871,99,14985851
|
||||||
|
39747,99,13346314
|
||||||
|
8390,99,15771711
|
||||||
|
55848,99,14322777
|
||||||
|
1723,99,52832158
|
||||||
|
2107,99,101923913
|
||||||
|
5301,99,22739376
|
||||||
|
20087,99,18044758
|
||||||
|
1301,99,18031495
|
||||||
|
-1,-1
|
||||||
|
-1,-1
|
||||||
|
32847,10
|
||||||
|
16567,3
|
||||||
|
147262,8
|
||||||
|
101823,7
|
||||||
|
382,9739
|
||||||
|
1265,825
|
||||||
|
131,6000
|
||||||
|
2401,1606
|
||||||
|
43386,311
|
||||||
|
799,460
|
||||||
|
945,537
|
||||||
|
5531,5516
|
||||||
|
-1,-1
|
||||||
|
7538,14780
|
||||||
|
6781,1060
|
||||||
|
6880,43487
|
||||||
|
3193,2192
|
||||||
|
2932,4796
|
||||||
|
37,111
|
||||||
|
44605,143
|
||||||
|
4023,1830
|
||||||
|
238149,5
|
||||||
|
47139,293
|
||||||
|
98811,58
|
||||||
|
10766,2657
|
||||||
|
200557,34
|
||||||
|
1735,692
|
||||||
|
23548,215
|
||||||
|
43337,116
|
||||||
|
61439,287
|
||||||
|
2859,2000
|
||||||
|
53841,106
|
||||||
|
18420,1078
|
||||||
|
23824,1062
|
||||||
|
18418,1088
|
||||||
|
85908,25
|
||||||
|
6077,1320
|
||||||
|
21980,1228
|
||||||
|
76561,477
|
||||||
|
774,3642
|
||||||
|
7917,281
|
||||||
|
2313,2046
|
||||||
|
52832,580
|
||||||
|
64296,2899
|
||||||
|
4205,1740
|
||||||
|
2681,1444
|
||||||
|
3424,324
|
||||||
|
523,48
|
||||||
|
35420,626
|
||||||
|
4162,749
|
||||||
|
5046,500
|
||||||
|
1218,217
|
||||||
|
21243,379
|
||||||
|
40733,249
|
||||||
|
53931,91
|
||||||
|
4870,1245
|
||||||
|
174877,29
|
||||||
|
2122,49
|
||||||
|
3471,1569
|
||||||
|
50947,273
|
||||||
|
236110,7
|
||||||
|
102423,201
|
||||||
|
10174,666
|
||||||
|
665,2072
|
||||||
|
23177,310
|
||||||
|
1967,419
|
||||||
|
8495,3066
|
||||||
|
122123,37
|
||||||
|
240,1537
|
||||||
|
7756,7
|
||||||
|
140561,8
|
||||||
|
4736,2113
|
||||||
|
249,7096
|
||||||
|
89194,51
|
||||||
|
15067,2780
|
||||||
|
975929,62
|
||||||
|
196393,25
|
||||||
|
227062,340
|
Can't render this file because it has a wrong number of fields in line 25.
|
654
__tests__/b0atyStats.json
Normal file
654
__tests__/b0atyStats.json
Normal file
@@ -0,0 +1,654 @@
|
|||||||
|
{
|
||||||
|
"skills": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "Overall",
|
||||||
|
"rank": 2130,
|
||||||
|
"level": 2277,
|
||||||
|
"xp": 1102428578
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Attack",
|
||||||
|
"rank": 22624,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 32956104
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "Defence",
|
||||||
|
"rank": 3574,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 47147957
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"name": "Strength",
|
||||||
|
"rank": 1226,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 200000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"name": "Hitpoints",
|
||||||
|
"rank": 1454,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 180249986
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"name": "Ranged",
|
||||||
|
"rank": 3112,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 155438899
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"name": "Prayer",
|
||||||
|
"rank": 2533,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 17520811
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"name": "Magic",
|
||||||
|
"rank": 2078,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 84629930
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"name": "Cooking",
|
||||||
|
"rank": 150276,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 13378420
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"name": "Woodcutting",
|
||||||
|
"rank": 23833,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 19182079
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"name": "Fletching",
|
||||||
|
"rank": 61715,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 13456926
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"name": "Fishing",
|
||||||
|
"rank": 24604,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 16672148
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"name": "Firemaking",
|
||||||
|
"rank": 19900,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 19161337
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"name": "Crafting",
|
||||||
|
"rank": 53450,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 14082613
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 14,
|
||||||
|
"name": "Smithing",
|
||||||
|
"rank": 12063,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 16553015
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 15,
|
||||||
|
"name": "Mining",
|
||||||
|
"rank": 32871,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 14985851
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 16,
|
||||||
|
"name": "Herblore",
|
||||||
|
"rank": 39747,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 13346314
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 17,
|
||||||
|
"name": "Agility",
|
||||||
|
"rank": 8390,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 15771711
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 18,
|
||||||
|
"name": "Thieving",
|
||||||
|
"rank": 55848,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 14322777
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 19,
|
||||||
|
"name": "Slayer",
|
||||||
|
"rank": 1723,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 52832158
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 20,
|
||||||
|
"name": "Farming",
|
||||||
|
"rank": 2107,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 101923913
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 21,
|
||||||
|
"name": "Runecraft",
|
||||||
|
"rank": 5301,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 22739376
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 22,
|
||||||
|
"name": "Hunter",
|
||||||
|
"rank": 20087,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 18044758
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 23,
|
||||||
|
"name": "Construction",
|
||||||
|
"rank": 1301,
|
||||||
|
"level": 99,
|
||||||
|
"xp": 18031495
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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": 32847,
|
||||||
|
"score": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"name": "Bounty Hunter - Rogue",
|
||||||
|
"rank": 16567,
|
||||||
|
"score": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"name": "Bounty Hunter (Legacy) - Hunter",
|
||||||
|
"rank": 147262,
|
||||||
|
"score": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"name": "Bounty Hunter (Legacy) - Rogue",
|
||||||
|
"rank": 101823,
|
||||||
|
"score": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"name": "Clue Scrolls (all)",
|
||||||
|
"rank": 382,
|
||||||
|
"score": 9739
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"name": "Clue Scrolls (beginner)",
|
||||||
|
"rank": 1265,
|
||||||
|
"score": 825
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"name": "Clue Scrolls (easy)",
|
||||||
|
"rank": 131,
|
||||||
|
"score": 6000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"name": "Clue Scrolls (medium)",
|
||||||
|
"rank": 2401,
|
||||||
|
"score": 1606
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"name": "Clue Scrolls (hard)",
|
||||||
|
"rank": 43386,
|
||||||
|
"score": 311
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"name": "Clue Scrolls (elite)",
|
||||||
|
"rank": 799,
|
||||||
|
"score": 460
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"name": "Clue Scrolls (master)",
|
||||||
|
"rank": 945,
|
||||||
|
"score": 537
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"name": "LMS - Rank",
|
||||||
|
"rank": 5531,
|
||||||
|
"score": 5516
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 14,
|
||||||
|
"name": "PvP Arena - Rank",
|
||||||
|
"rank": -1,
|
||||||
|
"score": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 15,
|
||||||
|
"name": "Soul Wars Zeal",
|
||||||
|
"rank": 7538,
|
||||||
|
"score": 14780
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 16,
|
||||||
|
"name": "Rifts closed",
|
||||||
|
"rank": 6781,
|
||||||
|
"score": 1060
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 17,
|
||||||
|
"name": "Colosseum Glory",
|
||||||
|
"rank": 6880,
|
||||||
|
"score": 43487
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 18,
|
||||||
|
"name": "Abyssal Sire",
|
||||||
|
"rank": 3193,
|
||||||
|
"score": 2192
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 19,
|
||||||
|
"name": "Alchemical Hydra",
|
||||||
|
"rank": 2932,
|
||||||
|
"score": 4796
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 20,
|
||||||
|
"name": "Araxxor",
|
||||||
|
"rank": 37,
|
||||||
|
"score": 111
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 21,
|
||||||
|
"name": "Artio",
|
||||||
|
"rank": 44605,
|
||||||
|
"score": 143
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 22,
|
||||||
|
"name": "Barrows Chests",
|
||||||
|
"rank": 4023,
|
||||||
|
"score": 1830
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 23,
|
||||||
|
"name": "Bryophyta",
|
||||||
|
"rank": 238149,
|
||||||
|
"score": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 24,
|
||||||
|
"name": "Callisto",
|
||||||
|
"rank": 47139,
|
||||||
|
"score": 293
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 25,
|
||||||
|
"name": "Calvar'ion",
|
||||||
|
"rank": 98811,
|
||||||
|
"score": 58
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 26,
|
||||||
|
"name": "Cerberus",
|
||||||
|
"rank": 10766,
|
||||||
|
"score": 2657
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 27,
|
||||||
|
"name": "Chambers of Xeric",
|
||||||
|
"rank": 200557,
|
||||||
|
"score": 34
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 28,
|
||||||
|
"name": "Chambers of Xeric: Challenge Mode",
|
||||||
|
"rank": 1735,
|
||||||
|
"score": 692
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 29,
|
||||||
|
"name": "Chaos Elemental",
|
||||||
|
"rank": 23548,
|
||||||
|
"score": 215
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 30,
|
||||||
|
"name": "Chaos Fanatic",
|
||||||
|
"rank": 43337,
|
||||||
|
"score": 116
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 31,
|
||||||
|
"name": "Commander Zilyana",
|
||||||
|
"rank": 61439,
|
||||||
|
"score": 287
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 32,
|
||||||
|
"name": "Corporeal Beast",
|
||||||
|
"rank": 2859,
|
||||||
|
"score": 2000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 33,
|
||||||
|
"name": "Crazy Archaeologist",
|
||||||
|
"rank": 53841,
|
||||||
|
"score": 106
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 34,
|
||||||
|
"name": "Dagannoth Prime",
|
||||||
|
"rank": 18420,
|
||||||
|
"score": 1078
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 35,
|
||||||
|
"name": "Dagannoth Rex",
|
||||||
|
"rank": 23824,
|
||||||
|
"score": 1062
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 36,
|
||||||
|
"name": "Dagannoth Supreme",
|
||||||
|
"rank": 18418,
|
||||||
|
"score": 1088
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 37,
|
||||||
|
"name": "Deranged Archaeologist",
|
||||||
|
"rank": 85908,
|
||||||
|
"score": 25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 38,
|
||||||
|
"name": "Duke Sucellus",
|
||||||
|
"rank": 6077,
|
||||||
|
"score": 1320
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 39,
|
||||||
|
"name": "General Graardor",
|
||||||
|
"rank": 21980,
|
||||||
|
"score": 1228
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 40,
|
||||||
|
"name": "Giant Mole",
|
||||||
|
"rank": 76561,
|
||||||
|
"score": 477
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 41,
|
||||||
|
"name": "Grotesque Guardians",
|
||||||
|
"rank": 774,
|
||||||
|
"score": 3642
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 42,
|
||||||
|
"name": "Hespori",
|
||||||
|
"rank": 7917,
|
||||||
|
"score": 281
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 43,
|
||||||
|
"name": "Kalphite Queen",
|
||||||
|
"rank": 2313,
|
||||||
|
"score": 2046
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 44,
|
||||||
|
"name": "King Black Dragon",
|
||||||
|
"rank": 52832,
|
||||||
|
"score": 580
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 45,
|
||||||
|
"name": "Kraken",
|
||||||
|
"rank": 64296,
|
||||||
|
"score": 2899
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 46,
|
||||||
|
"name": "Kree'Arra",
|
||||||
|
"rank": 4205,
|
||||||
|
"score": 1740
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 47,
|
||||||
|
"name": "K'ril Tsutsaroth",
|
||||||
|
"rank": 2681,
|
||||||
|
"score": 1444
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 48,
|
||||||
|
"name": "Lunar Chests",
|
||||||
|
"rank": 3424,
|
||||||
|
"score": 324
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 49,
|
||||||
|
"name": "Mimic",
|
||||||
|
"rank": 523,
|
||||||
|
"score": 48
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 50,
|
||||||
|
"name": "Nex",
|
||||||
|
"rank": 35420,
|
||||||
|
"score": 626
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 51,
|
||||||
|
"name": "Nightmare",
|
||||||
|
"rank": 4162,
|
||||||
|
"score": 749
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 52,
|
||||||
|
"name": "Phosani's Nightmare",
|
||||||
|
"rank": 5046,
|
||||||
|
"score": 500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 53,
|
||||||
|
"name": "Obor",
|
||||||
|
"rank": 1218,
|
||||||
|
"score": 217
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 54,
|
||||||
|
"name": "Phantom Muspah",
|
||||||
|
"rank": 21243,
|
||||||
|
"score": 379
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 55,
|
||||||
|
"name": "Sarachnis",
|
||||||
|
"rank": 40733,
|
||||||
|
"score": 249
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 56,
|
||||||
|
"name": "Scorpia",
|
||||||
|
"rank": 53931,
|
||||||
|
"score": 91
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 57,
|
||||||
|
"name": "Scurrius",
|
||||||
|
"rank": 4870,
|
||||||
|
"score": 1245
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 58,
|
||||||
|
"name": "Skotizo",
|
||||||
|
"rank": 174877,
|
||||||
|
"score": 29
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 59,
|
||||||
|
"name": "Sol Heredit",
|
||||||
|
"rank": 2122,
|
||||||
|
"score": 49
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 60,
|
||||||
|
"name": "Spindel",
|
||||||
|
"rank": 3471,
|
||||||
|
"score": 1569
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 61,
|
||||||
|
"name": "Tempoross",
|
||||||
|
"rank": 50947,
|
||||||
|
"score": 273
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 62,
|
||||||
|
"name": "The Gauntlet",
|
||||||
|
"rank": 236110,
|
||||||
|
"score": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 63,
|
||||||
|
"name": "The Corrupted Gauntlet",
|
||||||
|
"rank": 102423,
|
||||||
|
"score": 201
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 64,
|
||||||
|
"name": "The Leviathan",
|
||||||
|
"rank": 10174,
|
||||||
|
"score": 666
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 65,
|
||||||
|
"name": "The Whisperer",
|
||||||
|
"rank": 665,
|
||||||
|
"score": 2072
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 66,
|
||||||
|
"name": "Theatre of Blood",
|
||||||
|
"rank": 23177,
|
||||||
|
"score": 310
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 67,
|
||||||
|
"name": "Theatre of Blood: Hard Mode",
|
||||||
|
"rank": 1967,
|
||||||
|
"score": 419
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 68,
|
||||||
|
"name": "Thermonuclear Smoke Devil",
|
||||||
|
"rank": 8495,
|
||||||
|
"score": 3066
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 69,
|
||||||
|
"name": "Tombs of Amascut",
|
||||||
|
"rank": 122123,
|
||||||
|
"score": 37
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 70,
|
||||||
|
"name": "Tombs of Amascut: Expert Mode",
|
||||||
|
"rank": 240,
|
||||||
|
"score": 1537
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 71,
|
||||||
|
"name": "TzKal-Zuk",
|
||||||
|
"rank": 7756,
|
||||||
|
"score": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 72,
|
||||||
|
"name": "TzTok-Jad",
|
||||||
|
"rank": 140561,
|
||||||
|
"score": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 73,
|
||||||
|
"name": "Vardorvis",
|
||||||
|
"rank": 4736,
|
||||||
|
"score": 2113
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 74,
|
||||||
|
"name": "Venenatis",
|
||||||
|
"rank": 249,
|
||||||
|
"score": 7096
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 75,
|
||||||
|
"name": "Vet'ion",
|
||||||
|
"rank": 89194,
|
||||||
|
"score": 51
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 76,
|
||||||
|
"name": "Vorkath",
|
||||||
|
"rank": 15067,
|
||||||
|
"score": 2780
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 77,
|
||||||
|
"name": "Wintertodt",
|
||||||
|
"rank": 975929,
|
||||||
|
"score": 62
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 78,
|
||||||
|
"name": "Zalcano",
|
||||||
|
"rank": 196393,
|
||||||
|
"score": 25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 79,
|
||||||
|
"name": "Zulrah",
|
||||||
|
"rank": 227062,
|
||||||
|
"score": 340
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@@ -12,7 +12,10 @@ import {
|
|||||||
getSkillPageURL,
|
getSkillPageURL,
|
||||||
getStatsURL,
|
getStatsURL,
|
||||||
BOSSES,
|
BOSSES,
|
||||||
INVALID_FORMAT_ERROR
|
InvalidFormatError,
|
||||||
|
BH_MODES,
|
||||||
|
parseJsonStats,
|
||||||
|
HiscoresResponse
|
||||||
} from '../src/index';
|
} from '../src/index';
|
||||||
|
|
||||||
const B0ATY_NAME = 'B0ATY';
|
const B0ATY_NAME = 'B0ATY';
|
||||||
@@ -24,7 +27,13 @@ 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 lynxTitanStats = readFileSync(`${__dirname}/lynxTitanStats.csv`, 'utf8');
|
const b0atyStatsCsv = readFileSync(`${__dirname}/b0atyStats.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'
|
||||||
@@ -45,7 +54,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) === url) {
|
if (getStatsURL('main', LYNX_TITAN_FORMATTED_NAME, true) === 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}`);
|
||||||
@@ -77,6 +86,9 @@ 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
|
||||||
@@ -90,8 +102,10 @@ test('Parse CSV to json', () => {
|
|||||||
13,4057
|
13,4057
|
||||||
37,225
|
37,225
|
||||||
25,1110
|
25,1110
|
||||||
|
7827,43249
|
||||||
382,2780
|
382,2780
|
||||||
944,3000
|
944,3000
|
||||||
|
37,111
|
||||||
704,892
|
704,892
|
||||||
1981,1452
|
1981,1452
|
||||||
4981,23
|
4981,23
|
||||||
@@ -109,6 +123,7 @@ 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
|
||||||
@@ -118,6 +133,7 @@ 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
|
||||||
@@ -126,11 +142,15 @@ test('Parse CSV to json', () => {
|
|||||||
44,6233
|
44,6233
|
||||||
201,1101
|
201,1101
|
||||||
82,3404
|
82,3404
|
||||||
|
555,7079
|
||||||
5085,61
|
5085,61
|
||||||
|
8731,1423
|
||||||
678,903
|
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
|
||||||
@@ -138,6 +158,7 @@ test('Parse CSV to json', () => {
|
|||||||
289,13070
|
289,13070
|
||||||
489,8
|
489,8
|
||||||
967,47
|
967,47
|
||||||
|
968,48
|
||||||
11155,223
|
11155,223
|
||||||
1940,272
|
1940,272
|
||||||
8623,1340
|
8623,1340
|
||||||
@@ -173,7 +194,10 @@ 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 }
|
||||||
},
|
},
|
||||||
@@ -181,6 +205,7 @@ test('Parse CSV to json', () => {
|
|||||||
pvpArena: { rank: 13, score: 4057 },
|
pvpArena: { rank: 13, score: 4057 },
|
||||||
soulWarsZeal: { rank: 37, score: 225 },
|
soulWarsZeal: { rank: 37, score: 225 },
|
||||||
riftsClosed: { rank: 25, score: 1110 },
|
riftsClosed: { rank: 25, score: 1110 },
|
||||||
|
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 },
|
||||||
@@ -193,6 +218,7 @@ 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 },
|
||||||
|
araxxor: { rank: 37, score: 111 },
|
||||||
artio: { rank: 704, score: 892 },
|
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 },
|
||||||
@@ -210,6 +236,7 @@ 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 },
|
||||||
@@ -219,6 +246,7 @@ 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 },
|
||||||
@@ -227,11 +255,15 @@ test('Parse CSV to json', () => {
|
|||||||
phantomMuspah: { rank: 44, score: 6233 },
|
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 },
|
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 },
|
||||||
@@ -239,6 +271,7 @@ test('Parse CSV to json', () => {
|
|||||||
tombsOfAmascutExpertMode: { rank: 289, score: 13070 },
|
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 },
|
||||||
@@ -255,12 +288,12 @@ test('Parse CSV with unknown activity', () => {
|
|||||||
const statsWithUnknownActivity = `${lynxTitanStats}
|
const statsWithUnknownActivity = `${lynxTitanStats}
|
||||||
-1,-1`;
|
-1,-1`;
|
||||||
expect(() => parseStats(statsWithUnknownActivity)).toThrow(
|
expect(() => parseStats(statsWithUnknownActivity)).toThrow(
|
||||||
INVALID_FORMAT_ERROR
|
InvalidFormatError
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Parse invalid CSV', () => {
|
test('Parse invalid CSV', () => {
|
||||||
expect(() => parseStats('invalid')).toThrow(INVALID_FORMAT_ERROR);
|
expect(() => parseStats('invalid')).toThrow(InvalidFormatError);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Get name format', () => {
|
describe('Get name format', () => {
|
||||||
@@ -475,7 +508,7 @@ test('Get non-existent player', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('Get stats by gamemode', async () => {
|
test('Get stats by gamemode', async () => {
|
||||||
const { skills, bosses } = await getStatsByGamemode(
|
const { skills, bosses, bountyHunter } = await getStatsByGamemode(
|
||||||
LYNX_TITAN_FORMATTED_NAME
|
LYNX_TITAN_FORMATTED_NAME
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -508,8 +541,10 @@ 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(2);
|
expect.assertions(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Get stats options', () => {
|
describe('Get stats options', () => {
|
||||||
@@ -518,13 +553,13 @@ describe('Get stats options', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
axios.get = jest.fn(
|
axios.get = jest.fn(
|
||||||
(url) =>
|
(url) =>
|
||||||
new Promise<any>((resolve) =>
|
new Promise<any>((resolve) => {
|
||||||
resolve(
|
resolve(
|
||||||
url === getPlayerTableURL('main', rsn)
|
url === getPlayerTableURL('main', rsn)
|
||||||
? { data: lynxTitanNamePage }
|
? { data: lynxTitanNamePage }
|
||||||
: { status: 200, data: lynxTitanStats }
|
: { status: 200, data: lynxTitanStats }
|
||||||
)
|
);
|
||||||
)
|
})
|
||||||
);
|
);
|
||||||
axiosMock = axios.get as any;
|
axiosMock = axios.get as any;
|
||||||
axiosMock.mockClear();
|
axiosMock.mockClear();
|
||||||
@@ -532,11 +567,11 @@ describe('Get stats options', () => {
|
|||||||
it('fetches all gamemodes and formatted RSN when no options provided', async () => {
|
it('fetches all gamemodes and formatted RSN when no options provided', async () => {
|
||||||
await getStats(rsn);
|
await getStats(rsn);
|
||||||
expect(axiosMock.mock.calls.map((val) => val[0])).toEqual([
|
expect(axiosMock.mock.calls.map((val) => val[0])).toEqual([
|
||||||
getStatsURL('main', rsn),
|
getStatsURL('main', rsn, true),
|
||||||
getPlayerTableURL('main', rsn),
|
getPlayerTableURL('main', rsn),
|
||||||
getStatsURL('ironman', rsn),
|
getStatsURL('ironman', rsn, true),
|
||||||
getStatsURL('hardcore', rsn),
|
getStatsURL('hardcore', rsn, true),
|
||||||
getStatsURL('ultimate', rsn)
|
getStatsURL('ultimate', rsn, true)
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
it('skips fetching formatted RSN when option is provided', async () => {
|
it('skips fetching formatted RSN when option is provided', async () => {
|
||||||
@@ -557,4 +592,16 @@ describe('Get stats options', () => {
|
|||||||
)
|
)
|
||||||
).toBeFalsy();
|
).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
it('omits excluded gamemodes', async () => {
|
||||||
|
const response = await getStats(rsn, {
|
||||||
|
otherGamemodes: ['ironman', 'ultimate']
|
||||||
|
});
|
||||||
|
expect(response.hardcore).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('CSV and JSON parsing outputs identical object', async () => {
|
||||||
|
const csvOutput = parseStats(b0atyStatsCsv);
|
||||||
|
const jsonOutput = parseJsonStats(b0atyStatsJson);
|
||||||
|
expect(csvOutput).toEqual(jsonOutput);
|
||||||
});
|
});
|
||||||
|
2514
__tests__/lynxTitanNamePage.html
vendored
2514
__tests__/lynxTitanNamePage.html
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,92 +0,0 @@
|
|||||||
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
|
|
||||||
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
|
|
||||||
-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.
|
153
__tests__/lynxTitanStats.json
Normal file
153
__tests__/lynxTitanStats.json
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
{
|
||||||
|
"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": "Araxxor", "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 }
|
||||||
|
]
|
||||||
|
}
|
46
package.json
46
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "osrs-json-hiscores",
|
"name": "osrs-json-hiscores",
|
||||||
"version": "2.13.0",
|
"version": "2.19.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",
|
||||||
@@ -61,18 +61,23 @@
|
|||||||
"@typescript-eslint"
|
"@typescript-eslint"
|
||||||
],
|
],
|
||||||
"extends": [
|
"extends": [
|
||||||
"eslint-config-airbnb-typescript",
|
"airbnb-base",
|
||||||
|
"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": {
|
||||||
@@ -90,28 +95,25 @@
|
|||||||
"testEnvironment": "node"
|
"testEnvironment": "node"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.21.1",
|
"axios": "^1.6.2",
|
||||||
"jsdom": "^16.3.0",
|
"jsdom": "^22.1.0",
|
||||||
"useragent-generator": "^1.1.0"
|
"useragent-generator": "^1.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^26.0.21",
|
"@types/jest": "^29.5.3",
|
||||||
"@types/jsdom": "^16.2.3",
|
"@types/jsdom": "^21.1.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.19.0",
|
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||||
"@typescript-eslint/parser": "^4.19.0",
|
"@typescript-eslint/parser": "^6.0.0",
|
||||||
"eslint": "^7.22.0",
|
"eslint": "^8.44.0",
|
||||||
"eslint-config-airbnb-typescript": "^12.3.1",
|
"eslint-config-airbnb-typescript": "^17.1.0",
|
||||||
"eslint-config-prettier": "^8.1.0",
|
"eslint-config-prettier": "^8.8.0",
|
||||||
"eslint-plugin-import": "^2.22.1",
|
"eslint-plugin-import": "^2.27.5",
|
||||||
"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": "^26.6.3",
|
"jest": "^29.6.1",
|
||||||
"lint-staged": "^10.5.4",
|
"lint-staged": "^13.2.3",
|
||||||
"np": "^7.7.0",
|
"np": "^7.7.0",
|
||||||
"prettier": "^2.2.1",
|
"prettier": "^3.0.0",
|
||||||
"ts-jest": "^26.5.4",
|
"ts-jest": "^29.1.1",
|
||||||
"typescript": "^4.2.3"
|
"typescript": "^5.1.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
311
src/hiscores.ts
311
src/hiscores.ts
@@ -1,4 +1,4 @@
|
|||||||
import { AxiosResponse } from 'axios';
|
import axios, { AxiosRequestConfig } from 'axios';
|
||||||
import { BinaryData, JSDOM } from 'jsdom';
|
import { BinaryData, JSDOM } from 'jsdom';
|
||||||
import {
|
import {
|
||||||
Player,
|
Player,
|
||||||
@@ -14,7 +14,8 @@ import {
|
|||||||
ActivityName,
|
ActivityName,
|
||||||
PlayerActivityRow,
|
PlayerActivityRow,
|
||||||
Bosses,
|
Bosses,
|
||||||
GetStatsOptions
|
GetStatsOptions,
|
||||||
|
HiscoresResponse
|
||||||
} from './types';
|
} from './types';
|
||||||
import {
|
import {
|
||||||
getStatsURL,
|
getStatsURL,
|
||||||
@@ -30,23 +31,69 @@ import {
|
|||||||
getActivityPageURL,
|
getActivityPageURL,
|
||||||
httpGet,
|
httpGet,
|
||||||
BOSSES,
|
BOSSES,
|
||||||
INVALID_FORMAT_ERROR,
|
InvalidFormatError,
|
||||||
validateRSN
|
PlayerNotFoundError,
|
||||||
|
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(rsn: string): Promise<string> {
|
export async function getRSNFormat(
|
||||||
|
rsn: string,
|
||||||
|
config?: AxiosRequestConfig
|
||||||
|
): Promise<string> {
|
||||||
validateRSN(rsn);
|
validateRSN(rsn);
|
||||||
|
|
||||||
const url = getPlayerTableURL('main', rsn);
|
const url = getPlayerTableURL('main', rsn);
|
||||||
try {
|
try {
|
||||||
const response = await httpGet<string | Buffer | BinaryData | undefined>(
|
const response = await httpGet<string | Buffer | BinaryData | undefined>(
|
||||||
url
|
url,
|
||||||
|
config
|
||||||
);
|
);
|
||||||
const dom = new JSDOM(response.data);
|
const dom = new JSDOM(response.data);
|
||||||
const anchor = dom.window.document.querySelector(
|
const anchor = dom.window.document.querySelector(
|
||||||
@@ -55,12 +102,86 @@ export async function getRSNFormat(rsn: string): Promise<string> {
|
|||||||
if (anchor) {
|
if (anchor) {
|
||||||
return rsnFromElement(anchor);
|
return rsnFromElement(anchor);
|
||||||
}
|
}
|
||||||
throw Error('Player not found');
|
throw new PlayerNotFoundError();
|
||||||
} catch {
|
} catch {
|
||||||
throw Error('Player not found');
|
throw new HiScoresError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
*
|
*
|
||||||
@@ -73,11 +194,8 @@ export function parseStats(csv: string): Stats {
|
|||||||
.filter((entry) => !!entry)
|
.filter((entry) => !!entry)
|
||||||
.map((stat) => stat.split(','));
|
.map((stat) => stat.split(','));
|
||||||
|
|
||||||
if (
|
if (splitCSV.length !== SKILLS.length + ACTIVITIES.length) {
|
||||||
splitCSV.length !==
|
throw new InvalidFormatError();
|
||||||
SKILLS.length + BH_MODES.length + CLUES.length + BOSSES.length + 5
|
|
||||||
) {
|
|
||||||
throw Error(INVALID_FORMAT_ERROR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const skillObjects: Skill[] = splitCSV
|
const skillObjects: Skill[] = splitCSV
|
||||||
@@ -103,15 +221,11 @@ export function parseStats(csv: string): Stats {
|
|||||||
return activity;
|
return activity;
|
||||||
});
|
});
|
||||||
|
|
||||||
const [leaguePoints] = activityObjects.splice(0, 1);
|
const [leaguePoints, deadmanPoints] = activityObjects.splice(0, 2);
|
||||||
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 [
|
const [lastManStanding, pvpArena, soulWarsZeal, riftsClosed, colosseumGlory] =
|
||||||
lastManStanding,
|
activityObjects.splice(0, 5);
|
||||||
pvpArena,
|
|
||||||
soulWarsZeal,
|
|
||||||
riftsClosed
|
|
||||||
] = activityObjects.splice(0, 4);
|
|
||||||
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) => {
|
||||||
@@ -141,11 +255,13 @@ export function parseStats(csv: string): Stats {
|
|||||||
const stats: Stats = {
|
const stats: Stats = {
|
||||||
skills,
|
skills,
|
||||||
leaguePoints,
|
leaguePoints,
|
||||||
|
deadmanPoints,
|
||||||
bountyHunter,
|
bountyHunter,
|
||||||
lastManStanding,
|
lastManStanding,
|
||||||
pvpArena,
|
pvpArena,
|
||||||
soulWarsZeal,
|
soulWarsZeal,
|
||||||
riftsClosed,
|
riftsClosed,
|
||||||
|
colosseumGlory,
|
||||||
clues,
|
clues,
|
||||||
bosses
|
bosses
|
||||||
};
|
};
|
||||||
@@ -174,83 +290,70 @@ export async function getStats(
|
|||||||
];
|
];
|
||||||
const shouldGetFormattedRsn = options?.shouldGetFormattedRsn ?? true;
|
const shouldGetFormattedRsn = options?.shouldGetFormattedRsn ?? true;
|
||||||
|
|
||||||
const mainRes = await httpGet<string>(getStatsURL('main', rsn));
|
const main = await getOfficialStats(rsn, 'main', options?.axiosConfigs?.main);
|
||||||
if (mainRes.status === 200) {
|
|
||||||
const emptyResponse: AxiosResponse<string> = {
|
const getModeStats = async (
|
||||||
status: 404,
|
mode: Extract<Gamemode, 'ironman' | 'hardcore' | 'ultimate'>
|
||||||
data: '',
|
): Promise<HiscoresResponse | undefined> =>
|
||||||
statusText: '',
|
otherGamemodes.includes(mode)
|
||||||
headers: {},
|
? getOfficialStats(rsn, mode, options?.axiosConfigs?.[mode]).catch(
|
||||||
config: {}
|
() => undefined
|
||||||
};
|
)
|
||||||
const getModeStats = async (
|
|
||||||
mode: Extract<Gamemode, 'ironman' | 'hardcore' | 'ultimate'>
|
|
||||||
): Promise<AxiosResponse<string>> =>
|
|
||||||
otherGamemodes.includes(mode)
|
|
||||||
? httpGet<string>(getStatsURL(mode, rsn)).catch((err) => err)
|
|
||||||
: emptyResponse;
|
|
||||||
const formattedName = shouldGetFormattedRsn
|
|
||||||
? await getRSNFormat(rsn).catch(() => undefined)
|
|
||||||
: undefined;
|
: undefined;
|
||||||
|
const formattedName = shouldGetFormattedRsn
|
||||||
|
? await getRSNFormat(rsn, options?.axiosConfigs?.rsn).catch(() => undefined)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const player: Player = {
|
const player: Player = {
|
||||||
name: formattedName ?? rsn,
|
name: formattedName ?? rsn,
|
||||||
mode: 'main',
|
mode: 'main',
|
||||||
dead: false,
|
dead: false,
|
||||||
deulted: false,
|
deulted: false,
|
||||||
deironed: false
|
deironed: false
|
||||||
};
|
};
|
||||||
player.main = parseStats(mainRes.data);
|
player.main = parseJsonStats(main);
|
||||||
|
|
||||||
const ironRes = await getModeStats('ironman');
|
const iron = await getModeStats('ironman');
|
||||||
if (ironRes.status === 200) {
|
if (iron) {
|
||||||
player.ironman = parseStats(ironRes.data);
|
player.ironman = parseJsonStats(iron);
|
||||||
const hcRes = await getModeStats('hardcore');
|
const hc = await getModeStats('hardcore');
|
||||||
const ultRes = await getModeStats('ultimate');
|
const ult = await getModeStats('ultimate');
|
||||||
if (hcRes.status === 200) {
|
if (hc) {
|
||||||
player.mode = 'hardcore';
|
player.mode = 'hardcore';
|
||||||
player.hardcore = parseStats(hcRes.data);
|
player.hardcore = parseJsonStats(hc);
|
||||||
if (
|
if (
|
||||||
player.ironman.skills.overall.xp !== player.hardcore.skills.overall.xp
|
player.ironman.skills.overall.xp !== player.hardcore.skills.overall.xp
|
||||||
) {
|
) {
|
||||||
player.dead = true;
|
player.dead = true;
|
||||||
player.mode = 'ironman';
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
player.main.skills.overall.xp !== player.ironman.skills.overall.xp
|
|
||||||
) {
|
|
||||||
player.deironed = true;
|
|
||||||
player.mode = 'main';
|
|
||||||
}
|
|
||||||
} else if (ultRes.status === 200) {
|
|
||||||
player.mode = 'ultimate';
|
|
||||||
player.ultimate = parseStats(ultRes.data);
|
|
||||||
if (
|
|
||||||
player.ironman.skills.overall.xp !== player.ultimate.skills.overall.xp
|
|
||||||
) {
|
|
||||||
player.deulted = true;
|
|
||||||
player.mode = 'ironman';
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
player.main.skills.overall.xp !== player.ironman.skills.overall.xp
|
|
||||||
) {
|
|
||||||
player.deironed = true;
|
|
||||||
player.mode = 'main';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
player.mode = 'ironman';
|
player.mode = 'ironman';
|
||||||
if (
|
}
|
||||||
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) {
|
||||||
|
player.mode = 'ultimate';
|
||||||
|
player.ultimate = parseJsonStats(ult);
|
||||||
|
if (
|
||||||
|
player.ironman.skills.overall.xp !== player.ultimate.skills.overall.xp
|
||||||
|
) {
|
||||||
|
player.deulted = true;
|
||||||
|
player.mode = 'ironman';
|
||||||
|
}
|
||||||
|
if (player.main.skills.overall.xp !== player.ironman.skills.overall.xp) {
|
||||||
|
player.deironed = true;
|
||||||
|
player.mode = 'main';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
player.mode = 'ironman';
|
||||||
|
if (player.main.skills.overall.xp !== player.ironman.skills.overall.xp) {
|
||||||
|
player.deironed = true;
|
||||||
|
player.mode = 'main';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return player;
|
|
||||||
}
|
}
|
||||||
throw Error('Player not found');
|
|
||||||
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -258,21 +361,20 @@ export async function getStats(
|
|||||||
*
|
*
|
||||||
* @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);
|
validateRSN(rsn);
|
||||||
if (!GAMEMODES.includes(mode)) {
|
if (!GAMEMODES.includes(mode)) {
|
||||||
throw Error('Invalid game mode');
|
throw Error('Invalid game mode');
|
||||||
}
|
}
|
||||||
const response = await httpGet<string>(getStatsURL(mode, rsn));
|
const response = await getOfficialStats(rsn, mode, config);
|
||||||
if (response.status !== 200) {
|
const stats = parseJsonStats(response);
|
||||||
throw Error('Player not found');
|
|
||||||
}
|
|
||||||
const stats = parseStats(response.data);
|
|
||||||
|
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
@@ -280,7 +382,8 @@ 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');
|
||||||
@@ -291,7 +394,10 @@ export async function getSkillPage(
|
|||||||
}
|
}
|
||||||
const url = getSkillPageURL(mode, skill, page);
|
const url = getSkillPageURL(mode, skill, page);
|
||||||
|
|
||||||
const response = await httpGet<string | Buffer | BinaryData | undefined>(url);
|
const response = await httpGet<string | Buffer | BinaryData | undefined>(
|
||||||
|
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'
|
||||||
@@ -323,12 +429,14 @@ 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');
|
||||||
@@ -339,7 +447,10 @@ export async function getActivityPage(
|
|||||||
}
|
}
|
||||||
const url = getActivityPageURL(mode, activity, page);
|
const url = getActivityPageURL(mode, activity, page);
|
||||||
|
|
||||||
const response = await httpGet<string | Buffer | BinaryData | undefined>(url);
|
const response = await httpGet<string | Buffer | BinaryData | undefined>(
|
||||||
|
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'
|
||||||
|
71
src/types.ts
71
src/types.ts
@@ -1,3 +1,5 @@
|
|||||||
|
import { AxiosRequestConfig } from 'axios';
|
||||||
|
|
||||||
export type Gamemode =
|
export type Gamemode =
|
||||||
| 'main'
|
| 'main'
|
||||||
| 'ironman'
|
| 'ironman'
|
||||||
@@ -60,13 +62,14 @@ export type ClueType =
|
|||||||
|
|
||||||
export type Clues = { [Type in ClueType]: Activity };
|
export type Clues = { [Type in ClueType]: Activity };
|
||||||
|
|
||||||
export type BHType = 'rogue' | 'hunter';
|
export type BHType = 'rogue' | 'hunter' | 'rogueV2' | 'hunterV2';
|
||||||
|
|
||||||
export type BH = { [Type in BHType]: Activity };
|
export type BH = { [Type in BHType]: Activity };
|
||||||
|
|
||||||
export type Boss =
|
export type Boss =
|
||||||
| 'abyssalSire'
|
| 'abyssalSire'
|
||||||
| 'alchemicalHydra'
|
| 'alchemicalHydra'
|
||||||
|
| 'araxxor'
|
||||||
| 'artio'
|
| 'artio'
|
||||||
| 'barrows'
|
| 'barrows'
|
||||||
| 'bryophyta'
|
| 'bryophyta'
|
||||||
@@ -84,6 +87,7 @@ export type Boss =
|
|||||||
| 'dagannothRex'
|
| 'dagannothRex'
|
||||||
| 'dagannothSupreme'
|
| 'dagannothSupreme'
|
||||||
| 'derangedArchaeologist'
|
| 'derangedArchaeologist'
|
||||||
|
| 'dukeSucellus'
|
||||||
| 'generalGraardor'
|
| 'generalGraardor'
|
||||||
| 'giantMole'
|
| 'giantMole'
|
||||||
| 'grotesqueGuardians'
|
| 'grotesqueGuardians'
|
||||||
@@ -93,6 +97,7 @@ export type Boss =
|
|||||||
| 'kraken'
|
| 'kraken'
|
||||||
| 'kreeArra'
|
| 'kreeArra'
|
||||||
| 'krilTsutsaroth'
|
| 'krilTsutsaroth'
|
||||||
|
| 'lunarChests'
|
||||||
| 'mimic'
|
| 'mimic'
|
||||||
| 'nex'
|
| 'nex'
|
||||||
| 'nightmare'
|
| 'nightmare'
|
||||||
@@ -101,11 +106,15 @@ export type Boss =
|
|||||||
| 'phantomMuspah'
|
| 'phantomMuspah'
|
||||||
| 'sarachnis'
|
| 'sarachnis'
|
||||||
| 'scorpia'
|
| 'scorpia'
|
||||||
|
| 'scurrius'
|
||||||
| 'skotizo'
|
| 'skotizo'
|
||||||
|
| 'solHeredit'
|
||||||
| 'spindel'
|
| 'spindel'
|
||||||
| 'tempoross'
|
| 'tempoross'
|
||||||
| 'gauntlet'
|
| 'gauntlet'
|
||||||
| 'corruptedGauntlet'
|
| 'corruptedGauntlet'
|
||||||
|
| 'leviathan'
|
||||||
|
| 'whisperer'
|
||||||
| 'theatreOfBlood'
|
| 'theatreOfBlood'
|
||||||
| 'theatreOfBloodHardMode'
|
| 'theatreOfBloodHardMode'
|
||||||
| 'thermonuclearSmokeDevil'
|
| 'thermonuclearSmokeDevil'
|
||||||
@@ -113,6 +122,7 @@ export type Boss =
|
|||||||
| 'tombsOfAmascutExpertMode'
|
| 'tombsOfAmascutExpertMode'
|
||||||
| 'tzKalZuk'
|
| 'tzKalZuk'
|
||||||
| 'tzTokJad'
|
| 'tzTokJad'
|
||||||
|
| 'vardorvis'
|
||||||
| 'venenatis'
|
| 'venenatis'
|
||||||
| 'vetion'
|
| 'vetion'
|
||||||
| 'vorkath'
|
| 'vorkath'
|
||||||
@@ -124,6 +134,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'
|
||||||
@@ -137,17 +150,26 @@ 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;
|
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 };
|
||||||
@@ -181,4 +203,51 @@ export interface GetStatsOptions {
|
|||||||
* @defaultvalue `true`
|
* @defaultvalue `true`
|
||||||
*/
|
*/
|
||||||
shouldGetFormattedRsn?: boolean;
|
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[];
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ 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 = {
|
||||||
@@ -62,7 +63,7 @@ export const CLUES: ClueType[] = [
|
|||||||
'elite',
|
'elite',
|
||||||
'master'
|
'master'
|
||||||
];
|
];
|
||||||
export const BH_MODES: BHType[] = ['hunter', 'rogue'];
|
export const BH_MODES: BHType[] = ['hunterV2', 'rogueV2', 'hunter', 'rogue'];
|
||||||
export const GAMEMODES: Gamemode[] = [
|
export const GAMEMODES: Gamemode[] = [
|
||||||
'main',
|
'main',
|
||||||
'ironman',
|
'ironman',
|
||||||
@@ -75,6 +76,7 @@ export const GAMEMODES: Gamemode[] = [
|
|||||||
export const BOSSES: Boss[] = [
|
export const BOSSES: Boss[] = [
|
||||||
'abyssalSire',
|
'abyssalSire',
|
||||||
'alchemicalHydra',
|
'alchemicalHydra',
|
||||||
|
'araxxor',
|
||||||
'artio',
|
'artio',
|
||||||
'barrows',
|
'barrows',
|
||||||
'bryophyta',
|
'bryophyta',
|
||||||
@@ -92,6 +94,7 @@ export const BOSSES: Boss[] = [
|
|||||||
'dagannothRex',
|
'dagannothRex',
|
||||||
'dagannothSupreme',
|
'dagannothSupreme',
|
||||||
'derangedArchaeologist',
|
'derangedArchaeologist',
|
||||||
|
'dukeSucellus',
|
||||||
'generalGraardor',
|
'generalGraardor',
|
||||||
'giantMole',
|
'giantMole',
|
||||||
'grotesqueGuardians',
|
'grotesqueGuardians',
|
||||||
@@ -101,6 +104,7 @@ export const BOSSES: Boss[] = [
|
|||||||
'kraken',
|
'kraken',
|
||||||
'kreeArra',
|
'kreeArra',
|
||||||
'krilTsutsaroth',
|
'krilTsutsaroth',
|
||||||
|
'lunarChests',
|
||||||
'mimic',
|
'mimic',
|
||||||
'nex',
|
'nex',
|
||||||
'nightmare',
|
'nightmare',
|
||||||
@@ -109,11 +113,15 @@ export const BOSSES: Boss[] = [
|
|||||||
'phantomMuspah',
|
'phantomMuspah',
|
||||||
'sarachnis',
|
'sarachnis',
|
||||||
'scorpia',
|
'scorpia',
|
||||||
|
'scurrius',
|
||||||
'skotizo',
|
'skotizo',
|
||||||
|
'solHeredit',
|
||||||
'spindel',
|
'spindel',
|
||||||
'tempoross',
|
'tempoross',
|
||||||
'gauntlet',
|
'gauntlet',
|
||||||
'corruptedGauntlet',
|
'corruptedGauntlet',
|
||||||
|
'leviathan',
|
||||||
|
'whisperer',
|
||||||
'theatreOfBlood',
|
'theatreOfBlood',
|
||||||
'theatreOfBloodHardMode',
|
'theatreOfBloodHardMode',
|
||||||
'thermonuclearSmokeDevil',
|
'thermonuclearSmokeDevil',
|
||||||
@@ -121,6 +129,7 @@ export const BOSSES: Boss[] = [
|
|||||||
'tombsOfAmascutExpertMode',
|
'tombsOfAmascutExpertMode',
|
||||||
'tzKalZuk',
|
'tzKalZuk',
|
||||||
'tzTokJad',
|
'tzTokJad',
|
||||||
|
'vardorvis',
|
||||||
'venenatis',
|
'venenatis',
|
||||||
'vetion',
|
'vetion',
|
||||||
'vorkath',
|
'vorkath',
|
||||||
@@ -130,6 +139,9 @@ export const BOSSES: Boss[] = [
|
|||||||
];
|
];
|
||||||
export const ACTIVITIES: ActivityName[] = [
|
export const ACTIVITIES: ActivityName[] = [
|
||||||
'leaguePoints',
|
'leaguePoints',
|
||||||
|
'deadmanPoints',
|
||||||
|
'hunterBHV2',
|
||||||
|
'rogueBHV2',
|
||||||
'hunterBH',
|
'hunterBH',
|
||||||
'rogueBH',
|
'rogueBH',
|
||||||
'allClues',
|
'allClues',
|
||||||
@@ -143,6 +155,7 @@ export const ACTIVITIES: ActivityName[] = [
|
|||||||
'pvpArena',
|
'pvpArena',
|
||||||
'soulWarsZeal',
|
'soulWarsZeal',
|
||||||
'riftsClosed',
|
'riftsClosed',
|
||||||
|
'colosseumGlory',
|
||||||
...BOSSES
|
...BOSSES
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -153,11 +166,12 @@ 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',
|
||||||
|
araxxor: 'Araxxor',
|
||||||
artio: 'Artio',
|
artio: 'Artio',
|
||||||
barrows: 'Barrows Chests',
|
barrows: 'Barrows Chests',
|
||||||
bryophyta: 'Bryophyta',
|
bryophyta: 'Bryophyta',
|
||||||
callisto: 'Callisto',
|
callisto: 'Callisto',
|
||||||
calvarion: "Cal'varion",
|
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',
|
||||||
@@ -170,6 +184,7 @@ 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',
|
||||||
@@ -179,19 +194,24 @@ 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: 'The Nightmare of Ashihama',
|
nightmare: 'Nightmare',
|
||||||
phosanisNightmare: "Phosani's Nightmare",
|
phosanisNightmare: "Phosani's Nightmare",
|
||||||
obor: 'Obor',
|
obor: 'Obor',
|
||||||
phantomMuspah: 'Phantom Muspah',
|
phantomMuspah: 'Phantom Muspah',
|
||||||
sarachnis: 'Sarachnis',
|
sarachnis: 'Sarachnis',
|
||||||
scorpia: 'Scorpia',
|
scorpia: 'Scorpia',
|
||||||
|
scurrius: 'Scurrius',
|
||||||
skotizo: 'Skotizo',
|
skotizo: 'Skotizo',
|
||||||
|
solHeredit: 'Sol Heredit',
|
||||||
spindel: 'Spindel',
|
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',
|
||||||
@@ -199,6 +219,7 @@ export const FORMATTED_BOSS_NAMES: FormattedBossNames = {
|
|||||||
tombsOfAmascutExpertMode: 'Tombs of Amascut: Expert Mode',
|
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',
|
||||||
@@ -257,14 +278,56 @@ export type FormattedBHNames = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const FORMATTED_BH_NAMES: FormattedBHNames = {
|
export const FORMATTED_BH_NAMES: FormattedBHNames = {
|
||||||
rogue: 'Bounty Hunter - Rogue',
|
rogue: 'Bounty Hunter (Legacy) - Rogue',
|
||||||
hunter: 'Bounty Hunter - Hunter'
|
hunter: 'Bounty Hunter (Legacy) - Hunter',
|
||||||
|
rogueV2: 'Bounty Hunter - Rogue',
|
||||||
|
hunterV2: 'Bounty Hunter - Hunter'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FORMATTED_LMS = 'Last Man Standing';
|
export const FORMATTED_LMS = 'LMS - Rank';
|
||||||
export const FORMATTED_PVP_ARENA = 'PvP Arena';
|
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_RIFTS_CLOSED = 'Rifts Closed';
|
export const FORMATTED_DEADMAN_POINTS = 'Deadman Points';
|
||||||
|
export const FORMATTED_RIFTS_CLOSED = 'Rifts closed';
|
||||||
|
export const FORMATTED_COLOSSEUM_GLORY = 'Colosseum Glory';
|
||||||
|
|
||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import axios from 'axios';
|
import axios, { AxiosRequestConfig } 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,7 +6,9 @@ import {
|
|||||||
STATS_URL,
|
STATS_URL,
|
||||||
SCORES_URL,
|
SCORES_URL,
|
||||||
SKILLS,
|
SKILLS,
|
||||||
ACTIVITIES
|
ACTIVITIES,
|
||||||
|
JSON_STATS_URL,
|
||||||
|
InvalidRSNError
|
||||||
} from './constants';
|
} from './constants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -14,10 +16,13 @@ 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) =>
|
export const getStatsURL = (gamemode: Gamemode, rsn: string, json = false) =>
|
||||||
`${GAMEMODE_URL[gamemode]}${STATS_URL}${encodeURIComponent(rsn)}`;
|
`${GAMEMODE_URL[gamemode]}${
|
||||||
|
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.
|
||||||
@@ -96,12 +101,16 @@ 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>(url: string) =>
|
export const httpGet = <Response>(
|
||||||
|
url: string,
|
||||||
|
config: AxiosRequestConfig = {}
|
||||||
|
) =>
|
||||||
axios.get<Response>(url, {
|
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
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -111,10 +120,10 @@ export const httpGet = <Response>(url: string) =>
|
|||||||
*/
|
*/
|
||||||
export const validateRSN = (rsn: string) => {
|
export const validateRSN = (rsn: string) => {
|
||||||
if (typeof rsn !== 'string') {
|
if (typeof rsn !== 'string') {
|
||||||
throw Error('RSN must be a string');
|
throw new InvalidRSNError('RSN must be a string');
|
||||||
} else if (!/^[a-zA-Z0-9 _-]+$/.test(rsn)) {
|
} else if (!/^[a-zA-Z0-9 _-]+$/.test(rsn)) {
|
||||||
throw Error('RSN contains invalid character');
|
throw new InvalidRSNError('RSN contains invalid character');
|
||||||
} else if (rsn.length > 12 || rsn.length < 1) {
|
} else if (rsn.length > 12 || rsn.length < 1) {
|
||||||
throw Error('RSN must be between 1 and 12 characters');
|
throw new InvalidRSNError('RSN must be between 1 and 12 characters');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -6,10 +6,7 @@
|
|||||||
"outDir": "./lib",
|
"outDir": "./lib",
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"lib": ["ES2015", "DOM", "DOM.Iterable"],
|
"lib": ["ES2015", "DOM", "DOM.Iterable"],
|
||||||
"typeRoots": [
|
"typeRoots": ["./node_modules/@types", "./src/@types"]
|
||||||
"./node_modules/@types",
|
|
||||||
"./src/@types"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": ["src"],
|
||||||
"exclude": ["node_modules", "**/__tests__/*"]
|
"exclude": ["node_modules", "**/__tests__/*"]
|
||||||
|
Reference in New Issue
Block a user