Compare commits

...

153 Commits

Author SHA1 Message Date
maxswa
3b502cb7cf v2.17.0 2024-01-24 11:25:48 -05:00
Max Swartwout
b3f81e66eb Merge pull request #88 from maxswa/scurrius
Add Scurrius
2024-01-24 11:24:34 -05:00
Max Swartwout
01150b6234 Merge pull request #93 from maxswa/update-readme-json-api
Add reference to official JSON API in README
2024-01-19 18:43:47 -05:00
maxswa
ca4d75f2c7 Add reference to official JSON API in README. 2024-01-19 18:37:14 -05:00
Max Swartwout
03b2ef42d2 Merge pull request #91 from maxswa/dependabot/npm_and_yarn/follow-redirects-1.15.4
Bump follow-redirects from 1.15.3 to 1.15.4
2024-01-19 18:18:24 -05:00
dependabot[bot]
7e2603b235 Bump follow-redirects from 1.15.3 to 1.15.4
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.3 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.3...v1.15.4)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-11 04:36:21 +00:00
maxswa
92452e240c Add Scurrius 2023-12-30 23:25:32 -05:00
maxswa
d01b36422e v2.16.3 2023-12-17 20:17:17 -05:00
Max Swartwout
3bc1352838 Merge pull request #90 from maxswa/deadman-points
Add Deadman Points
2023-12-17 20:16:28 -05:00
maxswa
91066b5366 Add deadmanPoints 2023-12-17 20:13:06 -05:00
maxswa
4872d04301 v2.16.2 2023-11-26 19:53:20 -05:00
Max Swartwout
55312ba0fd Merge pull request #85 from seditionist/main
Add Custom Errors
2023-11-26 19:52:07 -05:00
maxswa
331a448503 Disabled eslint rule. 2023-11-26 19:43:35 -05:00
maxswa
84c704a846 Add test for excluded gamemodes. 2023-11-26 19:42:46 -05:00
seditionist
561495632c Fix gamemode checks 2023-11-26 18:55:26 -05:00
seditionist
bdfdf1eb14 Merge branch 'maxswa-main' 2023-11-26 18:23:31 -05:00
seditionist
e349776060 Merge branch 'main' of github.com:maxswa/osrs-json-hiscores into maxswa-main
# Conflicts:
#	src/hiscores.ts
2023-11-26 18:20:03 -05:00
seditionist
546bc5acf7 Remove unnecessary type cast 2023-11-26 17:43:38 -05:00
seditionist
66528cd9d9 Restore error message constants 2023-11-26 17:42:55 -05:00
maxswa
99ea3fb722 v2.16.1 2023-11-25 22:07:55 -05:00
Max Swartwout
7f16d26e3c Merge pull request #89 from maxswa/bump-axios
Bump `axios`
2023-11-25 22:06:07 -05:00
maxswa
ed9fcf47b3 Bump axios 2023-11-25 22:04:48 -05:00
Max Swartwout
bb935bb6c9 Merge pull request #86 from maxswa/dependabot/npm_and_yarn/babel/traverse-7.23.2
Bump @babel/traverse from 7.22.8 to 7.23.2
2023-11-25 21:36:52 -05:00
dependabot[bot]
d046fffa9e Bump @babel/traverse from 7.22.8 to 7.23.2
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.22.8 to 7.23.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-19 11:00:07 +00:00
seditionist
5483e54d78 cleanup 2023-08-17 00:58:37 -04:00
seditionist
97532b0c03 fix tests 2023-08-17 00:35:22 -04:00
seditionist
f9a56e2b3a update hiscores to throw new custom errors 2023-08-17 00:32:23 -04:00
seditionist
9f43f0c39f update validRSN to use InvalidRSNError 2023-08-17 00:12:27 -04:00
seditionist
d67ffa3e20 created custom errors 2023-08-17 00:11:18 -04:00
maxswa
7bdde9f5d1 v2.16.0 2023-08-15 21:26:53 -04:00
Max Swartwout
b4a50e69c8 Merge pull request #83 from maxswa/json-endpoint
Use Official JSON Endpoint
2023-08-15 21:25:57 -04:00
maxswa
7935c5a523 Update name matching to use lowercase values. 2023-08-12 11:19:33 -04:00
maxswa
f1e2155428 Fix tests. 2023-08-12 11:01:06 -04:00
maxswa
dd90fe1260 Update README with new formatted names. 2023-08-11 15:11:38 -04:00
maxswa
12c4e530a2 Update get stats functions to use JSON endpoint. 2023-08-11 14:59:49 -04:00
maxswa
5a2b0d9ad8 Add functions to support JSON endpoint. 2023-08-11 14:52:02 -04:00
maxswa
c43858eac1 v2.15.0 2023-07-26 18:50:54 -04:00
Max Swartwout
0d0081f4f6 Merge pull request #81 from wpdough/main
Add new DT2 bosses
2023-07-26 18:49:03 -04:00
wpdough
3570f0f07d Update README and revert yarn.lock 2023-07-26 14:32:13 -04:00
wpdough
3ddda54e02 Merge branch 'main' of https://github.com/wpdough/osrs-json-hiscores into main 2023-07-26 14:08:46 -04:00
wpdough
388f01522a Add new DT2 bosses 2023-07-26 14:08:38 -04:00
wpdough
24b3de2a29 Add new DT2 bosses 2023-07-26 12:00:23 -04:00
maxswa
fc7897d4c7 v2.14.2 2023-07-15 20:48:08 -04:00
Max Swartwout
72ea31490f Merge pull request #80 from maxswa/update-test-lint-format-libs
Update testing, linting and formatting libraries
2023-07-13 16:40:08 -04:00
maxswa
66825955c1 Update linting and formatting libraries. 2023-07-13 16:36:37 -04:00
maxswa
4b4bf8c702 Update jest, jsdom, typescript 2023-07-13 16:16:07 -04:00
Max Swartwout
53981e7468 Merge pull request #78 from maxswa/dependabot/npm_and_yarn/semver-5.7.2
Bump semver from 5.7.1 to 5.7.2
2023-07-13 16:04:52 -04:00
dependabot[bot]
a90e5b98dd Bump semver from 5.7.1 to 5.7.2
Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-11 11:51:33 +00:00
maxswa
16a975f10b v2.14.1 2023-06-16 15:36:44 -04:00
Max Swartwout
c800501188 Merge pull request #77 from maxswa/axios-config
Add optional axios config argument
2023-06-16 15:36:14 -04:00
maxswa
8a320df015 Update necessary jsdoc comments. 2023-05-28 13:47:21 -04:00
maxswa
169462c446 Add optional config arg to all fns using httpGet 2023-05-28 13:42:37 -04:00
maxswa
317a972314 Add optional axios configs to GetStatsOptions 2023-05-28 13:41:53 -04:00
maxswa
c558f8402c Add optional axios config arg to httpGet 2023-05-28 13:41:02 -04:00
maxswa
41c6da4d5c v2.14.0 2023-05-26 09:40:51 -04:00
Max Swartwout
99ad5eb662 Merge pull request #76 from dmeredith96/fix-invalid-format-err
Add support for the Legacy Bounty Hunter minigame
2023-05-26 09:38:31 -04:00
David Meredith
a8cf1713af Change approach to tracking the new Bounty Hunter stats 2023-05-25 09:37:30 -05:00
David Meredith
11391c90f2 Add support for the Legacy Bounty Hunter minigame 2023-05-24 08:48:46 -05:00
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
Max Swartwout
3a72315498 Merge pull request #49 from maxswa/dependabot/npm_and_yarn/follow-redirects-1.14.8
Bump follow-redirects from 1.14.7 to 1.14.8
2022-02-15 14:37:28 -05:00
dependabot[bot]
c1c9833ad0 Bump follow-redirects from 1.14.7 to 1.14.8
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.7 to 1.14.8.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.7...v1.14.8)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-12 12:01:29 +00:00
Max Swartwout
9fe03746e1 Merge pull request #48 from maxswa/dependabot/npm_and_yarn/follow-redirects-1.14.7
Bump follow-redirects from 1.14.4 to 1.14.7
2022-01-18 10:42:22 -05:00
dependabot[bot]
66a4fb47a6 Bump follow-redirects from 1.14.4 to 1.14.7
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.4 to 1.14.7.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.4...v1.14.7)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-13 14:22:21 +00:00
Max Swartwout
83c44148a7 Merge pull request #47 from maxswa/fix-tests-nex
Fix tests
2022-01-05 21:54:48 -05:00
maxswa
44dea6b35c Add line for nex in lynxTitanStats.csv 2022-01-05 21:50:28 -05:00
maxswa
0bf491638c v2.7.0 2022-01-05 10:04:11 -05:00
Max Swartwout
febca534b0 Merge pull request #45 from maxswa/add-nex
Add Nex.
2022-01-05 09:57:56 -05:00
Max Swartwout
8daee5c39b Merge pull request #46 from molo-pl/main
Throw error for unknown hiscores CSV format
2022-01-05 09:52:42 -05:00
molo-pl
f98cf8aaaa Throw error for unknown hiscores CSV format 2022-01-04 23:05:34 +01:00
maxswa
5d9f6a6bac Add Nex. 2021-12-19 21:40:09 -05:00
Max Swartwout
8737b50a82 Merge pull request #44 from maxswa/dependabot/npm_and_yarn/axios-0.21.2
Bump axios from 0.21.1 to 0.21.2
2021-09-27 16:38:12 -04:00
dependabot[bot]
1255e7e3d2 Bump axios from 0.21.1 to 0.21.2
Bumps [axios](https://github.com/axios/axios) from 0.21.1 to 0.21.2.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/master/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.21.1...v0.21.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-27 20:36:33 +00:00
Max Swartwout
f4135d0b0f Merge pull request #42 from maxswa/dependabot/npm_and_yarn/path-parse-1.0.7
Bump path-parse from 1.0.6 to 1.0.7
2021-09-27 16:36:09 -04:00
Max Swartwout
6135ff459c Merge pull request #43 from maxswa/dependabot/npm_and_yarn/tmpl-1.0.5
Bump tmpl from 1.0.4 to 1.0.5
2021-09-27 16:36:00 -04:00
dependabot[bot]
9fb7e4cebc Bump tmpl from 1.0.4 to 1.0.5
Bumps [tmpl](https://github.com/daaku/nodejs-tmpl) from 1.0.4 to 1.0.5.
- [Release notes](https://github.com/daaku/nodejs-tmpl/releases)
- [Commits](https://github.com/daaku/nodejs-tmpl/commits/v1.0.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-21 04:35:18 +00:00
dependabot[bot]
11350b2444 Bump path-parse from 1.0.6 to 1.0.7
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-11 02:52:55 +00:00
maxswa
e0d6c5b20f v2.6.0 2021-07-02 10:53:19 -04:00
Max Swartwout
c8a4440ac3 Merge pull request #41 from Not-Jayden/phosanis-nightmare
New Boss: Phosani's Nightmare
2021-07-02 10:49:19 -04:00
Not-Jayden
f3781cf142 add to readme 2021-07-02 18:09:10 +08:00
Not-Jayden
5fbc04217b Merge branch 'phosanis-nightmare' of https://github.com/Not-Jayden/osrs-json-hiscores into phosanis-nightmare 2021-07-02 13:20:45 +08:00
Not-Jayden
eb5d0ba49f add to lynxTitanStats.csv 2021-07-02 13:20:33 +08:00
Not-Jayden
c68e3bebea Update __tests__/hiscores.test.ts 2021-07-02 13:13:07 +08:00
Not-Jayden
aa6fd6f39a add phosani's nightmare to api 2021-07-01 20:14:31 +08:00
maxswa
cc9aae8ced v2.5.2 2021-06-11 14:46:04 -04:00
Max Swartwout
12882b4e34 Merge pull request #37 from Not-Jayden/revert-seasonal-changes
[Bug] Remove conditional `seasonal` gameMode logic for bosses
2021-06-11 14:44:07 -04:00
Max Swartwout
b17a8b9299 Merge pull request #39 from maxswa/dependabot/npm_and_yarn/hosted-git-info-2.8.9
Bump hosted-git-info from 2.8.8 to 2.8.9
2021-06-11 14:42:54 -04:00
dependabot[bot]
50f2c19257 Bump hosted-git-info from 2.8.8 to 2.8.9
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

---
updated-dependencies:
- dependency-name: hosted-git-info
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-11 18:40:53 +00:00
Max Swartwout
d892f8e67b Merge pull request #38 from maxswa/dependabot/npm_and_yarn/normalize-url-4.5.1
Bump normalize-url from 4.5.0 to 4.5.1
2021-06-11 14:39:31 -04:00
dependabot[bot]
af5664ae6e Bump normalize-url from 4.5.0 to 4.5.1
Bumps [normalize-url](https://github.com/sindresorhus/normalize-url) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/sindresorhus/normalize-url/releases)
- [Commits](https://github.com/sindresorhus/normalize-url/commits)

---
updated-dependencies:
- dependency-name: normalize-url
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-09 17:37:06 +00:00
NotJayden
20c5c2f730 delete seasonal changes 2021-06-10 00:15:11 +08:00
Max Swartwout
04e59cff04 Merge pull request #36 from maxswa/dependabot/npm_and_yarn/trim-newlines-3.0.1
Bump trim-newlines from 3.0.0 to 3.0.1
2021-06-08 17:14:27 -04:00
dependabot[bot]
ad2249ba76 Bump trim-newlines from 3.0.0 to 3.0.1
Bumps [trim-newlines](https://github.com/sindresorhus/trim-newlines) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/sindresorhus/trim-newlines/releases)
- [Commits](https://github.com/sindresorhus/trim-newlines/commits)

---
updated-dependencies:
- dependency-name: trim-newlines
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-08 21:11:36 +00:00
maxswa
5c52bfd883 v2.5.1 2021-06-08 17:10:31 -04:00
Max Swartwout
947d994cdf Merge pull request #35 from Not-Jayden/fix-seasonal-bosses-results
[Bug fix] Add conditional bosses filter if gamemode is seasonal
2021-06-08 17:08:44 -04:00
NotJayden
130446ab74 use boss generic 2021-06-08 16:36:36 +08:00
NotJayden
f96e7e3e2d check bossKeys strict equals the BOSSES array, and check seasonal bossKeys doesn't include tob hard mode 2021-06-08 16:29:43 +08:00
NotJayden
90d66b6b2c prettier 2021-06-08 16:16:00 +08:00
NotJayden
14cedb5877 add fysadStatsSeasonal.csv, update lynxTitanStats.csv, and write tests to make sure the keys for bosses match the expectation for the given gamemode 2021-06-08 16:15:36 +08:00
NotJayden
4703812b52 add comment 2021-06-08 15:49:02 +08:00
NotJayden
a11ac96f68 prettier 2021-06-07 14:53:36 +08:00
NotJayden
f97caa6a15 undo lib change 2021-06-07 14:51:15 +08:00
NotJayden
e7e54741d2 add conditional bosses filter if gamemode is seasonal 2021-06-07 14:50:21 +08:00
maxswa
062e5e02b2 v2.5.0 2021-06-03 22:26:38 -04:00
Max Swartwout
ed7036f9c0 Merge pull request #33 from maxswa/add-tob-hard-mode
Add TOB hard mode.
2021-06-03 22:25:24 -04:00
18 changed files with 7204 additions and 6166 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

1
.gitignore vendored
View File

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

View File

@@ -3,24 +3,26 @@
[![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!**
**The Old School RuneScape API wrapper that does more!**
## What it does
The official hiscores API for Old School Runescape (OSRS) returns CSV.
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).
The official hiscores API for Old School RuneScape (OSRS) can return CSV or a simple JSON array.
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.
---
### 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:
```javascript
const hiscores = require('osrs-json-hiscores');
```typescript
import { getStatsByGamemode, getSkillPage } from 'osrs-json-hiscores';
```
Once you import it you can call the functions asynchronously:
```javascript
hiscores
.getStats('Lynx Titan')
.then((res) => console.log(res))
.catch((err) => console.error(err));
```
If you are using TypeScript or transpiling your JS you can use ES6 syntax:
```javascript
import hiscores, { getSkillPage } from 'osrs-json-hiscores';
// ...
const stats = await hiscores.getStats('Lynx Titan');
```typescript
const stats = await getStatsByGamemode('Lynx Titan');
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:
```javascript
hiscores
.getSkillPage('attack', 'main', 1)
```typescript
getSkillPage('attack', 'main', 1)
.then((res) => console.log(res))
.catch((err) => console.error(err));
```
@@ -105,17 +93,22 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses
### Minigames
| Minigame | Param |
| ---------------------- | :---------------: |
| Bounty Hunter (Rogue) | `rogueBH` |
| Bounty Hunter (Hunter) | `hunterBH` |
| Last Man Standing | `lastManStanding` |
| ------------------------------- | :---------------: |
| Bounty Hunter (Legacy - Rogue) | `rogueBH` |
| Bounty Hunter (Legacy - Hunter) | `hunterBH` |
| Bounty Hunter (Rogue) | `rogueBHV2` |
| Bounty Hunter (Hunter) | `hunterBHV2` |
| LMS - Rank | `lastManStanding` |
| PvP Arena - Rank | `pvpArena` |
| Soul Wars Zeal | `soulWarsZeal` |
| Rifts closed | `riftsClosed` |
### Leagues
### Points
| Activity | Param |
| ------------- | :------------: |
| -------------- | :-------------: |
| League Points | `leaguePoints` |
| Deadman Points | `deadmanPoints` |
### Bosses
@@ -123,9 +116,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` |
@@ -138,6 +133,7 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses
| Dagannoth Rex | `dagannothRex` |
| Dagannoth Supreme | `dagannothSupreme` |
| Deranged Archaeologist | `derangedArchaeologist` |
| Duke Sucellus | `dukeSucellus` |
| General Graardor | `generalGraardor` |
| Giant Mole | `giantMole` |
| Grotesque Guardians | `grotesqueGuardians` |
@@ -148,19 +144,29 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses
| Kreearra | `kreeArra` |
| K'ril Tsutsaroth | `krilTsutsaroth` |
| Mimic | `mimic` |
| The Nightmare of Ashihama | `nightmare` |
| Nex | `nex` |
| Nightmare | `nightmare` |
| Phosani's Nightmare | `phosanisNightmare` |
| Obor | `obor` |
| Phantom Muspah | `phantomMuspah` |
| Sarachnis | `sarachnis` |
| Scorpia | `scorpia` |
| Scurrius | `scurrius` |
| Skotizo | `skotizo` |
| Spindel | `spindel` |
| Tempoross | `tempoross` |
| The Gauntlet | `gauntlet` |
| The Corrupted Gauntlet | `corruptedGauntlet` |
| The Leviathan | `leviathan` |
| The Whisperer | `whisperer` |
| 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` |
| Vardorvis | `vardorvis` |
| Venenatis | `venenatis` |
| Vetion | `vetion` |
| Vorkath | `vorkath` |
@@ -172,7 +178,7 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses
`getStats` returns a player object that looks like this:
```javascript
```typescript
{
name: 'Lynx Titan',
mode: 'main',
@@ -190,7 +196,9 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses
leaguePoints: {},
bountyHunter: {},
lastManStanding: {},
pvpArena: {},
soulWarsZeal: {},
riftsClosed: {},
bosses: {}
}
}
@@ -198,7 +206,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):
```javascript
```typescript
[
{ rank: 1, name: 'Lynx Titan', level: 2277, xp: 4600000000, dead: false },
{},
@@ -211,7 +219,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:
```javascript
```typescript
// kril === "K'ril Tsutsaroth"
const kril = FORMATTED_BOSS_NAMES['krilTsutsaroth'];
```

View File

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

File diff suppressed because it is too large Load Diff

100
__tests__/b0atyStats.csv Normal file
View File

@@ -0,0 +1,100 @@
3745,2277,840426611
24080,99,30276246
9559,99,31228199
2212,99,143599407
3550,99,123248914
6564,99,109953707
4244,99,15144846
4107,99,57072224
135556,99,13353511
25855,99,16903268
53472,99,13456850
22453,99,16654917
17690,99,19118307
45816,99,14059776
10409,99,16530015
27702,99,14765145
39430,99,13235063
6722,99,15754877
51813,99,14023027
5324,99,33835543
3154,99,69527777
4719,99,22733384
15135,99,17952893
1089,99,17998715
-1,-1
-1,-1
26164,10
12886,3
148658,8
103161,7
377,9556
862,825
148,6000
2153,1606
42107,298
504,459
1816,368
14701,2521
-1,-1
6776,14780
4773,1060
2741,2192
90203,594
29573,143
3668,1830
206778,5
32897,293
64112,58
18492,1905
178941,34
2379,501
82902,50
147358,26
51973,287
2377,2000
147406,41
32225,707
42229,698
32212,714
79605,25
9517,688
19125,1228
65849,474
2080,2140
16433,182
2006,2046
48575,580
115056,1908
10406,920
2196,1444
1005,33
28413,620
4157,744
4261,500
993,217
21848,251
66260,119
108898,26
23958,90
373975,9
2005,1569
40001,273
192608,7
78367,201
5679,583
478,1760
22932,250
15059,7
7161,3066
101555,35
610,933
15733,2
-1,-1
2051,2078
194,7096
69260,51
14039,2780
876367,62
165341,25
226318,275
Can't render this file because it has a wrong number of fields in line 25.

202
__tests__/b0atyStats.json Normal file
View File

@@ -0,0 +1,202 @@
{
"skills": [
{
"id": 0,
"name": "Overall",
"rank": 3745,
"level": 2277,
"xp": 840426611
},
{ "id": 1, "name": "Attack", "rank": 24080, "level": 99, "xp": 30276246 },
{ "id": 2, "name": "Defence", "rank": 9559, "level": 99, "xp": 31228199 },
{ "id": 3, "name": "Strength", "rank": 2212, "level": 99, "xp": 143599407 },
{
"id": 4,
"name": "Hitpoints",
"rank": 3550,
"level": 99,
"xp": 123248914
},
{ "id": 5, "name": "Ranged", "rank": 6564, "level": 99, "xp": 109953707 },
{ "id": 6, "name": "Prayer", "rank": 4244, "level": 99, "xp": 15144846 },
{ "id": 7, "name": "Magic", "rank": 4107, "level": 99, "xp": 57072224 },
{ "id": 8, "name": "Cooking", "rank": 135556, "level": 99, "xp": 13353511 },
{
"id": 9,
"name": "Woodcutting",
"rank": 25855,
"level": 99,
"xp": 16903268
},
{
"id": 10,
"name": "Fletching",
"rank": 53472,
"level": 99,
"xp": 13456850
},
{ "id": 11, "name": "Fishing", "rank": 22453, "level": 99, "xp": 16654917 },
{
"id": 12,
"name": "Firemaking",
"rank": 17690,
"level": 99,
"xp": 19118307
},
{
"id": 13,
"name": "Crafting",
"rank": 45816,
"level": 99,
"xp": 14059776
},
{
"id": 14,
"name": "Smithing",
"rank": 10409,
"level": 99,
"xp": 16530015
},
{ "id": 15, "name": "Mining", "rank": 27702, "level": 99, "xp": 14765145 },
{
"id": 16,
"name": "Herblore",
"rank": 39430,
"level": 99,
"xp": 13235063
},
{ "id": 17, "name": "Agility", "rank": 6722, "level": 99, "xp": 15754877 },
{
"id": 18,
"name": "Thieving",
"rank": 51813,
"level": 99,
"xp": 14023027
},
{ "id": 19, "name": "Slayer", "rank": 5324, "level": 99, "xp": 33835543 },
{ "id": 20, "name": "Farming", "rank": 3154, "level": 99, "xp": 69527777 },
{
"id": 21,
"name": "Runecraft",
"rank": 4719,
"level": 99,
"xp": 22733384
},
{ "id": 22, "name": "Hunter", "rank": 15135, "level": 99, "xp": 17952893 },
{
"id": 23,
"name": "Construction",
"rank": 1089,
"level": 99,
"xp": 17998715
}
],
"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": 26164, "score": 10 },
{ "id": 3, "name": "Bounty Hunter - Rogue", "rank": 12886, "score": 3 },
{
"id": 4,
"name": "Bounty Hunter (Legacy) - Hunter",
"rank": 148658,
"score": 8
},
{
"id": 5,
"name": "Bounty Hunter (Legacy) - Rogue",
"rank": 103161,
"score": 7
},
{ "id": 6, "name": "Clue Scrolls (all)", "rank": 377, "score": 9556 },
{ "id": 7, "name": "Clue Scrolls (beginner)", "rank": 862, "score": 825 },
{ "id": 8, "name": "Clue Scrolls (easy)", "rank": 148, "score": 6000 },
{ "id": 9, "name": "Clue Scrolls (medium)", "rank": 2153, "score": 1606 },
{ "id": 10, "name": "Clue Scrolls (hard)", "rank": 42107, "score": 298 },
{ "id": 11, "name": "Clue Scrolls (elite)", "rank": 504, "score": 459 },
{ "id": 12, "name": "Clue Scrolls (master)", "rank": 1816, "score": 368 },
{ "id": 13, "name": "LMS - Rank", "rank": 14701, "score": 2521 },
{ "id": 14, "name": "PvP Arena - Rank", "rank": -1, "score": -1 },
{ "id": 15, "name": "Soul Wars Zeal", "rank": 6776, "score": 14780 },
{ "id": 16, "name": "Rifts closed", "rank": 4773, "score": 1060 },
{ "id": 17, "name": "Abyssal Sire", "rank": 2741, "score": 2192 },
{ "id": 18, "name": "Alchemical Hydra", "rank": 90203, "score": 594 },
{ "id": 19, "name": "Artio", "rank": 29573, "score": 143 },
{ "id": 20, "name": "Barrows Chests", "rank": 3668, "score": 1830 },
{ "id": 21, "name": "Bryophyta", "rank": 206778, "score": 5 },
{ "id": 22, "name": "Callisto", "rank": 32897, "score": 293 },
{ "id": 23, "name": "Calvar'ion", "rank": 64112, "score": 58 },
{ "id": 24, "name": "Cerberus", "rank": 18492, "score": 1905 },
{ "id": 25, "name": "Chambers of Xeric", "rank": 178941, "score": 34 },
{
"id": 26,
"name": "Chambers of Xeric: Challenge Mode",
"rank": 2379,
"score": 501
},
{ "id": 27, "name": "Chaos Elemental", "rank": 82902, "score": 50 },
{ "id": 28, "name": "Chaos Fanatic", "rank": 147358, "score": 26 },
{ "id": 29, "name": "Commander Zilyana", "rank": 51973, "score": 287 },
{ "id": 30, "name": "Corporeal Beast", "rank": 2377, "score": 2000 },
{ "id": 31, "name": "Crazy Archaeologist", "rank": 147406, "score": 41 },
{ "id": 32, "name": "Dagannoth Prime", "rank": 32225, "score": 707 },
{ "id": 33, "name": "Dagannoth Rex", "rank": 42229, "score": 698 },
{ "id": 34, "name": "Dagannoth Supreme", "rank": 32212, "score": 714 },
{ "id": 35, "name": "Deranged Archaeologist", "rank": 79605, "score": 25 },
{ "id": 36, "name": "Duke Sucellus", "rank": 9517, "score": 688 },
{ "id": 37, "name": "General Graardor", "rank": 19125, "score": 1228 },
{ "id": 38, "name": "Giant Mole", "rank": 65849, "score": 474 },
{ "id": 39, "name": "Grotesque Guardians", "rank": 2080, "score": 2140 },
{ "id": 40, "name": "Hespori", "rank": 16433, "score": 182 },
{ "id": 41, "name": "Kalphite Queen", "rank": 2006, "score": 2046 },
{ "id": 42, "name": "King Black Dragon", "rank": 48575, "score": 580 },
{ "id": 43, "name": "Kraken", "rank": 115056, "score": 1908 },
{ "id": 44, "name": "Kree'Arra", "rank": 10406, "score": 920 },
{ "id": 45, "name": "K'ril Tsutsaroth", "rank": 2196, "score": 1444 },
{ "id": 46, "name": "Mimic", "rank": 1005, "score": 33 },
{ "id": 47, "name": "Nex", "rank": 28413, "score": 620 },
{ "id": 48, "name": "Nightmare", "rank": 4157, "score": 744 },
{ "id": 49, "name": "Phosani's Nightmare", "rank": 4261, "score": 500 },
{ "id": 50, "name": "Obor", "rank": 993, "score": 217 },
{ "id": 51, "name": "Phantom Muspah", "rank": 21848, "score": 251 },
{ "id": 52, "name": "Sarachnis", "rank": 66260, "score": 119 },
{ "id": 53, "name": "Scorpia", "rank": 108898, "score": 26 },
{ "id": 54, "name": "Scurrius", "rank": 23958, "score": 90 },
{ "id": 55, "name": "Skotizo", "rank": 373975, "score": 9 },
{ "id": 56, "name": "Spindel", "rank": 2005, "score": 1569 },
{ "id": 57, "name": "Tempoross", "rank": 40001, "score": 273 },
{ "id": 58, "name": "The Gauntlet", "rank": 192608, "score": 7 },
{ "id": 59, "name": "The Corrupted Gauntlet", "rank": 78367, "score": 201 },
{ "id": 60, "name": "The Leviathan", "rank": 5679, "score": 583 },
{ "id": 61, "name": "The Whisperer", "rank": 478, "score": 1760 },
{ "id": 62, "name": "Theatre of Blood", "rank": 22932, "score": 250 },
{
"id": 63,
"name": "Theatre of Blood: Hard Mode",
"rank": 15059,
"score": 7
},
{
"id": 64,
"name": "Thermonuclear Smoke Devil",
"rank": 7161,
"score": 3066
},
{ "id": 65, "name": "Tombs of Amascut", "rank": 101555, "score": 35 },
{
"id": 66,
"name": "Tombs of Amascut: Expert Mode",
"rank": 610,
"score": 933
},
{ "id": 67, "name": "TzKal-Zuk", "rank": 15733, "score": 2 },
{ "id": 68, "name": "TzTok-Jad", "rank": -1, "score": -1 },
{ "id": 69, "name": "Vardorvis", "rank": 2051, "score": 2078 },
{ "id": 70, "name": "Venenatis", "rank": 194, "score": 7096 },
{ "id": 71, "name": "Vet'ion", "rank": 69260, "score": 51 },
{ "id": 72, "name": "Vorkath", "rank": 14039, "score": 2780 },
{ "id": 73, "name": "Wintertodt", "rank": 876367, "score": 62 },
{ "id": 74, "name": "Zalcano", "rank": 165341, "score": 25 },
{ "id": 75, "name": "Zulrah", "rank": 226318, "score": 275 }
]
}

View File

@@ -10,7 +10,12 @@ import {
Stats,
getPlayerTableURL,
getSkillPageURL,
getStatsURL
getStatsURL,
BOSSES,
InvalidFormatError,
BH_MODES,
parseJsonStats,
HiscoresResponse
} from '../src/index';
const B0ATY_NAME = 'B0ATY';
@@ -22,7 +27,13 @@ const LYNX_TITAN_FORMATTED_NAME = 'Lynx Titan';
const attackTopPage = readFileSync(`${__dirname}/attackTopPage.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(
`${__dirname}/lynxTitanNamePage.html`,
'utf8'
@@ -43,7 +54,7 @@ jest.spyOn(axios, 'get').mockImplementation((url) => {
if (getSkillPageURL('main', 'attack', 1) === url) {
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 });
}
throw new Error(`No mock response for URL: ${url}`);
@@ -75,6 +86,9 @@ test('Parse CSV to json', () => {
810,99,37688883
92,99,32005622
23423,478
89554,301
89914,35
99834,25
99831,23
89912,37
32,12148
@@ -85,12 +99,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
@@ -103,6 +121,7 @@ test('Parse CSV to json', () => {
4342,1655
966,2951
10151,1
1289,2477
1288,2407
377,4669
545,1567
@@ -113,19 +132,29 @@ test('Parse CSV to json', () => {
625,2391
120,2981
1,109
99,39002
3,22666
2,84
26,323
44,6233
201,1101
82,3404
555,7079
5085,61
678,903
18823,23
63,375
2870,6
2871,7
2872,8
6984,138
23,923141
4043,2000
4073,1020
289,13070
489,8
967,47
968,48
11155,223
1940,272
8623,1340
@@ -161,12 +190,17 @@ test('Parse CSV to json', () => {
construction: { rank: 92, level: 99, xp: 32005622 }
},
leaguePoints: { rank: 23423, score: 478 },
deadmanPoints: { rank: 89554, score: 301 },
bountyHunter: {
hunterV2: { rank: 89914, score: 35 },
rogueV2: { rank: 99834, score: 25 },
hunter: { rank: 99831, score: 23 },
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 },
@@ -179,9 +213,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 },
@@ -194,6 +230,7 @@ test('Parse CSV to json', () => {
dagannothRex: { rank: 4342, score: 1655 },
dagannothSupreme: { rank: 966, score: 2951 },
derangedArchaeologist: { rank: 10151, score: 1 },
dukeSucellus: { rank: 1289, score: 2477 },
generalGraardor: { rank: 1288, score: 2407 },
giantMole: { rank: 377, score: 4669 },
grotesqueGuardians: { rank: 545, score: 1567 },
@@ -204,19 +241,29 @@ test('Parse CSV to json', () => {
kreeArra: { rank: 625, score: 2391 },
krilTsutsaroth: { rank: 120, score: 2981 },
mimic: { rank: 1, score: 109 },
nex: { rank: 99, score: 39002 },
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 },
scurrius: { rank: 555, score: 7079 },
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 },
leviathan: { rank: 2871, score: 7 },
whisperer: { rank: 2872, score: 8 },
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 },
vardorvis: { rank: 968, score: 48 },
venenatis: { rank: 11155, score: 223 },
vetion: { rank: 1940, score: 272 },
vorkath: { rank: 8623, score: 1340 },
@@ -229,6 +276,18 @@ test('Parse CSV to json', () => {
expect(parseStats(csv)).toStrictEqual(expectedOutput);
});
test('Parse CSV with unknown activity', () => {
const statsWithUnknownActivity = `${lynxTitanStats}
-1,-1`;
expect(() => parseStats(statsWithUnknownActivity)).toThrow(
InvalidFormatError
);
});
test('Parse invalid CSV', () => {
expect(() => parseStats('invalid')).toThrow(InvalidFormatError);
});
describe('Get name format', () => {
it('gets a name with a space', async () => {
const data = await getRSNFormat(LYNX_TITAN_SPACE_NAME);
@@ -441,7 +500,10 @@ test('Get non-existent player', async () => {
});
test('Get stats by gamemode', async () => {
const { skills } = await getStatsByGamemode(LYNX_TITAN_FORMATTED_NAME);
const { skills, bosses, bountyHunter } = await getStatsByGamemode(
LYNX_TITAN_FORMATTED_NAME
);
expect(skills).toMatchObject({
overall: { rank: expect.any(Number), level: 2277, xp: 4600000000 },
attack: { rank: expect.any(Number), level: 99, xp: 200000000 },
@@ -468,4 +530,70 @@ test('Get stats by gamemode', async () => {
hunter: { rank: expect.any(Number), level: 99, xp: 200000000 },
construction: { rank: expect.any(Number), level: 99, xp: 200000000 }
});
const bossKeys = Object.keys(bosses);
expect(bossKeys).toStrictEqual(BOSSES);
const bountyHunterKeys = Object.keys(bountyHunter);
expect(bountyHunterKeys).toStrictEqual(BH_MODES);
expect.assertions(3);
});
describe('Get stats options', () => {
const rsn = 'player';
let axiosMock: jest.Mock;
beforeEach(() => {
axios.get = jest.fn(
(url) =>
new Promise<any>((resolve) => {
resolve(
url === getPlayerTableURL('main', rsn)
? { data: lynxTitanNamePage }
: { status: 200, data: lynxTitanStats }
);
})
);
axiosMock = axios.get as any;
axiosMock.mockClear();
});
it('fetches all gamemodes and formatted RSN when no options provided', async () => {
await getStats(rsn);
expect(axiosMock.mock.calls.map((val) => val[0])).toEqual([
getStatsURL('main', rsn, true),
getPlayerTableURL('main', rsn),
getStatsURL('ironman', rsn, true),
getStatsURL('hardcore', rsn, true),
getStatsURL('ultimate', rsn, true)
]);
});
it('skips fetching formatted RSN when option is provided', async () => {
await getStats(rsn, { shouldGetFormattedRsn: false });
expect(
axiosMock.mock.calls.some(
(val) => val[0] === getPlayerTableURL('main', rsn)
)
).toBeFalsy();
});
it('skips fetching game mode when option is provided', async () => {
await getStats(rsn, {
otherGamemodes: ['ironman', 'ultimate']
});
expect(
axiosMock.mock.calls.some(
(val) => val[0] === getStatsURL('hardcore', rsn)
)
).toBeFalsy();
});
it('omits excluded gamemodes', async () => {
const response = await getStats(rsn, {
otherGamemodes: ['ironman', 'ultimate']
});
expect(response.hardcore).toBeUndefined();
});
});
test('CSV and JSON parsing outputs identical object', async () => {
const csvOutput = parseStats(b0atyStatsCsv);
const jsonOutput = parseJsonStats(b0atyStatsJson);
expect(csvOutput).toEqual(jsonOutput);
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,81 +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
24,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
344430,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
Can't render this file because it has a wrong number of fields in line 25.

View File

@@ -0,0 +1,148 @@
{
"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": 31, "level": 99, "xp": 200000000 },
{ "id": 8, "name": "Cooking", "rank": 155, "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": 805212, "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": 502504, "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": "Abyssal Sire", "rank": -1, "score": -1 },
{ "id": 18, "name": "Alchemical Hydra", "rank": -1, "score": -1 },
{ "id": 19, "name": "Artio", "rank": -1, "score": -1 },
{ "id": 20, "name": "Barrows Chests", "rank": -1, "score": -1 },
{ "id": 21, "name": "Bryophyta", "rank": -1, "score": -1 },
{ "id": 22, "name": "Callisto", "rank": -1, "score": -1 },
{ "id": 23, "name": "Calvar'ion", "rank": -1, "score": -1 },
{ "id": 24, "name": "Cerberus", "rank": -1, "score": -1 },
{ "id": 25, "name": "Chambers of Xeric", "rank": -1, "score": -1 },
{
"id": 26,
"name": "Chambers of Xeric: Challenge Mode",
"rank": -1,
"score": -1
},
{ "id": 27, "name": "Chaos Elemental", "rank": -1, "score": -1 },
{ "id": 28, "name": "Chaos Fanatic", "rank": -1, "score": -1 },
{ "id": 29, "name": "Commander Zilyana", "rank": -1, "score": -1 },
{ "id": 30, "name": "Corporeal Beast", "rank": -1, "score": -1 },
{ "id": 31, "name": "Crazy Archaeologist", "rank": -1, "score": -1 },
{ "id": 32, "name": "Dagannoth Prime", "rank": -1, "score": -1 },
{ "id": 33, "name": "Dagannoth Rex", "rank": -1, "score": -1 },
{ "id": 34, "name": "Dagannoth Supreme", "rank": -1, "score": -1 },
{ "id": 35, "name": "Deranged Archaeologist", "rank": -1, "score": -1 },
{ "id": 36, "name": "Duke Sucellus", "rank": -1, "score": -1 },
{ "id": 37, "name": "General Graardor", "rank": -1, "score": -1 },
{ "id": 38, "name": "Giant Mole", "rank": -1, "score": -1 },
{ "id": 39, "name": "Grotesque Guardians", "rank": -1, "score": -1 },
{ "id": 40, "name": "Hespori", "rank": -1, "score": -1 },
{ "id": 41, "name": "Kalphite Queen", "rank": -1, "score": -1 },
{ "id": 42, "name": "King Black Dragon", "rank": -1, "score": -1 },
{ "id": 43, "name": "Kraken", "rank": -1, "score": -1 },
{ "id": 44, "name": "Kree'Arra", "rank": -1, "score": -1 },
{ "id": 45, "name": "K'ril Tsutsaroth", "rank": -1, "score": -1 },
{ "id": 46, "name": "Mimic", "rank": -1, "score": -1 },
{ "id": 47, "name": "Nex", "rank": -1, "score": -1 },
{ "id": 48, "name": "Nightmare", "rank": -1, "score": -1 },
{ "id": 49, "name": "Phosani's Nightmare", "rank": -1, "score": -1 },
{ "id": 50, "name": "Obor", "rank": -1, "score": -1 },
{ "id": 51, "name": "Phantom Muspah", "rank": -1, "score": -1 },
{ "id": 52, "name": "Sarachnis", "rank": -1, "score": -1 },
{ "id": 53, "name": "Scorpia", "rank": -1, "score": -1 },
{ "id": 54, "name": "Skotizo", "rank": -1, "score": -1 },
{ "id": 55, "name": "Spindel", "rank": -1, "score": -1 },
{ "id": 56, "name": "Tempoross", "rank": -1, "score": -1 },
{ "id": 57, "name": "The Gauntlet", "rank": -1, "score": -1 },
{ "id": 58, "name": "The Corrupted Gauntlet", "rank": -1, "score": -1 },
{ "id": 59, "name": "The Leviathan", "rank": -1, "score": -1 },
{ "id": 60, "name": "The Whisperer", "rank": -1, "score": -1 },
{ "id": 61, "name": "Theatre of Blood", "rank": -1, "score": -1 },
{
"id": 62,
"name": "Theatre of Blood: Hard Mode",
"rank": -1,
"score": -1
},
{ "id": 63, "name": "Thermonuclear Smoke Devil", "rank": -1, "score": -1 },
{ "id": 64, "name": "Tombs of Amascut", "rank": -1, "score": -1 },
{
"id": 65,
"name": "Tombs of Amascut: Expert Mode",
"rank": -1,
"score": -1
},
{ "id": 66, "name": "TzKal-Zuk", "rank": -1, "score": -1 },
{ "id": 67, "name": "TzTok-Jad", "rank": 225, "score": 186 },
{ "id": 68, "name": "Vardorvis", "rank": -1, "score": -1 },
{ "id": 69, "name": "Venenatis", "rank": -1, "score": -1 },
{ "id": 70, "name": "Vet'ion", "rank": -1, "score": -1 },
{ "id": 71, "name": "Vorkath", "rank": -1, "score": -1 },
{ "id": 72, "name": "Wintertodt", "rank": -1, "score": -1 },
{ "id": 73, "name": "Zalcano", "rank": -1, "score": -1 },
{ "id": 74, "name": "Zulrah", "rank": -1, "score": -1 }
]
}

View File

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

View File

@@ -1,4 +1,5 @@
import { JSDOM } from 'jsdom';
import axios, { AxiosRequestConfig } from 'axios';
import { BinaryData, JSDOM } from 'jsdom';
import {
Player,
Activity,
@@ -12,7 +13,9 @@ import {
PlayerSkillRow,
ActivityName,
PlayerActivityRow,
Bosses
Bosses,
GetStatsOptions,
HiscoresResponse
} from './types';
import {
getStatsURL,
@@ -27,41 +30,155 @@ import {
rsnFromElement,
getActivityPageURL,
httpGet,
BOSSES
BOSSES,
InvalidFormatError,
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
} 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.
*
* @param rsn Username of the player.
* @param config Optional axios request config object.
* @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');
}
export async function getRSNFormat(
rsn: string,
config?: AxiosRequestConfig
): Promise<string> {
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,
config
);
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');
throw new PlayerNotFoundError();
} 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 stats: Stats = {
skills,
leaguePoints,
deadmanPoints,
bountyHunter,
lastManStanding,
pvpArena,
soulWarsZeal,
riftsClosed,
clues,
bosses
};
return stats;
}
/**
* Parses CSV string of raw stats and returns a stats object.
*
@@ -74,6 +191,10 @@ export function parseStats(csv: string): Stats {
.filter((entry) => !!entry)
.map((stat) => stat.split(','));
if (splitCSV.length !== SKILLS.length + ACTIVITIES.length) {
throw new InvalidFormatError();
}
const skillObjects: Skill[] = splitCSV
.filter((stat) => stat.length === 3)
.map((stat) => {
@@ -97,10 +218,11 @@ export function parseStats(csv: string): Stats {
return activity;
});
const [leaguePoints] = activityObjects.splice(0, 1);
const [leaguePoints, deadmanPoints] = activityObjects.splice(0, 2);
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) => {
@@ -130,9 +252,12 @@ export function parseStats(csv: string): Stats {
const stats: Stats = {
skills,
leaguePoints,
deadmanPoints,
bountyHunter,
lastManStanding,
pvpArena,
soulWarsZeal,
riftsClosed,
clues,
bosses
};
@@ -149,25 +274,31 @@ 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));
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 main = await getOfficialStats(rsn, 'main', options?.axiosConfigs?.main);
const [ironRes, hcRes, ultRes, formattedName] = otherResponses;
const getModeStats = async (
mode: Extract<Gamemode, 'ironman' | 'hardcore' | 'ultimate'>
): Promise<HiscoresResponse | undefined> =>
otherGamemodes.includes(mode)
? getOfficialStats(rsn, mode, options?.axiosConfigs?.[mode]).catch(
() => undefined
)
: undefined;
const formattedName = shouldGetFormattedRsn
? await getRSNFormat(rsn, options?.axiosConfigs?.rsn).catch(() => undefined)
: undefined;
const player: Player = {
name: formattedName ?? rsn,
@@ -176,45 +307,42 @@ export async function getStats(rsn: string): Promise<Player> {
deulted: false,
deironed: false
};
player.main = parseStats(mainRes.data);
player.main = parseJsonStats(main);
if (ironRes.status === 200) {
player.ironman = parseStats(ironRes.data);
if (hcRes.status === 200) {
const iron = await getModeStats('ironman');
if (iron) {
player.ironman = parseJsonStats(iron);
const hc = await getModeStats('hardcore');
const ult = await getModeStats('ultimate');
if (hc) {
player.mode = 'hardcore';
player.hardcore = parseStats(hcRes.data);
player.hardcore = parseJsonStats(hc);
if (
player.ironman.skills.overall.xp !== player.hardcore.skills.overall.xp
) {
player.dead = true;
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.mode = 'main';
}
} else if (ultRes.status === 200) {
} else if (ult) {
player.mode = 'ultimate';
player.ultimate = parseStats(ultRes.data);
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
) {
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
) {
if (player.main.skills.overall.xp !== player.ironman.skills.overall.xp) {
player.deironed = true;
player.mode = 'main';
}
@@ -223,34 +351,26 @@ export async function getStats(rsn: string): Promise<Player> {
return player;
}
throw Error('Player not found');
}
/**
* Fetches stats from the OSRS API and returns them as an object.
*
* @param rsn Username of the player.
* @param mode Gamemode to fetch ranks for.
* @param config Optional axios request config object.
* @returns Stats object.
*/
export async function getStatsByGamemode(
rsn: string,
mode: Gamemode = 'main'
mode: Gamemode = 'main',
config?: AxiosRequestConfig
): 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));
if (response.status !== 200) {
throw Error('Player not found');
}
const stats = parseStats(response.data);
const response = await getOfficialStats(rsn, mode, config);
const stats = parseJsonStats(response);
return stats;
}
@@ -258,7 +378,8 @@ export async function getStatsByGamemode(
export async function getSkillPage(
skill: SkillName,
mode: Gamemode = 'main',
page: number = 1
page: number = 1,
config?: AxiosRequestConfig
): Promise<PlayerSkillRow[]> {
if (!GAMEMODES.includes(mode)) {
throw Error('Invalid game mode');
@@ -269,7 +390,10 @@ export async function getSkillPage(
}
const url = getSkillPageURL(mode, skill, page);
const response = await httpGet(url);
const response = await httpGet<string | Buffer | BinaryData | undefined>(
url,
config
);
const dom = new JSDOM(response.data);
const playersHTML = dom.window.document.querySelectorAll(
'.personal-hiscores__row'
@@ -301,12 +425,14 @@ export async function getSkillPage(
* @param activity Name of the activity or boss to fetch hiscores for.
* @param mode Gamemode to fetch ranks for.
* @param page Page number.
* @param config Optional axios request config object.
* @returns Array of `PlayerActivityRow` objects.
*/
export async function getActivityPage(
activity: ActivityName,
mode: Gamemode = 'main',
page: number = 1
page: number = 1,
config?: AxiosRequestConfig
): Promise<PlayerActivityRow[]> {
if (!GAMEMODES.includes(mode)) {
throw Error('Invalid game mode');
@@ -317,7 +443,10 @@ export async function getActivityPage(
}
const url = getActivityPageURL(mode, activity, page);
const response = await httpGet(url);
const response = await httpGet<string | Buffer | BinaryData | undefined>(
url,
config
);
const dom = new JSDOM(response.data);
const playersHTML = dom.window.document.querySelectorAll(
'.personal-hiscores__row'

View File

@@ -1,3 +1,5 @@
import { AxiosRequestConfig } from 'axios';
export type Gamemode =
| 'main'
| 'ironman'
@@ -5,7 +7,10 @@ export type Gamemode =
| 'hardcore'
| 'deadman'
| 'seasonal'
| 'tournament';
| 'tournament'
| 'skiller'
| 'oneDefence'
| 'freshStart';
export interface Skill {
rank: number;
@@ -57,16 +62,18 @@ export type ClueType =
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 Boss =
| 'abyssalSire'
| 'alchemicalHydra'
| 'artio'
| 'barrows'
| 'bryophyta'
| 'callisto'
| 'calvarion'
| 'cerberus'
| 'chambersOfXeric'
| 'chambersOfXericChallengeMode'
@@ -79,6 +86,7 @@ export type Boss =
| 'dagannothRex'
| 'dagannothSupreme'
| 'derangedArchaeologist'
| 'dukeSucellus'
| 'generalGraardor'
| 'giantMole'
| 'grotesqueGuardians'
@@ -89,19 +97,29 @@ export type Boss =
| 'kreeArra'
| 'krilTsutsaroth'
| 'mimic'
| 'nex'
| 'nightmare'
| 'phosanisNightmare'
| 'obor'
| 'phantomMuspah'
| 'sarachnis'
| 'scorpia'
| 'scurrius'
| 'skotizo'
| 'spindel'
| 'tempoross'
| 'gauntlet'
| 'corruptedGauntlet'
| 'leviathan'
| 'whisperer'
| 'theatreOfBlood'
| 'theatreOfBloodHardMode'
| 'thermonuclearSmokeDevil'
| 'tombsOfAmascut'
| 'tombsOfAmascutExpertMode'
| 'tzKalZuk'
| 'tzTokJad'
| 'vardorvis'
| 'venenatis'
| 'vetion'
| 'vorkath'
@@ -113,10 +131,15 @@ export type Bosses = { [Type in Boss]: Activity };
export type ActivityName =
| 'leaguePoints'
| 'deadmanPoints'
| 'hunterBHV2'
| 'rogueBHV2'
| 'hunterBH'
| 'rogueBH'
| 'lastManStanding'
| 'pvpArena'
| 'soulWarsZeal'
| 'riftsClosed'
| 'allClues'
| 'beginnerClues'
| 'easyClues'
@@ -129,10 +152,19 @@ export type ActivityName =
export interface Stats {
skills: Skills;
clues: Clues;
/**
* Will only contain rank and score data for the `seasonal` gamemode.
*/
leaguePoints: Activity;
/**
* Will only contain rank and score data for the `tournament` gamemode.
*/
deadmanPoints: Activity;
bountyHunter: BH;
lastManStanding: Activity;
pvpArena: Activity;
soulWarsZeal: Activity;
riftsClosed: Activity;
bosses: Bosses;
}
export type Modes = { [M in Gamemode]?: Stats };
@@ -154,3 +186,63 @@ 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;
/**
* Map of configs for each requests that can take place in the `getStats` function.
*/
axiosConfigs?: Partial<Record<Gamemode, AxiosRequestConfig>> & {
/**
* The axios request config object to use for the RSN format request.
*/
rsn?: AxiosRequestConfig;
};
}
export interface HiscoresCommon {
/**
* This field behaves more like an index than a true unique ID.
*/
id: number;
/**
* The display name of this skill / activity.
*/
name: string;
/**
* The player's official hiscores rank in this skill / activity.
*/
rank: number;
}
export interface HiscoresSkill extends HiscoresCommon {
/**
* The player's current level in this skill.
*/
level: number;
/**
* The player's current experience in this skill.
*/
xp: number;
}
export interface HiscoresActivity extends HiscoresCommon {
/**
* The player's current score in this activity.
*/
score: number;
}
export interface HiscoresResponse {
skills: HiscoresSkill[];
activities: HiscoresActivity[];
}

View File

@@ -9,6 +9,7 @@ import {
export const BASE_URL = 'https://secure.runescape.com/m=hiscore_oldschool';
export const STATS_URL = 'index_lite.ws?player=';
export const JSON_STATS_URL = 'index_lite.json?player=';
export const SCORES_URL = 'overall.ws?';
export type GamemodeUrl = {
@@ -22,7 +23,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',
@@ -59,7 +63,7 @@ export const CLUES: ClueType[] = [
'elite',
'master'
];
export const BH_MODES: BHType[] = ['hunter', 'rogue'];
export const BH_MODES: BHType[] = ['hunterV2', 'rogueV2', 'hunter', 'rogue'];
export const GAMEMODES: Gamemode[] = [
'main',
'ironman',
@@ -72,9 +76,11 @@ export const GAMEMODES: Gamemode[] = [
export const BOSSES: Boss[] = [
'abyssalSire',
'alchemicalHydra',
'artio',
'barrows',
'bryophyta',
'callisto',
'calvarion',
'cerberus',
'chambersOfXeric',
'chambersOfXericChallengeMode',
@@ -87,6 +93,7 @@ export const BOSSES: Boss[] = [
'dagannothRex',
'dagannothSupreme',
'derangedArchaeologist',
'dukeSucellus',
'generalGraardor',
'giantMole',
'grotesqueGuardians',
@@ -97,19 +104,29 @@ export const BOSSES: Boss[] = [
'kreeArra',
'krilTsutsaroth',
'mimic',
'nex',
'nightmare',
'phosanisNightmare',
'obor',
'phantomMuspah',
'sarachnis',
'scorpia',
'scurrius',
'skotizo',
'spindel',
'tempoross',
'gauntlet',
'corruptedGauntlet',
'leviathan',
'whisperer',
'theatreOfBlood',
'theatreOfBloodHardMode',
'thermonuclearSmokeDevil',
'tombsOfAmascut',
'tombsOfAmascutExpertMode',
'tzKalZuk',
'tzTokJad',
'vardorvis',
'venenatis',
'vetion',
'vorkath',
@@ -119,6 +136,9 @@ export const BOSSES: Boss[] = [
];
export const ACTIVITIES: ActivityName[] = [
'leaguePoints',
'deadmanPoints',
'hunterBHV2',
'rogueBHV2',
'hunterBH',
'rogueBH',
'allClues',
@@ -129,7 +149,9 @@ export const ACTIVITIES: ActivityName[] = [
'eliteClues',
'masterClues',
'lastManStanding',
'pvpArena',
'soulWarsZeal',
'riftsClosed',
...BOSSES
];
@@ -140,9 +162,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',
@@ -155,6 +179,7 @@ export const FORMATTED_BOSS_NAMES: FormattedBossNames = {
dagannothRex: 'Dagannoth Rex',
dagannothSupreme: 'Dagannoth Supreme',
derangedArchaeologist: 'Deranged Archaeologist',
dukeSucellus: 'Duke Sucellus',
generalGraardor: 'General Graardor',
giantMole: 'Giant Mole',
grotesqueGuardians: 'Grotesque Guardians',
@@ -165,19 +190,29 @@ export const FORMATTED_BOSS_NAMES: FormattedBossNames = {
kreeArra: "Kree'Arra",
krilTsutsaroth: "K'ril Tsutsaroth",
mimic: 'Mimic',
nightmare: 'The Nightmare of Ashihama',
nex: 'Nex',
nightmare: 'Nightmare',
phosanisNightmare: "Phosani's Nightmare",
obor: 'Obor',
phantomMuspah: 'Phantom Muspah',
sarachnis: 'Sarachnis',
scorpia: 'Scorpia',
scurrius: 'Scurrius',
skotizo: 'Skotizo',
spindel: 'Spindel',
tempoross: 'Tempoross',
gauntlet: 'The Gauntlet',
corruptedGauntlet: 'The Corrupted Gauntlet',
leviathan: 'The Leviathan',
whisperer: 'The Whisperer',
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',
vardorvis: 'Vardorvis',
venenatis: 'Venenatis',
vetion: "Vet'ion",
vorkath: 'Vorkath',
@@ -236,10 +271,55 @@ export type FormattedBHNames = {
};
export const FORMATTED_BH_NAMES: FormattedBHNames = {
rogue: 'Bounty Hunter - Rogue',
hunter: 'Bounty Hunter - Hunter'
rogue: 'Bounty Hunter (Legacy) - Rogue',
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 - Rank';
export const FORMATTED_SOUL_WARS = 'Soul Wars Zeal';
export const FORMATTED_LEAGUE_POINTS = 'League Points';
export const FORMATTED_DEADMAN_POINTS = 'Deadman Points';
export const FORMATTED_RIFTS_CLOSED = 'Rifts closed';
export const INVALID_FORMAT_ERROR = 'Invalid hiscores format';
export const PLAYER_NOT_FOUND_ERROR = 'Player not found';
export const HISCORES_ERROR = 'HiScores not responding';
export class InvalidFormatError extends Error {
__proto__ = Error;
constructor() {
super(INVALID_FORMAT_ERROR);
Object.setPrototypeOf(this, InvalidFormatError.prototype);
}
}
export class InvalidRSNError extends Error {
__proto__ = Error;
constructor(message: string) {
super(message);
Object.setPrototypeOf(this, InvalidRSNError.prototype);
}
}
export class PlayerNotFoundError extends Error {
__proto__ = Error;
constructor() {
super(PLAYER_NOT_FOUND_ERROR);
Object.setPrototypeOf(this, PlayerNotFoundError.prototype);
}
}
export class HiScoresError extends Error {
__proto__ = Error;
constructor() {
super(HISCORES_ERROR);
Object.setPrototypeOf(this, HiScoresError.prototype);
}
}

View File

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

View File

@@ -4,7 +4,12 @@
"module": "commonjs",
"declaration": true,
"outDir": "./lib",
"strict": true
"strict": true,
"lib": ["ES2015", "DOM", "DOM.Iterable"],
"typeRoots": [
"./node_modules/@types",
"./src/@types"
]
},
"include": ["src"],
"exclude": ["node_modules", "**/__tests__/*"]

5560
yarn.lock

File diff suppressed because it is too large Load Diff