Compare commits

...

50 Commits

Author SHA1 Message Date
maxswa
e4d2b10488 v2.13.1 2023-04-12 21:51:49 -04:00
Max Swartwout
0cfd0e2471 Merge pull request #73 from davidvorona/main
Fix spelling of Calvar'ion
2023-04-12 21:50:54 -04:00
David Vorona
9c284ad4b0 Fix spelling of Calvar'ion 2023-04-12 10:26:06 -07:00
Max Swartwout
ec60a72c08 Merge pull request #71 from maxswa/fix-gh-workflow
Update GH action versions.
2023-04-12 13:04:45 -04:00
maxswa
d45e2efcfd Update GH action versions. 2023-04-12 13:00:18 -04:00
maxswa
f80b417121 v2.13.0 2023-04-12 12:53:39 -04:00
maxswa
415fe2f02b Update np 2023-04-12 12:50:41 -04:00
Max Swartwout
968799176f Merge pull request #70 from maxswa/wilderness-boss-variants
Add wilderness boss variants.
2023-04-12 12:46:11 -04:00
maxswa
f45ae67813 Add wilderness boss variants. 2023-04-12 12:42:37 -04:00
maxswa
7724636b40 v2.12.1 2023-03-19 23:33:33 -04:00
maxswa
3954b04acc Add publishConfig to package.json 2023-03-19 23:33:03 -04:00
Max Swartwout
ee7389455c Merge pull request #68 from maxswa/add-options
Add `validateRSN`, `GetStatsOptions`
2023-03-19 23:28:34 -04:00
maxswa
54c11635e0 Add GetStatsOptions for getStats 2023-03-19 23:14:10 -04:00
maxswa
dc799cf363 Add validateRSN helper function. 2023-03-19 22:38:45 -04:00
maxswa
d4c7aaff2a v2.12.0 2023-02-19 18:31:43 -05:00
Max Swartwout
03c089ebff Merge pull request #66 from maxswa/account-builds
Add new `Gamemode`s
2023-02-19 18:31:00 -05:00
Max Swartwout
96641d875a Merge pull request #65 from maxswa/dependabot/npm_and_yarn/http-cache-semantics-4.1.1
Bump http-cache-semantics from 4.1.0 to 4.1.1
2023-02-19 18:28:33 -05:00
maxswa
b2bdd3d2ce Add new Gamemodes 2023-02-19 18:27:56 -05:00
dependabot[bot]
a4ddfaf199 Bump http-cache-semantics from 4.1.0 to 4.1.1
Bumps [http-cache-semantics](https://github.com/kornelski/http-cache-semantics) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/kornelski/http-cache-semantics/releases)
- [Commits](https://github.com/kornelski/http-cache-semantics/compare/v4.1.0...v4.1.1)

---
updated-dependencies:
- dependency-name: http-cache-semantics
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-03 04:54:08 +00:00
maxswa
efbcdfc709 v2.11.0 2023-01-13 11:21:51 -05:00
Max Swartwout
fe913aba1f Merge pull request #64 from davidvorona/add-muspah
Add phantom muspah
2023-01-12 15:30:21 -05:00
Max Swartwout
1497653bab Merge pull request #63 from maxswa/dependabot/npm_and_yarn/json5-1.0.2
Bump json5 from 1.0.1 to 1.0.2
2023-01-12 15:29:38 -05:00
David Vorona
f76de38eb1 Add phantom muspah 2023-01-11 11:45:59 -08:00
dependabot[bot]
2f1d10b309 Bump json5 from 1.0.1 to 1.0.2
Bumps [json5](https://github.com/json5/json5) from 1.0.1 to 1.0.2.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v1.0.1...v1.0.2)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-06 22:36:14 +00:00
Max Swartwout
f08424cb81 Update build badge in README 2022-12-19 12:24:03 -05:00
maxswa
69088db3b6 v2.10.2 2022-12-15 23:24:59 +00:00
Max Swartwout
9cc89735cd Merge pull request #61 from maxswa/get-rsn-format-highlight-row
Fix `getRSNFormat`
2022-12-15 18:15:34 -05:00
Max Swartwout
a126ae4fc7 Merge pull request #58 from maxswa/dependabot/npm_and_yarn/minimatch-3.1.2
Bump minimatch from 3.0.4 to 3.1.2
2022-12-15 18:11:34 -05:00
Max Swartwout
52f3df1838 Merge pull request #59 from maxswa/dependabot/npm_and_yarn/qs-6.5.3
Bump qs from 6.5.2 to 6.5.3
2022-12-15 18:11:23 -05:00
maxswa
2891bbddc4 Fix getRSNFormat 2022-12-15 18:09:21 -05:00
dependabot[bot]
e2ea2fe33d Bump qs from 6.5.2 to 6.5.3
Bumps [qs](https://github.com/ljharb/qs) from 6.5.2 to 6.5.3.
- [Release notes](https://github.com/ljharb/qs/releases)
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.5.2...v6.5.3)

---
updated-dependencies:
- dependency-name: qs
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-09 06:28:03 +00:00
maxswa
b7ae812f45 v2.10.1 2022-12-04 14:17:36 -05:00
dependabot[bot]
240628d9cc Bump minimatch from 3.0.4 to 3.1.2
Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.0.4 to 3.1.2.
- [Release notes](https://github.com/isaacs/minimatch/releases)
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.0.4...v3.1.2)

---
updated-dependencies:
- dependency-name: minimatch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-04 19:14:24 +00:00
Max Swartwout
b754663f6d Merge pull request #56 from APKiwi/main
Added "-" as a valid character in "getStats"
2022-12-04 14:13:51 -05:00
Max Swartwout
0678f7c979 Merge pull request #57 from maxswa/dependabot/npm_and_yarn/decode-uri-component-0.2.2
Bump decode-uri-component from 0.2.0 to 0.2.2
2022-12-04 14:13:27 -05:00
dependabot[bot]
2a60283a93 Bump decode-uri-component from 0.2.0 to 0.2.2
Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2.
- [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases)
- [Commits](https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.2)

---
updated-dependencies:
- dependency-name: decode-uri-component
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-03 13:49:33 +00:00
SirKhaoz
b912731cf9 Added "-" as a valid character in "getStats" 2022-11-04 21:56:39 +13:00
maxswa
cf44c57ec6 v2.10.0 2022-08-24 08:25:48 -04:00
Max Swartwout
3f34c9ab61 Merge pull request #55 from maxswa/toa
Add Tombs of Amascut.
2022-08-24 08:24:58 -04:00
maxswa
1ef73b6b0c Add Tombs of Amascut. 2022-08-24 07:48:05 -04:00
maxswa
08ecf68689 v2.9.0 2022-07-17 13:08:27 -04:00
Max Swartwout
ab3ee70237 Merge pull request #53 from maxswa/dependabot/npm_and_yarn/jsdom-16.5.0
Bump jsdom from 16.4.0 to 16.5.0
2022-07-17 13:06:51 -04:00
Max Swartwout
cad20d41cb Merge pull request #54 from davidvorona/pvp-arena
Add support for PvP Arena activity
2022-07-17 13:02:32 -04:00
David Vorona
2877b9c8a1 Add support for PvP Arena activity 2022-07-15 14:29:06 -07:00
dependabot[bot]
afbb22749a Bump jsdom from 16.4.0 to 16.5.0
Bumps [jsdom](https://github.com/jsdom/jsdom) from 16.4.0 to 16.5.0.
- [Release notes](https://github.com/jsdom/jsdom/releases)
- [Changelog](https://github.com/jsdom/jsdom/blob/master/Changelog.md)
- [Commits](https://github.com/jsdom/jsdom/compare/16.4.0...16.5.0)

---
updated-dependencies:
- dependency-name: jsdom
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-23 04:56:25 +00:00
maxswa
eebf76a34c v2.8.0 2022-04-18 15:50:35 -04:00
Max Swartwout
e60442a7ce Merge pull request #50 from maxswa/dependabot/npm_and_yarn/minimist-1.2.6
Bump minimist from 1.2.5 to 1.2.6
2022-04-18 15:47:45 -04:00
Max Swartwout
1132a085e1 Merge pull request #52 from molo-pl/main
Add support for Rifts Closed
2022-04-18 15:47:35 -04:00
molo-pl
a2a34a46e3 Add support for Rifts Closed 2022-04-15 11:30:56 +02:00
dependabot[bot]
71a76a12fd Bump minimist from 1.2.5 to 1.2.6
Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-28 13:26:44 +00:00
13 changed files with 4851 additions and 3209 deletions

View File

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

View File

@@ -3,7 +3,7 @@
[![npm](https://img.shields.io/npm/v/osrs-json-hiscores.svg?style=flat-square)](https://www.npmjs.com/package/osrs-json-hiscores)
[![downloads](https://img.shields.io/npm/dm/osrs-json-hiscores.svg?style=flat-square)](https://npm-stat.com/charts.html?package=osrs-json-hiscores)
[![types](https://img.shields.io/npm/types/osrs-json-hiscores.svg?style=flat-square)](https://github.com/maxswa/osrs-json-hiscores/blob/master/src/types.ts)
[![build](https://img.shields.io/github/workflow/status/maxswa/osrs-json-hiscores/CI?style=flat-square)](https://github.com/maxswa/osrs-json-hiscores/actions/workflows/main.yml?query=branch%3Amain)
[![build](https://img.shields.io/github/actions/workflow/status/maxswa/osrs-json-hiscores/main.yml?style=flat-square&branch=main)](https://github.com/maxswa/osrs-json-hiscores/actions/workflows/main.yml?query=branch%3Amain)
**The Old School Runescape API wrapper that does more!**
@@ -109,7 +109,9 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses
| Bounty Hunter (Rogue) | `rogueBH` |
| Bounty Hunter (Hunter) | `hunterBH` |
| Last Man Standing | `lastManStanding` |
| PvP Arena | `pvpArena` |
| Soul Wars Zeal | `soulWarsZeal` |
| Rifts Closed | `riftsClosed` |
### Leagues
@@ -123,9 +125,11 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses
| --------------------------------- | :----------------------------: |
| Abyssal Sire | `abyssalSire` |
| Alchemical Hydra | `alchemicalHydra` |
| Artio | `artio` |
| Barrows Chests | `barrows` |
| Bryophyta | `bryophyta` |
| Callisto | `callisto` |
| Calvar'ion | `calvarion` |
| Cerberus | `cerberus` |
| Chambers Of Xeric | `chambersOfXeric` |
| Chambers Of Xeric: Challenge Mode | `chambersOfXericChallengeMode` |
@@ -152,15 +156,19 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses
| The Nightmare of Ashihama | `nightmare` |
| Phosani's Nightmare | `phosanisNightmare` |
| Obor | `obor` |
| Phantom Muspah | `phantomMuspah` |
| Sarachnis | `sarachnis` |
| Scorpia | `scorpia` |
| Skotizo | `skotizo` |
| Spindel | `spindel` |
| Tempoross | `tempoross` |
| The Gauntlet | `gauntlet` |
| The Corrupted Gauntlet | `corruptedGauntlet` |
| Theatre Of Blood | `theatreOfBlood` |
| Theatre Of Blood: Hard Mode | `theatreOfBloodHardMode` |
| Thermonuclear Smoke Devil | `thermonuclearSmokeDevil` |
| Tombs of Amascut | `tombsOfAmascut` |
| Tombs of Amascut: Expert Mode | `tombsOfAmascutExpertMode` |
| TzKal-Zuk | `tzKalZuk` |
| TzTok-Jad | `tzTokJad` |
| Venenatis | `venenatis` |
@@ -192,7 +200,9 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses
leaguePoints: {},
bountyHunter: {},
lastManStanding: {},
pvpArena: {},
soulWarsZeal: {},
riftsClosed: {},
bosses: {}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -87,12 +87,16 @@ test('Parse CSV to json', () => {
392,250
1,6143
4814,898
13,4057
37,225
25,1110
382,2780
944,3000
704,892
1981,1452
4981,23
888,1046
444,792
613,4856
102,4038
156,334
@@ -119,15 +123,19 @@ test('Parse CSV to json', () => {
3,22666
2,84
26,323
44,6233
201,1101
82,3404
5085,61
678,903
18823,23
63,375
2870,6
6984,138
23,923141
4043,2000
4073,1020
289,13070
489,8
967,47
11155,223
@@ -170,7 +178,9 @@ test('Parse CSV to json', () => {
rogue: { rank: 89912, score: 37 }
},
lastManStanding: { rank: 4814, score: 898 },
pvpArena: { rank: 13, score: 4057 },
soulWarsZeal: { rank: 37, score: 225 },
riftsClosed: { rank: 25, score: 1110 },
clues: {
all: { rank: 32, score: 12148 },
beginner: { rank: 3105, score: 76 },
@@ -183,9 +193,11 @@ test('Parse CSV to json', () => {
bosses: {
abyssalSire: { rank: 382, score: 2780 },
alchemicalHydra: { rank: 944, score: 3000 },
artio: { rank: 704, score: 892 },
barrows: { rank: 1981, score: 1452 },
bryophyta: { rank: 4981, score: 23 },
callisto: { rank: 888, score: 1046 },
calvarion: { rank: 444, score: 792 },
cerberus: { rank: 613, score: 4856 },
chambersOfXeric: { rank: 102, score: 4038 },
chambersOfXericChallengeMode: { rank: 156, score: 334 },
@@ -212,15 +224,19 @@ test('Parse CSV to json', () => {
nightmare: { rank: 3, score: 22666 },
phosanisNightmare: { rank: 2, score: 84 },
obor: { rank: 26, score: 323 },
phantomMuspah: { rank: 44, score: 6233 },
sarachnis: { rank: 201, score: 1101 },
scorpia: { rank: 82, score: 3404 },
skotizo: { rank: 5085, score: 61 },
spindel: { rank: 678, score: 903 },
tempoross: { rank: 18823, score: 23 },
gauntlet: { rank: 63, score: 375 },
corruptedGauntlet: { rank: 2870, score: 6 },
theatreOfBlood: { rank: 6984, score: 138 },
theatreOfBloodHardMode: { rank: 23, score: 923141 },
thermonuclearSmokeDevil: { rank: 4043, score: 2000 },
tombsOfAmascut: { rank: 4073, score: 1020 },
tombsOfAmascutExpertMode: { rank: 289, score: 13070 },
tzKalZuk: { rank: 489, score: 8 },
tzTokJad: { rank: 967, score: 47 },
venenatis: { rank: 11155, score: 223 },
@@ -236,9 +252,11 @@ test('Parse CSV to json', () => {
});
test('Parse CSV with unknown activity', () => {
const statsWithUnknownActivity = lynxTitanStats + `
const statsWithUnknownActivity = `${lynxTitanStats}
-1,-1`;
expect(() => parseStats(statsWithUnknownActivity)).toThrow(INVALID_FORMAT_ERROR);
expect(() => parseStats(statsWithUnknownActivity)).toThrow(
INVALID_FORMAT_ERROR
);
});
test('Parse invalid CSV', () => {
@@ -493,3 +511,50 @@ test('Get stats by gamemode', async () => {
expect.assertions(2);
});
describe('Get stats options', () => {
const rsn = 'player';
let axiosMock: jest.Mock;
beforeEach(() => {
axios.get = jest.fn(
(url) =>
new Promise<any>((resolve) =>
resolve(
url === getPlayerTableURL('main', rsn)
? { data: lynxTitanNamePage }
: { status: 200, data: lynxTitanStats }
)
)
);
axiosMock = axios.get as any;
axiosMock.mockClear();
});
it('fetches all gamemodes and formatted RSN when no options provided', async () => {
await getStats(rsn);
expect(axiosMock.mock.calls.map((val) => val[0])).toEqual([
getStatsURL('main', rsn),
getPlayerTableURL('main', rsn),
getStatsURL('ironman', rsn),
getStatsURL('hardcore', rsn),
getStatsURL('ultimate', rsn)
]);
});
it('skips fetching formatted RSN when option is provided', async () => {
await getStats(rsn, { shouldGetFormattedRsn: false });
expect(
axiosMock.mock.calls.some(
(val) => val[0] === getPlayerTableURL('main', rsn)
)
).toBeFalsy();
});
it('skips fetching game mode when option is provided', async () => {
await getStats(rsn, {
otherGamemodes: ['ironman', 'ultimate']
});
expect(
axiosMock.mock.calls.some(
(val) => val[0] === getStatsURL('hardcore', rsn)
)
).toBeFalsy();
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -82,3 +82,11 @@
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
-1,-1
Can't render this file because it has a wrong number of fields in line 25.

View File

@@ -1,6 +1,6 @@
{
"name": "osrs-json-hiscores",
"version": "2.7.0",
"version": "2.13.1",
"description": "The Old School Runescape API wrapper that does more!",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@@ -30,6 +30,9 @@
"stats",
"skills"
],
"publishConfig": {
"registry": "https://registry.npmjs.org"
},
"author": "maxswa",
"license": "ISC",
"bugs": {
@@ -106,7 +109,7 @@
"husky": "^5.2.0",
"jest": "^26.6.3",
"lint-staged": "^10.5.4",
"np": "6.5.0",
"np": "^7.7.0",
"prettier": "^2.2.1",
"ts-jest": "^26.5.4",
"typescript": "^4.2.3"

View File

@@ -1,4 +1,5 @@
import { JSDOM } from 'jsdom';
import { AxiosResponse } from 'axios';
import { BinaryData, JSDOM } from 'jsdom';
import {
Player,
Activity,
@@ -12,7 +13,8 @@ import {
PlayerSkillRow,
ActivityName,
PlayerActivityRow,
Bosses
Bosses,
GetStatsOptions
} from './types';
import {
getStatsURL,
@@ -28,7 +30,8 @@ import {
getActivityPageURL,
httpGet,
BOSSES,
INVALID_FORMAT_ERROR
INVALID_FORMAT_ERROR,
validateRSN
} from './utils';
/**
@@ -38,24 +41,19 @@ import {
* @returns Formatted version of the rsn.
*/
export async function getRSNFormat(rsn: string): Promise<string> {
if (typeof rsn !== 'string') {
throw Error('RSN must be a string');
} else if (!/^[a-zA-Z0-9 _-]+$/.test(rsn)) {
throw Error('RSN contains invalid character');
} else if (rsn.length > 12 || rsn.length < 1) {
throw Error('RSN must be between 1 and 12 characters');
}
validateRSN(rsn);
const url = getPlayerTableURL('main', rsn);
try {
const response = await httpGet(url);
const dom = new JSDOM(response.data);
const spans = dom.window.document.querySelectorAll(
'span[style="color:#AA0022;"]'
const response = await httpGet<string | Buffer | BinaryData | undefined>(
url
);
if (spans.length >= 2) {
const nameSpan = spans[1];
return rsnFromElement(nameSpan);
const dom = new JSDOM(response.data);
const anchor = dom.window.document.querySelector(
'.personal-hiscores__row.personal-hiscores__row--type-highlight a'
);
if (anchor) {
return rsnFromElement(anchor);
}
throw Error('Player not found');
} catch {
@@ -75,7 +73,10 @@ export function parseStats(csv: string): Stats {
.filter((entry) => !!entry)
.map((stat) => stat.split(','));
if (splitCSV.length !== SKILLS.length + BH_MODES.length + CLUES.length + BOSSES.length + 3) {
if (
splitCSV.length !==
SKILLS.length + BH_MODES.length + CLUES.length + BOSSES.length + 5
) {
throw Error(INVALID_FORMAT_ERROR);
}
@@ -105,7 +106,12 @@ export function parseStats(csv: string): Stats {
const [leaguePoints] = activityObjects.splice(0, 1);
const bhObjects = activityObjects.splice(0, BH_MODES.length);
const clueObjects = activityObjects.splice(0, CLUES.length);
const [lastManStanding, soulWarsZeal] = activityObjects.splice(0, 2);
const [
lastManStanding,
pvpArena,
soulWarsZeal,
riftsClosed
] = activityObjects.splice(0, 4);
const bossObjects = activityObjects.splice(0, BOSSES.length);
const skills: Skills = skillObjects.reduce<Skills>((prev, curr, index) => {
@@ -137,7 +143,9 @@ export function parseStats(csv: string): Stats {
leaguePoints,
bountyHunter,
lastManStanding,
pvpArena,
soulWarsZeal,
riftsClosed,
clues,
bosses
};
@@ -154,25 +162,36 @@ export function parseStats(csv: string): Stats {
* @param rsn Username of the player.
* @returns Player object.
*/
export async function getStats(rsn: string): Promise<Player> {
if (typeof rsn !== 'string') {
throw Error('RSN must be a string');
} else if (!/^[a-zA-Z0-9 _]+$/.test(rsn)) {
throw Error('RSN contains invalid character');
} else if (rsn.length > 12 || rsn.length < 1) {
throw Error('RSN must be between 1 and 12 characters');
}
export async function getStats(
rsn: string,
options?: GetStatsOptions
): Promise<Player> {
validateRSN(rsn);
const otherGamemodes = options?.otherGamemodes ?? [
'ironman',
'hardcore',
'ultimate'
];
const shouldGetFormattedRsn = options?.shouldGetFormattedRsn ?? true;
const mainRes = await httpGet(getStatsURL('main', rsn));
const mainRes = await httpGet<string>(getStatsURL('main', rsn));
if (mainRes.status === 200) {
const otherResponses = await Promise.all([
httpGet(getStatsURL('ironman', rsn)).catch((err) => err),
httpGet(getStatsURL('hardcore', rsn)).catch((err) => err),
httpGet(getStatsURL('ultimate', rsn)).catch((err) => err),
getRSNFormat(rsn).catch(() => undefined)
]);
const [ironRes, hcRes, ultRes, formattedName] = otherResponses;
const emptyResponse: AxiosResponse<string> = {
status: 404,
data: '',
statusText: '',
headers: {},
config: {}
};
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;
const player: Player = {
name: formattedName ?? rsn,
@@ -183,8 +202,11 @@ export async function getStats(rsn: string): Promise<Player> {
};
player.main = parseStats(mainRes.data);
const ironRes = await getModeStats('ironman');
if (ironRes.status === 200) {
player.ironman = parseStats(ironRes.data);
const hcRes = await getModeStats('hardcore');
const ultRes = await getModeStats('ultimate');
if (hcRes.status === 200) {
player.mode = 'hardcore';
player.hardcore = parseStats(hcRes.data);
@@ -242,16 +264,11 @@ export async function getStatsByGamemode(
rsn: string,
mode: Gamemode = 'main'
): Promise<Stats> {
if (typeof rsn !== 'string') {
throw Error('RSN must be a string');
} else if (!/^[a-zA-Z0-9 _]+$/.test(rsn)) {
throw Error('RSN contains invalid character');
} else if (rsn.length > 12 || rsn.length < 1) {
throw Error('RSN must be between 1 and 12 characters');
} else if (!GAMEMODES.includes(mode)) {
validateRSN(rsn);
if (!GAMEMODES.includes(mode)) {
throw Error('Invalid game mode');
}
const response = await httpGet(getStatsURL(mode, rsn));
const response = await httpGet<string>(getStatsURL(mode, rsn));
if (response.status !== 200) {
throw Error('Player not found');
}
@@ -274,7 +291,7 @@ export async function getSkillPage(
}
const url = getSkillPageURL(mode, skill, page);
const response = await httpGet(url);
const response = await httpGet<string | Buffer | BinaryData | undefined>(url);
const dom = new JSDOM(response.data);
const playersHTML = dom.window.document.querySelectorAll(
'.personal-hiscores__row'
@@ -322,7 +339,7 @@ export async function getActivityPage(
}
const url = getActivityPageURL(mode, activity, page);
const response = await httpGet(url);
const response = await httpGet<string | Buffer | BinaryData | undefined>(url);
const dom = new JSDOM(response.data);
const playersHTML = dom.window.document.querySelectorAll(
'.personal-hiscores__row'

View File

@@ -5,7 +5,10 @@ export type Gamemode =
| 'hardcore'
| 'deadman'
| 'seasonal'
| 'tournament';
| 'tournament'
| 'skiller'
| 'oneDefence'
| 'freshStart';
export interface Skill {
rank: number;
@@ -64,9 +67,11 @@ export type BH = { [Type in BHType]: Activity };
export type Boss =
| 'abyssalSire'
| 'alchemicalHydra'
| 'artio'
| 'barrows'
| 'bryophyta'
| 'callisto'
| 'calvarion'
| 'cerberus'
| 'chambersOfXeric'
| 'chambersOfXericChallengeMode'
@@ -93,15 +98,19 @@ export type Boss =
| 'nightmare'
| 'phosanisNightmare'
| 'obor'
| 'phantomMuspah'
| 'sarachnis'
| 'scorpia'
| 'skotizo'
| 'spindel'
| 'tempoross'
| 'gauntlet'
| 'corruptedGauntlet'
| 'theatreOfBlood'
| 'theatreOfBloodHardMode'
| 'thermonuclearSmokeDevil'
| 'tombsOfAmascut'
| 'tombsOfAmascutExpertMode'
| 'tzKalZuk'
| 'tzTokJad'
| 'venenatis'
@@ -118,7 +127,9 @@ export type ActivityName =
| 'hunterBH'
| 'rogueBH'
| 'lastManStanding'
| 'pvpArena'
| 'soulWarsZeal'
| 'riftsClosed'
| 'allClues'
| 'beginnerClues'
| 'easyClues'
@@ -134,7 +145,9 @@ export interface Stats {
leaguePoints: Activity;
bountyHunter: BH;
lastManStanding: Activity;
pvpArena: Activity;
soulWarsZeal: Activity;
riftsClosed: Activity;
bosses: Bosses;
}
export type Modes = { [M in Gamemode]?: Stats };
@@ -156,3 +169,16 @@ export interface PlayerActivityRow extends Activity {
name: string;
dead: boolean;
}
export interface GetStatsOptions {
/**
* Other game modes to fetch ranks for.
* @defaultvalue `['ironman', 'hardcore', 'ultimate']`
*/
otherGamemodes?: Extract<Gamemode, 'ironman' | 'hardcore' | 'ultimate'>[];
/**
* If true, the formatted RSN will be fetched. Otherwise it will return the provided, unformatted RSN.
* @defaultvalue `true`
*/
shouldGetFormattedRsn?: boolean;
}

View File

@@ -22,7 +22,10 @@ export const GAMEMODE_URL: GamemodeUrl = {
ultimate: `${BASE_URL}_ultimate/`,
deadman: `${BASE_URL}_deadman/`,
seasonal: `${BASE_URL}_seasonal/`,
tournament: `${BASE_URL}_tournament/`
tournament: `${BASE_URL}_tournament/`,
skiller: `${BASE_URL}_skiller/`,
oneDefence: `${BASE_URL}_skiller_defence/`,
freshStart: `${BASE_URL}_fresh_start/`
};
export const SKILLS: SkillName[] = [
'overall',
@@ -72,9 +75,11 @@ export const GAMEMODES: Gamemode[] = [
export const BOSSES: Boss[] = [
'abyssalSire',
'alchemicalHydra',
'artio',
'barrows',
'bryophyta',
'callisto',
'calvarion',
'cerberus',
'chambersOfXeric',
'chambersOfXericChallengeMode',
@@ -101,15 +106,19 @@ export const BOSSES: Boss[] = [
'nightmare',
'phosanisNightmare',
'obor',
'phantomMuspah',
'sarachnis',
'scorpia',
'skotizo',
'spindel',
'tempoross',
'gauntlet',
'corruptedGauntlet',
'theatreOfBlood',
'theatreOfBloodHardMode',
'thermonuclearSmokeDevil',
'tombsOfAmascut',
'tombsOfAmascutExpertMode',
'tzKalZuk',
'tzTokJad',
'venenatis',
@@ -131,7 +140,9 @@ export const ACTIVITIES: ActivityName[] = [
'eliteClues',
'masterClues',
'lastManStanding',
'pvpArena',
'soulWarsZeal',
'riftsClosed',
...BOSSES
];
@@ -142,9 +153,11 @@ export type FormattedBossNames = {
export const FORMATTED_BOSS_NAMES: FormattedBossNames = {
abyssalSire: 'Abyssal Sire',
alchemicalHydra: 'Alchemical Hydra',
artio: 'Artio',
barrows: 'Barrows Chests',
bryophyta: 'Bryophyta',
callisto: 'Callisto',
calvarion: "Calvar'ion",
cerberus: 'Cerberus',
chambersOfXeric: 'Chambers of Xeric',
chambersOfXericChallengeMode: 'Chambers of Xeric: Challenge Mode',
@@ -171,15 +184,19 @@ export const FORMATTED_BOSS_NAMES: FormattedBossNames = {
nightmare: 'The Nightmare of Ashihama',
phosanisNightmare: "Phosani's Nightmare",
obor: 'Obor',
phantomMuspah: 'Phantom Muspah',
sarachnis: 'Sarachnis',
scorpia: 'Scorpia',
skotizo: 'Skotizo',
spindel: 'Spindel',
tempoross: 'Tempoross',
gauntlet: 'The Gauntlet',
corruptedGauntlet: 'The Corrupted Gauntlet',
theatreOfBlood: 'Theatre of Blood',
theatreOfBloodHardMode: 'Theatre of Blood: Hard Mode',
thermonuclearSmokeDevil: 'Thermonuclear Smoke Devil',
tombsOfAmascut: 'Tombs of Amascut',
tombsOfAmascutExpertMode: 'Tombs of Amascut: Expert Mode',
tzKalZuk: 'TzKal-Zuk',
tzTokJad: 'TzTok-Jad',
venenatis: 'Venenatis',
@@ -245,7 +262,9 @@ export const FORMATTED_BH_NAMES: FormattedBHNames = {
};
export const FORMATTED_LMS = 'Last Man Standing';
export const FORMATTED_PVP_ARENA = 'PvP Arena';
export const FORMATTED_SOUL_WARS = 'Soul Wars Zeal';
export const FORMATTED_LEAGUE_POINTS = 'League Points';
export const FORMATTED_RIFTS_CLOSED = 'Rifts Closed';
export const INVALID_FORMAT_ERROR = 'Invalid hiscores format';

View File

@@ -96,10 +96,25 @@ export const rsnFromElement = (el: Element | null) => {
* @param url URL to run a `GET` request against.
* @returns Axios response.
*/
export const httpGet = (url: string) =>
axios.get(url, {
export const httpGet = <Response>(url: string) =>
axios.get<Response>(url, {
headers: {
// without User-Agent header requests may be rejected by DDoS protection mechanism
'User-Agent': ua.firefox(80)
}
});
/**
* Validates that a provided RSN has the same username restrictions as Jagex.
* @param rsn Username to validate.
* @throws Error if the RSN fails validation.
*/
export const validateRSN = (rsn: string) => {
if (typeof rsn !== 'string') {
throw Error('RSN must be a string');
} else if (!/^[a-zA-Z0-9 _-]+$/.test(rsn)) {
throw Error('RSN contains invalid character');
} else if (rsn.length > 12 || rsn.length < 1) {
throw Error('RSN must be between 1 and 12 characters');
}
};

764
yarn.lock

File diff suppressed because it is too large Load Diff