├── .eslintignore ├── .eslintrc ├── .github ├── examples │ └── runescape │ │ └── bestiary.getAreas.svg └── workflows │ └── main.yml ├── .gitignore ├── .npmignore ├── .prettierignore ├── .prettierrc ├── LICENSE ├── README.md ├── SUMMARY.md ├── docs ├── bestiary.md ├── clan.md ├── grandexchange.md ├── hiscores.md ├── miscellaneous.md ├── osrs │ ├── README.md │ ├── grandexchange.md │ └── hiscores.md └── runemetrics.md ├── package.json ├── scripts └── build_and_move ├── src ├── configs │ ├── oldschool.ts │ └── runescape.ts ├── index.ts ├── lib │ ├── Oldschool.ts │ └── RuneScape.ts ├── osrs │ ├── grandexchange.ts │ ├── hiscores.ts │ └── index.ts ├── runescape │ ├── bestiary.ts │ ├── clan.ts │ ├── grandexchange.ts │ ├── hiscores.ts │ ├── index.ts │ ├── miscellaneous.ts │ └── runemetrics.ts ├── types.ts └── utils │ └── Jagex.ts ├── tsconfig.json └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | # Ignore everything: 2 | /* 3 | 4 | # Except the source files 5 | !/src 6 | 7 | # Exclude tests folder 8 | src/__tests__ 9 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "parserOptions": { 4 | "project": "./tsconfig.json" 5 | }, 6 | "plugins": ["@typescript-eslint/eslint-plugin"], 7 | "extends": [ 8 | "prettier" 9 | ], 10 | "rules": { 11 | /** 12 | 0 = off 13 | 1 = warn 14 | 2 = error 15 | */ 16 | "@typescript-eslint/explicit-function-return-type": "off", 17 | "@typescript-eslint/no-explicit-any": "off", 18 | "@typescript-eslint/no-empty-function": "warn", 19 | "@typescript-eslint/no-namespace": "off", 20 | "@typescript-eslint/no-use-before-define": "off" 21 | }, 22 | "overrides": [ 23 | { 24 | "files": [ 25 | "./src/configs.ts", 26 | "./src/utils/Jagex.ts", 27 | "./src/lib/RuneScape.ts" 28 | ], 29 | "rules": { 30 | "@typescript-eslint/camelcase": "off" 31 | } 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: status 2 | on: push 3 | 4 | jobs: 5 | npm: 6 | name: Publish to NPM 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@master 10 | - uses: actions/setup-node@master 11 | with: 12 | node-version: "12.x" 13 | 14 | - name: install 15 | run: npm install 16 | 17 | - name: format 18 | run: npm run format 19 | 20 | - name: lint 21 | run: npm run lint 22 | 23 | - name: build 24 | run: scripts/build_and_move 25 | 26 | - name: publish (npm) 27 | uses: primer/publish@v2.0.0 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} 31 | args: "--dry-run -- --unsafe-perm" 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependancies 2 | node_modules 3 | 4 | # Built Files 5 | build 6 | 7 | # Mac OS Files 8 | .DS_STORE 9 | package-lock.json 10 | 11 | .idea 12 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # GitHub Configuration Files 2 | .github 3 | 4 | # Testing Files 5 | __tests__ 6 | 7 | # Dependancies 8 | node_modules 9 | 10 | # Executables 11 | scripts 12 | 13 | # Source Files 14 | src 15 | 16 | # Log Files 17 | *.log 18 | 19 | # Lock Files 20 | *.lock 21 | package-lock.json 22 | 23 | # Dotfiles 24 | .* 25 | 26 | # Configurations 27 | tsconfig.json 28 | tsconfig.tsbuildinfo 29 | 30 | # Results of `npm pack` 31 | *.tgz 32 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore everything: 2 | /* 3 | 4 | # Except these root files: 5 | !LICENSE 6 | !README.md 7 | !package.json 8 | 9 | # Except the source files 10 | !/src 11 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf", 3 | "semi": false, 4 | "singleQuote": false, 5 | "tabWidth": 2, 6 | "trailingComma": "es5" 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Joshua Filby 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | [![Current Github Action build status.](https://github.com/astrect/runescape-api/workflows/status/badge.svg?color=005cc5)](https://github.com/astrect/runescape-api/actions?workflow=status) [![Package dependancy status.](https://img.shields.io/librariesio/release/npm/runescape-api)](https://www.npmjs.org/package/runescape-api) [![Total downloads.](https://img.shields.io/npm/dt/runescape-api?color=005cc5)](https://www.npmjs.org/package/runescape-api) [![npm package version.](https://img.shields.io/npm/v/runescape-api.svg?color=005cc5)](https://www.npmjs.org/package/runescape-api) [![Released under the MIT license.](https://img.shields.io/badge/license-MIT-blue.svg?color=005cc5)](https://github.com/astrect/runescape-api/blob/master/LICENSE) 4 | 5 | `runescape-api` is an open-source wrapper, written in [Node.js](https://nodejs.org), that allows interaction with the various APIs available for the popular MMORPG [RuneScape](http://www.runescape.com). 6 | 7 | ### Installation 8 | 9 | ```bash 10 | npm install runescape-api 11 | # OR 12 | yarn add runescape-api 13 | ``` 14 | 15 | ### Usage 16 | 17 | The implementation offers functionality to interact with the following: 18 | 19 | * [Bestiary](https://astrect.gitbook.io/runescape-api/docs/bestiary) 20 | * [Clan](https://astrect.gitbook.io/runescape-api/docs/clan) 21 | * [Grand Exchange](https://astrect.gitbook.io/runescape-api/docs/grandexchange) 22 | * [Hiscores](https://astrect.gitbook.io/runescape-api/docs/hiscores) 23 | * [Miscellaneous](https://astrect.gitbook.io/runescape-api/docs/miscellaneous) 24 | * [RuneMetrics](https://astrect.gitbook.io/runescape-api/docs/runemetrics) 25 | * [Oldschool](https://astrect.gitbook.io/runescape-api/docs/osrs) 26 | * [Grand Exchange](https://astrect.gitbook.io/runescape-api/docs/osrs/grandexchange) 27 | * [Hiscores](https://astrect.gitbook.io/runescape-api/docs/osrs/hiscores) 28 | 29 | More, detailed examples can be provided on the respective documentation pages, a complete summary of the documentation pages can be found on the [GitHub Repository](https://github.com/astrect/runescape-api/blob/master/SUMMARY.md). 30 | 31 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [Introduction](README.md) 4 | 5 | ## Documentation 6 | 7 | * [Bestiary](docs/bestiary.md) 8 | * [Clan](docs/clan.md) 9 | * [Grand Exchange](docs/grandexchange.md) 10 | * [Hiscores](docs/hiscores.md) 11 | * [Miscellaneous](docs/miscellaneous.md) 12 | * [RuneMetrics](docs/runemetrics.md) 13 | * [Oldschool](docs/osrs/README.md) 14 | * [OSRS Grand Exchange](docs/osrs/grandexchange.md) 15 | * [OSRS Hiscores](docs/osrs/hiscores.md) 16 | 17 | -------------------------------------------------------------------------------- /docs/bestiary.md: -------------------------------------------------------------------------------- 1 | # Bestiary 2 | 3 | ## Usage 4 | 5 | ```javascript 6 | import { bestiary } from "runescape-api" 7 | ``` 8 | 9 | ## Functions 10 | 11 | ### getAreas\(\) 12 | 13 | > List all bestiary areas 14 | 15 | {% tabs %} 16 | {% tab title="JavaScript" %} 17 | ```javascript 18 | bestiary.getAreas().then(data => { 19 | console.log(data) 20 | }) 21 | ``` 22 | {% endtab %} 23 | 24 | {% tab title="Response" %} 25 | ```javascript 26 | [ 27 | Area { name: 'Agility Pyramid' }, 28 | Area { name: 'Agility course pit' }, 29 | Area { name: 'Air Rune Temple' }, 30 | Area { name: 'Aminishi Dungeon' }, 31 | Area { name: 'Ancient cavern' }, 32 | Area { name: 'Ape Atoll' }, 33 | Area { name: 'Ape Atoll tunnels' }, 34 | Area { name: 'Ardougne underground' }, 35 | Area { name: "Armadyl's Citadel" }, 36 | Area { name: 'Arposandran underground' }, 37 | Area { name: 'Arzinian Gold Mine' }, 38 | Area { name: 'Ascension Dungeon' }, 39 | Area { name: 'Asgarnian Ice Dungeon' }, 40 | Area { name: 'Ashdale Caves' }, 41 | Area { name: 'Assault course' }, 42 | Area { name: "Baba Yaga's House" }, 43 | // ... 200+ more items 44 | ] 45 | ``` 46 | {% endtab %} 47 | {% endtabs %} 48 | 49 | ### getBeast\(:id\) 50 | 51 | > Retrieve a beast 52 | 53 | | Parameter | Type | 54 | | :--- | :--- | 55 | | `id` | `number` | 56 | 57 | {% tabs %} 58 | {% tab title="JavaScript" %} 59 | ```javascript 60 | bestiary.getBeast(16705).then(data => { 61 | console.log(data) 62 | }) 63 | ``` 64 | {% endtab %} 65 | 66 | {% tab title="Response" %} 67 | ```javascript 68 | Beast { 69 | id: 16705, 70 | name: 'Exiled kalphite guardian', 71 | examine: 'A dauntless guardian of the Exiled Kalphite Queen.', 72 | members: false, 73 | level: 98, 74 | attack: 70, 75 | defence: 70, 76 | magic: 1, 77 | ranged: 1, 78 | lifepoints: 7000, 79 | xp: '628.6', 80 | areas: [ 'Exiled Kalphite Hive' ], 81 | animations: { death: 19470, attack: 19473 }, 82 | size: 4, 83 | attackable: true, 84 | aggressive: true, 85 | poisonous: true, 86 | weakness: Weakness { id: 2, name: 'Water' } 87 | } 88 | ``` 89 | {% endtab %} 90 | {% endtabs %} 91 | 92 | ### getBeastsByArea\(:area\) 93 | 94 | > List all beasts within a given area 95 | 96 | | Parameter | Type | 97 | | :--- | :--- | 98 | | `area` | `string` \| `Area` | 99 | 100 | {% tabs %} 101 | {% tab title="JavaScript" %} 102 | ```javascript 103 | bestiary.getBeastsByArea("Glarial's tomb").then(data => { 104 | console.log(data) 105 | }) 106 | ``` 107 | {% endtab %} 108 | 109 | {% tab title="Response" %} 110 | ```javascript 111 | [ 112 | BeastSearchResult { id: 153, name: 'Butterfly' }, 113 | BeastSearchResult { id: 1681, name: 'Moss giant (61)' }, 114 | BeastSearchResult { id: 90, name: 'Skeleton (15)' }, 115 | BeastSearchResult { id: 92, name: 'Skeleton (32)' }, 116 | BeastSearchResult { id: 5337, name: 'Skeleton (32)' }, 117 | BeastSearchResult { id: 5339, name: 'Skeleton (32)' }, 118 | BeastSearchResult { id: 5340, name: 'Skeleton (32)' }, 119 | BeastSearchResult { id: 73, name: 'Zombie (12)' }, 120 | BeastSearchResult { id: 75, name: 'Zombie (29)' }, 121 | BeastSearchResult { id: 5296, name: 'Zombie (12)' }, 122 | BeastSearchResult { id: 5314, name: 'Zombie (29)' }, 123 | BeastSearchResult { id: 5315, name: 'Zombie (29)' } 124 | ] 125 | ``` 126 | {% endtab %} 127 | {% endtabs %} 128 | 129 | ### getBeastsByTerms\(:term\) 130 | 131 | > List all beasts by a given term 132 | 133 | | Parameter | Type | 134 | | :--- | :--- | 135 | | `term` | `string` | 136 | 137 | {% tabs %} 138 | {% tab title="JavaScript" %} 139 | ```javascript 140 | bestiary.getBeastsByTerms("cow").then(data => { 141 | console.log(data) 142 | }) 143 | ``` 144 | {% endtab %} 145 | 146 | {% tab title="Response" %} 147 | ```javascript 148 | [ 149 | BeastSearchResult { id: 81, name: 'Cow (4)' }, 150 | BeastSearchResult { id: 397, name: 'Cow (4)' }, 151 | BeastSearchResult { id: 3309, name: 'Cow (1)' }, 152 | BeastSearchResult { id: 14999, name: 'Cow (1)' }, 153 | BeastSearchResult { id: 20969, name: 'Cow (4)' }, 154 | BeastSearchResult { id: 20970, name: 'Cow (4)' }, 155 | BeastSearchResult { id: 20971, name: 'Cow (4)' }, 156 | BeastSearchResult { id: 20974, name: 'Cow (4)' }, 157 | BeastSearchResult { id: 1886, name: 'Cowardly Bandit' }, 158 | BeastSearchResult { id: 2310, name: 'Cow calf (1)' }, 159 | BeastSearchResult { id: 5097, name: 'Coward in armour (82)' }, 160 | BeastSearchResult { id: 6049, name: 'Coward with bow (105)' }, 161 | BeastSearchResult { id: 20978, name: 'Cow calf (1)' }, 162 | BeastSearchResult { id: 20979, name: 'Cow calf (1)' }, 163 | BeastSearchResult { id: 1691, name: 'Undead cow (4)' }, 164 | BeastSearchResult { id: 1998, name: 'Plague cow' }, 165 | BeastSearchResult { id: 1999, name: 'Plague cow' }, 166 | BeastSearchResult { id: 5603, name: 'Unicow (57)' }, 167 | BeastSearchResult { id: 18597, name: 'Zombie cow (1)' }, 168 | BeastSearchResult { id: 20928, name: 'Zombie cow (1)' }, 169 | BeastSearchResult { id: 21497, name: 'Super Cow (5)' }, 170 | BeastSearchResult { id: 22418, name: 'Dairy cow' }, 171 | BeastSearchResult { id: 5986, name: 'Armoured cow thing (62)' }, 172 | BeastSearchResult { id: 6048, name: 'Armoured cow thing (62)' } 173 | ] 174 | ``` 175 | {% endtab %} 176 | {% endtabs %} 177 | 178 | ### getBeastsByFirstLetter\(:letter\) 179 | 180 | > List all beasts starting with a given letter 181 | 182 | | Parameter | Type | 183 | | :--- | :--- | 184 | | `letter` | `string` | 185 | 186 | {% tabs %} 187 | {% tab title="JavaScript" %} 188 | ```javascript 189 | bestiary.getBeastsByFirstLetter("c").then(data => { 190 | console.log(data) 191 | }) 192 | ``` 193 | {% endtab %} 194 | 195 | {% tab title="Response" %} 196 | ```javascript 197 | [ 198 | { label: 'Cabbage', value: 22836 }, 199 | { label: 'Cabbage archer (0)', value: 21455 }, 200 | { label: 'Cabbagemancer (105)', value: 25157 }, 201 | { label: 'Cabbageomancer (0)', value: 21456 }, 202 | { label: 'Cabin boy', value: 4539 }, 203 | { label: 'Cadarn herald', value: 19857 }, 204 | // ... 200+ more items 205 | ] 206 | ``` 207 | {% endtab %} 208 | {% endtabs %} 209 | 210 | ### getBeastsBySlayerCategory\(:categoryId\) 211 | 212 | > List all beasts within a given slayer category 213 | 214 | | Parameter | Type | 215 | | :--- | :--- | 216 | | `categoryId` | `number` | 217 | 218 | {% tabs %} 219 | {% tab title="JavaScript" %} 220 | ```javascript 221 | bestiary.getBeastsBySlayerCategory(45).then(data => { 222 | console.log(data) 223 | }) 224 | ``` 225 | {% endtab %} 226 | 227 | {% tab title="Response" %} 228 | ```javascript 229 | [ 230 | BeastSearchResult { id: 1608, name: 'Kurask (78)' }, 231 | BeastSearchResult { id: 7805, name: 'Kurask minion (63)' }, 232 | BeastSearchResult { id: 7797, name: 'Kurask overlord (96)' } 233 | ] 234 | ``` 235 | {% endtab %} 236 | {% endtabs %} 237 | 238 | ### getBeastsByWeakness\(:weaknessId\) 239 | 240 | > List all beasts with a given weakness 241 | 242 | | Parameter | Type | 243 | | :--- | :--- | 244 | | `weaknessId` | `number` | 245 | 246 | {% tabs %} 247 | {% tab title="JavaScript" %} 248 | ```javascript 249 | bestiary.getBeastsByWeakness(7).then(data => { 250 | console.log(data) 251 | }) 252 | ``` 253 | {% endtab %} 254 | 255 | {% tab title="Response" %} 256 | ```javascript 257 | [ 258 | BeastSearchResult { id: 2263, name: 'Abyssal leech (72)' }, 259 | BeastSearchResult { id: 4283, name: 'Animated Adamant Armour (67)' }, 260 | BeastSearchResult { id: 4281, name: 'Animated Black Armour (46)' }, 261 | BeastSearchResult { id: 4278, name: 'Animated Bronze Armour (11)' }, 262 | BeastSearchResult { id: 4279, name: 'Animated Iron Armour (25)' }, 263 | BeastSearchResult { id: 4282, name: 'Animated Mithril Armour (53)' }, 264 | BeastSearchResult { id: 4284, name: 'Animated Rune Armour (81)' }, 265 | BeastSearchResult { id: 4280, name: 'Animated Steel Armour (39)' }, 266 | // ... 100+ more items 267 | ] 268 | ``` 269 | {% endtab %} 270 | {% endtabs %} 271 | 272 | ### getSlayerCategories\(\) 273 | 274 | > List all slayer categories 275 | 276 | {% tabs %} 277 | {% tab title="JavaScript" %} 278 | ```javascript 279 | bestiary.getSlayerCategories().then(data => { 280 | console.log(data) 281 | }) 282 | ``` 283 | {% endtab %} 284 | 285 | {% tab title="Response" %} 286 | ```javascript 287 | [ 288 | SlayerCategory { id: 41, name: 'Aberrant spectres' }, 289 | SlayerCategory { id: 42, name: 'Abyssal demons' }, 290 | SlayerCategory { id: 133, name: 'Acheron mammoths' }, 291 | SlayerCategory { id: 127, name: 'Adamant dragons' }, 292 | SlayerCategory { id: 117, name: 'Airut' }, 293 | SlayerCategory { id: 79, name: 'Ankou' }, 294 | // ... 100+ more items 295 | ] 296 | ``` 297 | {% endtab %} 298 | {% endtabs %} 299 | 300 | ### getWeaknesses\(\) 301 | 302 | > List all weaknesses 303 | 304 | {% tabs %} 305 | {% tab title="JavaScript" %} 306 | ```javascript 307 | bestiary.getWeaknesses().then(data => { 308 | console.log(data) 309 | }) 310 | ``` 311 | {% endtab %} 312 | 313 | {% tab title="Response" %} 314 | ```javascript 315 | [ 316 | Weakness { id: 1, name: 'Air' }, 317 | Weakness { id: 8, name: 'Arrow' }, 318 | Weakness { id: 9, name: 'Bolt' }, 319 | Weakness { id: 7, name: 'Crushing' }, 320 | Weakness { id: 3, name: 'Earth' }, 321 | Weakness { id: 4, name: 'Fire' }, 322 | Weakness { id: 0, name: 'None' }, 323 | Weakness { id: 6, name: 'Slashing' }, 324 | Weakness { id: 5, name: 'Stabbing' }, 325 | Weakness { id: 10, name: 'Thrown' }, 326 | Weakness { id: 2, name: 'Water' } 327 | ] 328 | ``` 329 | {% endtab %} 330 | {% endtabs %} 331 | 332 | -------------------------------------------------------------------------------- /docs/clan.md: -------------------------------------------------------------------------------- 1 | # Clan 2 | 3 | ## Usage 4 | 5 | ```javascript 6 | import { clan } from "runescape-api" 7 | ``` 8 | 9 | ## Functions 10 | 11 | ### getMembers\(:clanName\) 12 | 13 | > List all clan members 14 | 15 | {% tabs %} 16 | {% tab title="JavaScript" %} 17 | ```javascript 18 | clan.getMembers("Royal 58").then(data => { 19 | console.log(data) 20 | }) 21 | ``` 22 | {% endtab %} 23 | 24 | {% tab title="Response" %} 25 | ```javascript 26 | [ 27 | ClanMember { 28 | name: 'Conundrum129', 29 | rank: 'Owner', 30 | experience: 695552733, 31 | kills: 0 32 | }, 33 | ClanMember { 34 | name: 'I50RitaWendt', 35 | rank: 'Deputy Owner', 36 | experience: 414968518, 37 | kills: 3204 38 | }, 39 | ClanMember { 40 | name: 'Good Potato', 41 | rank: 'Overseer', 42 | experience: 32816425, 43 | kills: 0 44 | }, 45 | ClanMember { 46 | name: 'Emagnifesin', 47 | rank: 'Coordinator', 48 | experience: 15144845, 49 | kills: 0 50 | }, 51 | // ... 100+ more items 52 | ] 53 | ``` 54 | {% endtab %} 55 | {% endtabs %} 56 | 57 | -------------------------------------------------------------------------------- /docs/grandexchange.md: -------------------------------------------------------------------------------- 1 | # Grand Exchange 2 | 3 | ## Usage 4 | 5 | ```javascript 6 | import { grandexchange } from "runescape-api" 7 | ``` 8 | 9 | ## Functions 10 | 11 | ### getCategories\(\) 12 | 13 | > List all categories 14 | 15 | {% tabs %} 16 | {% tab title="JavaScript" %} 17 | ```javascript 18 | grandexchange.getCategories().then(data => { 19 | console.log(data) 20 | }) 21 | ``` 22 | {% endtab %} 23 | 24 | {% tab title="Response" %} 25 | ```javascript 26 | [ 27 | GrandExchangeCategory { id: 0, name: 'Miscellaneous' }, 28 | GrandExchangeCategory { id: 1, name: 'Ammo' }, 29 | GrandExchangeCategory { id: 2, name: 'Arrows' }, 30 | GrandExchangeCategory { id: 3, name: 'Bolts' }, 31 | GrandExchangeCategory { id: 4, name: 'Construction materials' }, 32 | GrandExchangeCategory { id: 5, name: 'Construction projects' }, 33 | GrandExchangeCategory { id: 6, name: 'Cooking ingredients' }, 34 | GrandExchangeCategory { id: 7, name: 'Costumes' }, 35 | GrandExchangeCategory { id: 8, name: 'Crafting materials' }, 36 | GrandExchangeCategory { id: 9, name: 'Familiars' }, 37 | GrandExchangeCategory { id: 10, name: 'Farming produce' }, 38 | GrandExchangeCategory { id: 11, name: 'Fletching materials' }, 39 | GrandExchangeCategory { id: 12, name: 'Food and drink' }, 40 | GrandExchangeCategory { id: 13, name: 'Herblore materials' }, 41 | GrandExchangeCategory { id: 14, name: 'Hunting equipment' }, 42 | GrandExchangeCategory { id: 15, name: 'Hunting produce' }, 43 | GrandExchangeCategory { id: 16, name: 'Jewellery' }, 44 | GrandExchangeCategory { id: 17, name: 'Mage armour' }, 45 | GrandExchangeCategory { id: 18, name: 'Mage weapons' }, 46 | GrandExchangeCategory { id: 19, name: 'Melee armour - low level' }, 47 | GrandExchangeCategory { id: 20, name: 'Melee armour - mid level' }, 48 | GrandExchangeCategory { id: 21, name: 'Melee armour - high level' }, 49 | GrandExchangeCategory { id: 22, name: 'Melee weapons - low level' }, 50 | GrandExchangeCategory { id: 23, name: 'Melee weapons - mid level' }, 51 | GrandExchangeCategory { id: 24, name: 'Melee weapons - high level' }, 52 | GrandExchangeCategory { id: 25, name: 'Mining and smithing' }, 53 | GrandExchangeCategory { id: 26, name: 'Potions' }, 54 | GrandExchangeCategory { id: 27, name: 'Prayer armour' }, 55 | GrandExchangeCategory { id: 28, name: 'Prayer materials' }, 56 | GrandExchangeCategory { id: 29, name: 'Range armour' }, 57 | GrandExchangeCategory { id: 30, name: 'Range weapons' }, 58 | GrandExchangeCategory { id: 31, name: 'Runecrafting' }, 59 | GrandExchangeCategory { id: 32, name: 'Runes, Spells and Teleports' }, 60 | GrandExchangeCategory { id: 33, name: 'Seeds' }, 61 | GrandExchangeCategory { id: 34, name: 'Summoning scrolls' }, 62 | GrandExchangeCategory { id: 35, name: 'Tools and containers' }, 63 | GrandExchangeCategory { id: 36, name: 'Woodcutting product' }, 64 | GrandExchangeCategory { id: 37, name: 'Pocket items' } 65 | ] 66 | ``` 67 | {% endtab %} 68 | {% endtabs %} 69 | 70 | ### getCategoryCounts\(:categoryId\) 71 | 72 | {% tabs %} 73 | {% tab title="JavaScript" %} 74 | ```javascript 75 | grandexchange.getCategoryCounts(24).then(data => { 76 | console.log(data) 77 | }) 78 | ``` 79 | {% endtab %} 80 | 81 | {% tab title="Response" %} 82 | ```javascript 83 | [ 84 | { letter: '#', items: 0 }, 85 | { letter: 'a', items: 7 }, 86 | { letter: 'b', items: 26 }, 87 | { letter: 'c', items: 0 }, 88 | { letter: 'd', items: 9 }, 89 | { letter: 'e', items: 18 }, 90 | { letter: 'f', items: 0 }, 91 | { letter: 'g', items: 6 }, 92 | { letter: 'h', items: 0 }, 93 | { letter: 'i', items: 0 }, 94 | { letter: 'j', items: 0 }, 95 | { letter: 'k', items: 2 }, 96 | { letter: 'l', items: 1 }, 97 | { letter: 'm', items: 1 }, 98 | { letter: 'n', items: 16 }, 99 | { letter: 'o', items: 3 }, 100 | { letter: 'p', items: 0 }, 101 | { letter: 'q', items: 0 }, 102 | { letter: 'r', items: 3 }, 103 | { letter: 's', items: 5 }, 104 | { letter: 't', items: 3 }, 105 | { letter: 'u', items: 0 }, 106 | { letter: 'v', items: 4 }, 107 | { letter: 'w', items: 4 }, 108 | { letter: 'x', items: 0 }, 109 | { letter: 'y', items: 0 }, 110 | { letter: 'z', items: 4 } 111 | ] 112 | ``` 113 | {% endtab %} 114 | {% endtabs %} 115 | 116 | ### getCategoryCountsByPrefix\(:categoryId, :prefix, :page?\) 117 | 118 | {% tabs %} 119 | {% tab title="JavaScript" %} 120 | ```javascript 121 | grandexchange.getCategoryCountsByPrefix(24, "b").then(data => { 122 | console.log(data) 123 | }) 124 | ``` 125 | {% endtab %} 126 | 127 | {% tab title="Response" %} 128 | ``` 129 | 130 | ``` 131 | {% endtab %} 132 | {% endtabs %} 133 | 134 | ### getItem\(:id\) 135 | 136 | > Retrieve an item 137 | 138 | {% tabs %} 139 | {% tab title="JavaScript" %} 140 | ```javascript 141 | grandexchange.getItem(4151).then(data => { 142 | console.log(data) 143 | }) 144 | ``` 145 | {% endtab %} 146 | 147 | {% tab title="Response" %} 148 | ```javascript 149 | Item { 150 | id: 4151, 151 | examine: 'A weapon from the Abyss.', 152 | category: GrandExchangeCategory { id: 24, name: 'Melee weapons - high level' }, 153 | members: true, 154 | icons: { 155 | default: 'http://services.runescape.com/m=itemdb_rs/1581332634573_obj_sprite.gif?id=4151', 156 | large: 'http://services.runescape.com/m=itemdb_rs/1581332634573_obj_big.gif?id=4151' 157 | }, 158 | trends: { 159 | current: { trend: 'neutral', price: '70.5k' }, 160 | today: { trend: 'negative', price: '- 248' }, 161 | day30: { trend: 'negative', change: '-0.0%' }, 162 | day90: { trend: 'positive', change: '+0.0%' }, 163 | day180: { trend: 'negative', change: '-0.0%' } 164 | } 165 | } 166 | ``` 167 | {% endtab %} 168 | {% endtabs %} 169 | 170 | ### getItemGraph\(:id\) 171 | 172 | > Retrieve an item's price history graph data 173 | 174 | {% tabs %} 175 | {% tab title="JavaScript" %} 176 | ```javascript 177 | grandexchange.getItemGraph(4151).then(data => { 178 | console.log(data) 179 | }) 180 | ``` 181 | {% endtab %} 182 | 183 | {% tab title="Response" %} 184 | ```javascript 185 | ItemGraph { 186 | id: 4151, 187 | daily: { 188 | '1565913600000': 70984, 189 | '1566000000000': 70741, 190 | '1566086400000': 70681, 191 | '1566172800000': 70299, 192 | // ... 193 | }, 194 | average: { 195 | '1565913600000': 70923, 196 | '1566000000000': 70895, 197 | '1566086400000': 70874, 198 | '1566172800000': 70841, 199 | '1566259200000': 70833, 200 | // ... 201 | } 202 | } 203 | ``` 204 | {% endtab %} 205 | {% endtabs %} 206 | 207 | -------------------------------------------------------------------------------- /docs/hiscores.md: -------------------------------------------------------------------------------- 1 | # Hiscores 2 | 3 | ## Usage 4 | 5 | ```javascript 6 | import { hiscores } from "runescape-api" 7 | ``` 8 | 9 | ## Functions 10 | 11 | ### getPlayer\(:playerName, :gamemode?\) 12 | 13 | > Retrieve a player 14 | 15 | | Parameter | Type | Default | 16 | | :--- | :--- | :--- | 17 | | `playerName` | `string` | | 18 | | `gamemode` | `normal` \| `ironman` \| `hardcore` | `normal` | 19 | 20 | {% tabs %} 21 | {% tab title="JavaScript" %} 22 | ```javascript 23 | hiscores.getPlayer("Paqt").then(data => { 24 | console.log(data) 25 | }) 26 | ``` 27 | {% endtab %} 28 | 29 | {% tab title="Response" %} 30 | ```javascript 31 | Player { 32 | name: 'Paqt', 33 | activities: { 34 | bounty_hunters: { rank: -1, count: -1 }, 35 | bh_rogues: { rank: -1, count: -1 }, 36 | dominion_tower: { rank: 103693, count: 151380 }, 37 | the_crucible: { rank: -1, count: -1 }, 38 | castle_wars_games: { rank: -1, count: -1 }, 39 | ba_attackers: { rank: 120772, count: 542 }, 40 | ba_defenders: { rank: 70156, count: 662 }, 41 | ba_collectors: { rank: 108986, count: 530 }, 42 | ba_healers: { rank: 110711, count: 680 }, 43 | duel_tournament: { rank: -1, count: -1 }, 44 | mobilising_armies: { rank: -1, count: -1 }, 45 | conquest: { rank: -1, count: -1 }, 46 | fist_of_guthix: { rank: 47711, count: 1795 }, 47 | gg_resource_race: { rank: -1, count: -1 }, 48 | gg_athletics: { rank: -1, count: -1 }, 49 | we2_armadyl_lifetime_contribution: { rank: -1, count: -1 }, 50 | we2_bandos_lifetime_contribution: { rank: -1, count: -1 }, 51 | we2_armadyl_pvp_kills: { rank: -1, count: -1 }, 52 | we2_bandos_pvp_kills: { rank: -1, count: -1 }, 53 | heist_guard_level: { rank: -1, count: -1 }, 54 | heist_robber_level: { rank: -1, count: -1 }, 55 | cfp_5_game_average: { rank: -1, count: -1 }, 56 | af15_cow_tipping: { rank: -1, count: -1 }, 57 | af15_rats_killed_after_the_miniquest: { rank: -1, count: -1 }, 58 | runescore: { rank: 263993, count: 2940 }, 59 | clue_scrolls_easy: { rank: -1, count: -1 }, 60 | clue_scrolls_medium: { rank: -1, count: -1 }, 61 | clue_scrolls_hard: { rank: -1, count: -1 }, 62 | clue_scrolls_elite: { rank: -1, count: -1 }, 63 | clue_scrolls_master: { rank: -1, count: -1 } 64 | }, 65 | skills: { 66 | overall: { rank: 333178, level: 1929, experience: 73706113 }, 67 | attack: { rank: 355307, level: 85, experience: 3316903 }, 68 | defence: { rank: 345122, level: 85, experience: 3504461 }, 69 | strength: { rank: 363656, level: 87, experience: 3988528 }, 70 | constitution: { rank: 392483, level: 87, experience: 4177540 }, 71 | ranged: { rank: 403024, level: 79, experience: 1917280 }, 72 | prayer: { rank: 333545, level: 76, experience: 1388722 }, 73 | magic: { rank: 361077, level: 86, experience: 3933552 }, 74 | cooking: { rank: 426816, level: 73, experience: 1022402 }, 75 | woodcutting: { rank: 87660, level: 99, experience: 15060159 }, 76 | fletching: { rank: 131406, level: 99, experience: 13296413 }, 77 | fishing: { rank: 276013, level: 83, experience: 2725887 }, 78 | firemaking: { rank: 174632, level: 99, experience: 13160560 }, 79 | crafting: { rank: 406280, level: 69, experience: 716275 }, 80 | smithing: { rank: 483621, level: 65, experience: 474637 }, 81 | mining: { rank: 480847, level: 68, experience: 615516 }, 82 | herblore: { rank: 397209, level: 61, experience: 327273 }, 83 | agility: { rank: 431840, level: 60, experience: 275465 }, 84 | thieving: { rank: 381201, level: 61, experience: 313800 }, 85 | slayer: { rank: 388600, level: 69, experience: 717844 }, 86 | farming: { rank: 349770, level: 60, experience: 283189 }, 87 | runecrafting: { rank: 394485, level: 60, experience: 288191 }, 88 | hunter: { rank: 378823, level: 62, experience: 344831 }, 89 | construction: { rank: 406151, level: 60, experience: 274448 }, 90 | summoning: { rank: 383196, level: 62, experience: 356905 }, 91 | dungeoneering: { rank: 401470, level: 61, experience: 321961 }, 92 | divination: { rank: 286807, level: 72, experience: 903371 }, 93 | invention: { rank: -1, level: 0, experience: -1 } 94 | } 95 | } 96 | ``` 97 | {% endtab %} 98 | {% endtabs %} 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /docs/miscellaneous.md: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | 3 | ## Usage 4 | 5 | ```javascript 6 | import { miscellaneous } from "runescape-api" 7 | ``` 8 | 9 | ## Functions 10 | 11 | ### getAvatar\(:playerName\) 12 | 13 | > Retrieve a player's avatar URL 14 | 15 | | Parameter | Type | 16 | | :--- | :--- | 17 | | `playerName` | `string` | 18 | 19 | {% tabs %} 20 | {% tab title="JavaScript" %} 21 | ```javascript 22 | miscellaneous.getAvatar("Conundrum129").then(data => { 23 | console.log(data) 24 | }) 25 | ``` 26 | {% endtab %} 27 | 28 | {% tab title="Response" %} 29 | ``` 30 | https://secure.runescape.com/m=avatar-rs/avatar.png?id=18857418 31 | ``` 32 | {% endtab %} 33 | {% endtabs %} 34 | 35 | ### getTotalUsers\(\) 36 | 37 | > Retrieve the total registered RuneScape accounts 38 | 39 | {% tabs %} 40 | {% tab title="JavaScript" %} 41 | ```javascript 42 | miscellaneous.getTotalUsers().then(data => { 43 | console.log(data) 44 | }) 45 | ``` 46 | {% endtab %} 47 | 48 | {% tab title="Response" %} 49 | ``` 50 | 278497947 51 | ``` 52 | {% endtab %} 53 | {% endtabs %} 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /docs/osrs/README.md: -------------------------------------------------------------------------------- 1 | # Oldschool 2 | 3 | ## Usage 4 | 5 | ```javascript 6 | import { osrs } from "runescape-api" 7 | ``` 8 | 9 | -------------------------------------------------------------------------------- /docs/osrs/grandexchange.md: -------------------------------------------------------------------------------- 1 | # OSRS Grand Exchange 2 | 3 | ## Usage 4 | 5 | ```javascript 6 | import { grandexchange } from "runescape-api/osrs" 7 | ``` 8 | 9 | ## Functions 10 | 11 | ### getItem\(:id\) 12 | 13 | > Retrieve an item 14 | 15 | | Parameter | Type | 16 | | :--- | :--- | 17 | | `id` | `number` | 18 | 19 | {% tabs %} 20 | {% tab title="JavaScript" %} 21 | ```javascript 22 | grandexchange.getItem(4151).then(data => { 23 | console.log(data) 24 | }) 25 | ``` 26 | {% endtab %} 27 | 28 | {% tab title="Response" %} 29 | ```javascript 30 | Item { 31 | id: 4151, 32 | examine: 'A weapon from the abyss.', 33 | category: GrandExchangeCategory { id: -1, name: 'Default' }, 34 | members: true, 35 | icons: { 36 | default: 'http://services.runescape.com/m=itemdb_oldschool/1581593391039_obj_sprite.gif?id=4151', 37 | large: 'http://services.runescape.com/m=itemdb_oldschool/1581593391039_obj_big.gif?id=4151' 38 | }, 39 | trends: { 40 | current: { trend: 'neutral', price: '2.4m' }, 41 | today: { trend: 'negative', price: '- 4,721' }, 42 | day30: { trend: 'negative', change: '-10.0%' }, 43 | day90: { trend: 'negative', change: '-2.0%' }, 44 | day180: { trend: 'negative', change: '-4.0%' } 45 | } 46 | } 47 | ``` 48 | {% endtab %} 49 | {% endtabs %} 50 | 51 | ### getItemGraph\(:id\) 52 | 53 | > Retrieve an item's price history graph data 54 | 55 | | Parameter | Type | 56 | | :--- | :--- | 57 | | `id` | `number` | 58 | 59 | {% tabs %} 60 | {% tab title="JavaScript" %} 61 | ```javascript 62 | grandexchange.getItemGraph(4151).then(data => { 63 | console.log(data) 64 | }) 65 | ``` 66 | {% endtab %} 67 | 68 | {% tab title="Response" %} 69 | ```javascript 70 | ItemGraph { 71 | id: 4151, 72 | daily: { 73 | '1566172800000': 2549498, 74 | '1566259200000': 2544316, 75 | '1566345600000': 2544202, 76 | '1566432000000': 2543003, 77 | '1566518400000': 2539868, 78 | '1566604800000': 2537681, 79 | // ... 80 | }, 81 | average: { 82 | '1566172800000': 2534034, 83 | '1566259200000': 2533446, 84 | '1566345600000': 2532924, 85 | '1566432000000': 2532410, 86 | '1566518400000': 2532022, 87 | '1566604800000': 2531650, 88 | // ... 89 | } 90 | } 91 | ``` 92 | {% endtab %} 93 | {% endtabs %} 94 | 95 | -------------------------------------------------------------------------------- /docs/osrs/hiscores.md: -------------------------------------------------------------------------------- 1 | # OSRS Hiscores 2 | 3 | ## Usage 4 | 5 | ```javascript 6 | import { hiscores } from "runescape-api/osrs" 7 | ``` 8 | 9 | ## Functions 10 | 11 | ### getPlayer\(:playerName, :gamemode?\) 12 | 13 | > Retrieve a player 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 27 | 29 | 30 | 31 | 32 | 34 | 39 | 41 | 42 | 43 |
ParameterTypeDefault
playerName 26 | string 28 |
gamemode 33 | 35 |

"normal" | "ironman" | "hardcore" |

36 |

"ultimate" | "deadman" | "seasonal" 37 |

38 |
"normal" 40 |
{% tabs %} 44 | {% tab title="JavaScript" %} 45 | ```javascript 46 | hiscores.getPlayer("Paqt").then(data => { 47 | console.log(data) 48 | }) 49 | ``` 50 | {% endtab %} 51 | 52 | {% tab title="Response" %} 53 | ```javascript 54 | Player { 55 | name: 'Paqt', 56 | activities: { 57 | league_points: { rank: -1, count: -1 }, 58 | bounty_hunter_hunter: { rank: -1, count: -1 }, 59 | bounty_hunter_rogue: { rank: -1, count: -1 }, 60 | clue_scrolls_all: { rank: 288824, count: 53 }, 61 | clue_scrolls_beginner: { rank: -1, count: -1 }, 62 | clue_scrolls_easy: { rank: 548353, count: 3 }, 63 | clue_scrolls_medium: { rank: 384480, count: 7 }, 64 | clue_scrolls_hard: { rank: 205155, count: 37 }, 65 | clue_scrolls_elite: { rank: 158167, count: 5 }, 66 | clue_scrolls_master: { rank: 147219, count: 1 }, 67 | last_man_standing: { rank: -1, count: -1 } 68 | }, 69 | bosses: { 70 | abyssal_sire: { rank: -1, count: -1 }, 71 | alchemical_hydra: { rank: -1, count: -1 }, 72 | barrows_chests: { rank: 359209, count: 4 }, 73 | bryophyta: { rank: -1, count: -1 }, 74 | callisto: { rank: -1, count: -1 }, 75 | cerberus: { rank: -1, count: -1 }, 76 | chambers_of_xeric: { rank: -1, count: -1 }, 77 | chambers_of_xeric_challenge_mode: { rank: -1, count: -1 }, 78 | chaos_elemental: { rank: -1, count: -1 }, 79 | chaos_fanatic: { rank: -1, count: -1 }, 80 | commander_zilyana: { rank: -1, count: -1 }, 81 | corporeal_beast: { rank: -1, count: -1 }, 82 | crazy_archaeologist: { rank: -1, count: -1 }, 83 | dagannoth_prime: { rank: -1, count: -1 }, 84 | dagannoth_rex: { rank: -1, count: -1 }, 85 | dagannoth_supreme: { rank: -1, count: -1 }, 86 | deranged_archaeologist: { rank: -1, count: -1 }, 87 | general_graardor: { rank: -1, count: -1 }, 88 | giant_mole: { rank: 198700, count: 1 }, 89 | grotesque_guardians: { rank: -1, count: -1 }, 90 | hespori: { rank: 106376, count: 10 }, 91 | kalphite_queen: { rank: -1, count: -1 }, 92 | king_black_dragon: { rank: -1, count: -1 }, 93 | kraken: { rank: -1, count: -1 }, 94 | kreearra: { rank: -1, count: -1 }, 95 | kril_tsutsaroth: { rank: -1, count: -1 }, 96 | mimic: { rank: -1, count: -1 }, 97 | nightmare: { rank: -1, count: -1 }, 98 | obor: { rank: -1, count: -1 }, 99 | sarachnis: { rank: -1, count: -1 }, 100 | scorpia: { rank: -1, count: -1 }, 101 | skotizo: { rank: 207521, count: 2 }, 102 | the_gauntlet: { rank: -1, count: -1 }, 103 | the_corrupted_gauntlet: { rank: -1, count: -1 }, 104 | theatre_of_blood: { rank: -1, count: -1 }, 105 | thermonuclear_smoke_devil: { rank: -1, count: -1 }, 106 | tzkal_zuk: { rank: -1, count: -1 }, 107 | tztok_jad: { rank: 108415, count: 2 }, 108 | venenatis: { rank: -1, count: -1 }, 109 | vetion: { rank: -1, count: -1 }, 110 | vorkath: { rank: 46942, count: 408 }, 111 | wintertodt: { rank: 447700, count: 1 }, 112 | zalcano: { rank: -1, count: -1 }, 113 | zulrah: { rank: 191518, count: 1 } 114 | }, 115 | skills: { 116 | overall: { rank: 207774, level: 1811, experience: 65850144 }, 117 | attack: { rank: 254027, level: 90, experience: 5549049 }, 118 | defence: { rank: 224846, level: 90, experience: 5514750 }, 119 | strength: { rank: 324177, level: 94, experience: 8296000 }, 120 | constitution: { rank: 306887, level: 95, experience: 8791927 }, 121 | ranged: { rank: 331625, level: 92, experience: 6650862 }, 122 | prayer: { rank: 182148, level: 77, experience: 1531254 }, 123 | magic: { rank: 521879, level: 83, experience: 2715672 }, 124 | cooking: { rank: 560622, level: 70, experience: 794196 }, 125 | woodcutting: { rank: 445155, level: 73, experience: 1007131 }, 126 | fletching: { rank: 160389, level: 90, experience: 5406675 }, 127 | fishing: { rank: 758917, level: 63, experience: 390135 }, 128 | firemaking: { rank: 561724, level: 66, experience: 527722 }, 129 | crafting: { rank: 232208, level: 75, experience: 1273183 }, 130 | smithing: { rank: 324435, level: 70, experience: 786998 }, 131 | mining: { rank: 250574, level: 74, experience: 1157311 }, 132 | herblore: { rank: 65449, level: 90, experience: 5367034 }, 133 | agility: { rank: 303024, level: 71, experience: 874126 }, 134 | thieving: { rank: 275437, level: 70, experience: 778913 }, 135 | slayer: { rank: 351633, level: 78, experience: 1779777 }, 136 | farming: { rank: 167858, level: 83, experience: 2896562 }, 137 | runecrafting: { rank: 175724, level: 65, experience: 485273 }, 138 | hunter: { rank: 289379, level: 70, experience: 758356 }, 139 | construction: { rank: 157046, level: 82, experience: 2517238 } 140 | } 141 | } 142 | ``` 143 | {% endtab %} 144 | {% endtabs %} 145 | 146 | -------------------------------------------------------------------------------- /docs/runemetrics.md: -------------------------------------------------------------------------------- 1 | # RuneMetrics 2 | 3 | ## Usage 4 | 5 | ```javascript 6 | import { runemetrics } from "runescape-api" 7 | ``` 8 | 9 | ## Functions 10 | 11 | ### getMonthlyXp\(:playerName, :skillId\) 12 | 13 | > Retrieve a user's monthly experience gained in a given skill 14 | 15 | | Parameter | Type | 16 | | :--- | :--- | 17 | | `playerName` | `string` | 18 | | `skillId` | `number` \| `Skill` | 19 | 20 | {% tabs %} 21 | {% tab title="JavaScript" %} 22 | ```javascript 23 | runemetrics.getMonthlyXp("Conundrum129", 19).then(data => { 24 | console.log(data) 25 | }) 26 | ``` 27 | {% endtab %} 28 | 29 | {% tab title="Response" %} 30 | ```javascript 31 | RuneMetricsMonthlyExperience { 32 | skill: Skill { id: 19, name: 'slayer' }, 33 | totalExperience: 30150355, 34 | totalGain: 13802938, 35 | monthData: [ 36 | { xpGain: 290353, timestamp: 1553986429592, rank: 59668 }, 37 | { xpGain: 341001, timestamp: 1556575611538, rank: 54255 }, 38 | { xpGain: 1341256, timestamp: 1559251411801, rank: 32790 }, 39 | { xpGain: 1802315, timestamp: 1561848075528, rank: 28989 }, 40 | { xpGain: 1988909, timestamp: 1564523768740, rank: 28842 }, 41 | { xpGain: 1932892, timestamp: 1567196221990, rank: 27813 }, 42 | { xpGain: 1401914, timestamp: 1569787993025, rank: 25709 }, 43 | { xpGain: 1021838, timestamp: 1572466642590, rank: 24056 }, 44 | { xpGain: 870090, timestamp: 1575064444827, rank: 31899 }, 45 | { xpGain: 1011392, timestamp: 1577743934977, rank: 38277 }, 46 | { xpGain: 1140432, timestamp: 1580424470870, rank: 35616 }, 47 | { xpGain: 660546, timestamp: 1581548496021, rank: 31331 } 48 | ] 49 | } 50 | ``` 51 | {% endtab %} 52 | {% endtabs %} 53 | 54 | ### getProfile\(:playerName\) 55 | 56 | > Retrieve a user's RuneMetrics profile name 57 | 58 | | Parameter | Type | 59 | | :--- | :--- | 60 | | `playerName` | `string` | 61 | 62 | {% tabs %} 63 | {% tab title="JavaScript" %} 64 | ```javascript 65 | runemetrics.getProfile("Paqt").then(data => { 66 | console.log(data) 67 | }) 68 | ``` 69 | {% endtab %} 70 | 71 | {% tab title="Response" %} 72 | ```javascript 73 | RuneMetricsProfile { 74 | name: 'Paqt', 75 | combatLevel: 116, 76 | experience_distribution: { magic: 1388722, melee: 38605287, ranged: 4177540 }, 77 | overall: { rank: 333433, level: 1929, experience: 73706113 }, 78 | skills: { 79 | overall: { rank: 355648, level: 85, experience: 33169037 }, 80 | attack: { rank: 345457, level: 85, experience: 35044619 }, 81 | defence: { rank: 364015, level: 87, experience: 39885284 }, 82 | strength: { rank: 392848, level: 87, experience: 41775402 }, 83 | constitution: { rank: 403383, level: 79, experience: 19172809 }, 84 | ranged: { rank: 333802, level: 76, experience: 13887225 }, 85 | prayer: { rank: 361412, level: 86, experience: 39335527 }, 86 | magic: { rank: 427228, level: 73, experience: 10224021 }, 87 | cooking: { rank: 87705, level: 99, experience: 150601597 }, 88 | woodcutting: { rank: 131492, level: 99, experience: 132964135 }, 89 | fletching: { rank: 276258, level: 83, experience: 27258878 }, 90 | fishing: { rank: 174737, level: 99, experience: 131605606 }, 91 | firemaking: { rank: 406598, level: 69, experience: 7162753 }, 92 | crafting: { rank: 484064, level: 65, experience: 4746375 }, 93 | smithing: { rank: 481099, level: 68, experience: 6155160 }, 94 | mining: { rank: 397512, level: 61, experience: 3272737 }, 95 | herblore: { rank: 432198, level: 60, experience: 2754656 }, 96 | agility: { rank: 381537, level: 61, experience: 3138007 }, 97 | thieving: { rank: 388917, level: 69, experience: 7178440 }, 98 | slayer: { rank: 350054, level: 60, experience: 2831891 }, 99 | farming: { rank: 394855, level: 60, experience: 2881919 }, 100 | runecrafting: { rank: 379164, level: 62, experience: 3448319 }, 101 | hunter: { rank: 406448, level: 60, experience: 2744480 }, 102 | construction: { rank: 383513, level: 62, experience: 3569058 }, 103 | summoning: { rank: 401807, level: 61, experience: 3219611 }, 104 | dungeoneering: { rank: 286981, level: 72, experience: 9033714 }, 105 | divination: { rank: 0, level: 1, experience: 0 }, 106 | invention: { rank: 0, level: 1, experience: 0 } 107 | }, 108 | quests: { complete: 132, started: 18, not_started: 144 }, 109 | activities: [ 110 | { 111 | title: 'Visited my Clan Citadel.', 112 | description: 'I have visited my Clan Citadel this week.', 113 | date: '04-Feb-2020 01:13' 114 | }, 115 | { 116 | title: 'Visited my Clan Citadel.', 117 | description: 'I have visited my Clan Citadel this week.', 118 | date: '24-Jan-2020 21:12' 119 | }, 120 | { 121 | title: 'Visited my Clan Citadel.', 122 | description: 'I have visited my Clan Citadel this week.', 123 | date: '17-Jul-2019 07:04' 124 | }, 125 | { 126 | title: 'Levelled up Herblore.', 127 | description: 'I levelled my Herblore skill, I am now level 61.', 128 | date: '17-Jul-2019 07:04' 129 | }, 130 | { 131 | title: 'Visited my Clan Citadel.', 132 | description: 'I have visited my Clan Citadel this week.', 133 | date: '26-Jun-2019 04:41' 134 | }, 135 | { 136 | title: 'Visited my Clan Citadel.', 137 | description: 'I have visited my Clan Citadel this week.', 138 | date: '20-Jun-2019 01:10' 139 | }, 140 | { 141 | title: 'Visited my Clan Citadel.', 142 | description: 'I have visited my Clan Citadel this week.', 143 | date: '05-Jun-2019 04:34' 144 | }, 145 | { 146 | title: 'Visited my Clan Citadel.', 147 | description: 'I have visited my Clan Citadel this week.', 148 | date: '07-Feb-2019 04:27' 149 | }, 150 | { 151 | title: 'Large amount of coins obtained.', 152 | description: 'I won a large amount of coins on Treasure Hunter!', 153 | date: '07-Feb-2019 04:24' 154 | }, 155 | { 156 | title: 'Visited my Clan Citadel.', 157 | description: 'I have visited my Clan Citadel this week.', 158 | date: '30-Jan-2019 05:40' 159 | }, 160 | { 161 | title: 'Visited my Clan Citadel.', 162 | description: 'I have visited my Clan Citadel this week.', 163 | date: '03-Jan-2019 01:47' 164 | }, 165 | { 166 | title: 'Visited my Clan Citadel.', 167 | description: 'I have visited my Clan Citadel this week.', 168 | date: '26-Dec-2018 10:22' 169 | }, 170 | { 171 | title: 'Visited my Clan Citadel.', 172 | description: 'I have visited my Clan Citadel this week.', 173 | date: '04-Dec-2018 04:14' 174 | }, 175 | { 176 | title: 'Visited my Clan Citadel.', 177 | description: 'I have visited my Clan Citadel this week.', 178 | date: '28-Nov-2018 05:31' 179 | }, 180 | { 181 | title: 'Levelled up Mining.', 182 | description: 'I levelled my Mining skill, I am now level 68.', 183 | date: '08-Nov-2018 07:58' 184 | }, 185 | { 186 | title: 'Visited my Clan Citadel.', 187 | description: 'I have visited my Clan Citadel this week.', 188 | date: '08-Nov-2018 07:43' 189 | }, 190 | { 191 | title: 'Levelled up Summoning.', 192 | description: 'I levelled my Summoning skill, I am now level 62.', 193 | date: '14-Oct-2014 12:51' 194 | }, 195 | { 196 | title: 'Levelled up Hunter.', 197 | description: 'I levelled my Hunter skill, I am now level 62.', 198 | date: '13-Oct-2014 00:30' 199 | }, 200 | { 201 | title: 'Levelled up Crafting.', 202 | description: 'I levelled my Crafting skill, I am now level 69.', 203 | date: '13-Oct-2014 00:29' 204 | }, 205 | { 206 | title: 'Lucky Armadyl crossbow obtained.', 207 | description: 'I won a Lucky Armadyl crossbow on Treasure Hunter!', 208 | date: '10-Aug-2014 01:50' 209 | } 210 | ] 211 | } 212 | ``` 213 | {% endtab %} 214 | {% endtabs %} 215 | 216 | ### getQuests\(:playerName\) 217 | 218 | > Retrieve a user's quest list with completion status' 219 | 220 | | Parameter | Type | 221 | | :--- | :--- | 222 | | `playerName` | `string` | 223 | 224 | {% tabs %} 225 | {% tab title="JavaScript" %} 226 | ```javascript 227 | runemetrics.getQuests("Paqt").then(data => { 228 | console.log(data) 229 | }) 230 | ``` 231 | {% endtab %} 232 | 233 | {% tab title="Response" %} 234 | ```javascript 235 | [ 236 | Quest { 237 | name: 'A Fairy Tale I - Growing Pains', 238 | status: 'COMPLETED', 239 | difficulty: 2, 240 | members: true, 241 | questPoints: 2, 242 | eligible: true 243 | }, 244 | Quest { 245 | name: 'A Fairy Tale II - Cure a Queen', 246 | status: 'COMPLETED', 247 | difficulty: 2, 248 | members: true, 249 | questPoints: 2, 250 | eligible: true 251 | }, 252 | Quest { 253 | name: "A Fairy Tale III - Battle at Ork's Rift", 254 | status: 'COMPLETED', 255 | difficulty: 2, 256 | members: true, 257 | questPoints: 2, 258 | eligible: true 259 | }, 260 | Quest { 261 | name: "A Soul's Bane", 262 | status: 'COMPLETED', 263 | difficulty: 0, 264 | members: false, 265 | questPoints: 1, 266 | eligible: true 267 | }, 268 | Quest { 269 | name: 'A Tail of Two Cats', 270 | status: 'COMPLETED', 271 | difficulty: 1, 272 | members: true, 273 | questPoints: 2, 274 | eligible: true 275 | }, 276 | // ... 200+ more items 277 | ] 278 | ``` 279 | {% endtab %} 280 | {% endtabs %} 281 | 282 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "runescape-api", 3 | "version": "2.1.6", 4 | "description": "Simple wrapper for RuneScape APIs written in node.", 5 | "author": "Joshua Filby ", 6 | "maintainers": [ 7 | "Astrect (https://astrect.me)" 8 | ], 9 | "keywords": [ 10 | "Oldschool", 11 | "Old School", 12 | "Runescape", 13 | "OSRS", 14 | "2007scape", 15 | "Runescape 3" 16 | ], 17 | "main": "./index.js", 18 | "types": "./index.d.ts", 19 | "scripts": { 20 | "build": "rimraf build && tsc", 21 | "commit": "npx git-cz@4.2.0", 22 | "cz": "npm run commit", 23 | "dev": "rimraf build && tsc --watch", 24 | "format": "prettier ./{src,__{tests,mocks}__}/{**,**/**}/*.{js,jsx,ts,tsx} --write", 25 | "lint": "eslint src --ext=\".js,.jsx,.ts,.tsx\"", 26 | "test": "jest" 27 | }, 28 | "dependencies": { 29 | "got": "^11.8.2", 30 | "slugify": "^1.5.0" 31 | }, 32 | "devDependencies": { 33 | "@types/jest": "^26.0.23", 34 | "@typescript-eslint/eslint-plugin": "^4.22.0", 35 | "@typescript-eslint/parser": "^4.22.0", 36 | "eslint": "^7.25.0", 37 | "eslint-config-prettier": "^8.3.0", 38 | "jest": "^26.6.3", 39 | "prettier": "^2.2.1", 40 | "rimraf": "^3.0.2", 41 | "ts-jest": "^26.5.5", 42 | "typescript": "^4.2.4" 43 | }, 44 | "repository": { 45 | "type": "git", 46 | "url": "https://github.com/astrect/runescape-api" 47 | }, 48 | "bugs": { 49 | "url": "https://github.com/astrect/runescape-api/issues" 50 | }, 51 | "jest": { 52 | "roots": [ 53 | "/src" 54 | ], 55 | "testMatch": [ 56 | "**/__tests__/**/*.+(ts|tsx|js)", 57 | "**/?(*.)+(spec|test).+(ts|tsx|js)" 58 | ], 59 | "transform": { 60 | "^.+\\.(ts|tsx)$": "ts-jest" 61 | } 62 | }, 63 | "license": "MIT" 64 | } 65 | -------------------------------------------------------------------------------- /scripts/build_and_move: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # generate the build directory 5 | npm run build 6 | 7 | # move files from the build directory to project root 8 | mv ./build/* ./ 9 | -------------------------------------------------------------------------------- /src/configs/oldschool.ts: -------------------------------------------------------------------------------- 1 | export const hiscores = { 2 | endpoints: { 3 | normal: `http://services.runescape.com/m=hiscore_oldschool/index_lite.ws?player=`, 4 | ironman: `http://services.runescape.com/m=hiscore_oldschool_ironman/index_lite.ws?player=`, 5 | hardcore: `http://services.runescape.com/m=hiscore_oldschool_hardcore_ironman/index_lite.ws?player=`, 6 | ultimate: `http://services.runescape.com/m=hiscore_oldschool_ultimate/index_lite.ws?player=`, 7 | deadman: `http://services.runescape.com/m=hiscore_oldschool_deadman/index_lite.ws?player=`, 8 | seasonal: `http://services.runescape.com/m=hiscore_oldschool_seasonal/index_lite.ws?player=`, 9 | }, 10 | activities: [ 11 | "league_points", 12 | "bounty_hunter_hunter", 13 | "bounty_hunter_rogue", 14 | "clue_scrolls_all", 15 | "clue_scrolls_beginner", 16 | "clue_scrolls_easy", 17 | "clue_scrolls_medium", 18 | "clue_scrolls_hard", 19 | "clue_scrolls_elite", 20 | "clue_scrolls_master", 21 | "last_man_standing", 22 | ] as const, 23 | bosses: [ 24 | "abyssal_sire", 25 | "alchemical_hydra", 26 | "barrows_chests", 27 | "bryophyta", 28 | "callisto", 29 | "cerberus", 30 | "chambers_of_xeric", 31 | "chambers_of_xeric_challenge_mode", 32 | "chaos_elemental", 33 | "chaos_fanatic", 34 | "commander_zilyana", 35 | "corporeal_beast", 36 | "crazy_archaeologist", 37 | "dagannoth_prime", 38 | "dagannoth_rex", 39 | "dagannoth_supreme", 40 | "deranged_archaeologist", 41 | "general_graardor", 42 | "giant_mole", 43 | "grotesque_guardians", 44 | "hespori", 45 | "kalphite_queen", 46 | "king_black_dragon", 47 | "kraken", 48 | "kreearra", 49 | "kril_tsutsaroth", 50 | "mimic", 51 | "nightmare", 52 | "obor", 53 | "sarachnis", 54 | "scorpia", 55 | "skotizo", 56 | "the_gauntlet", 57 | "the_corrupted_gauntlet", 58 | "theatre_of_blood", 59 | "thermonuclear_smoke_devil", 60 | "tzkal_zuk", 61 | "tztok_jad", 62 | "venenatis", 63 | "vetion", 64 | "vorkath", 65 | "wintertodt", 66 | "zalcano", 67 | "zulrah", 68 | ] as const, 69 | gamemodes: [ 70 | "normal", 71 | "ironman", 72 | "hardcore", 73 | "ultimate", 74 | "deadman", 75 | "seasonal", 76 | ] as const, 77 | skills: [ 78 | "overall", 79 | "attack", 80 | "defence", 81 | "strength", 82 | "constitution", 83 | "ranged", 84 | "prayer", 85 | "magic", 86 | "cooking", 87 | "woodcutting", 88 | "fletching", 89 | "fishing", 90 | "firemaking", 91 | "crafting", 92 | "smithing", 93 | "mining", 94 | "herblore", 95 | "agility", 96 | "thieving", 97 | "slayer", 98 | "farming", 99 | "runecrafting", 100 | "hunter", 101 | "construction", 102 | ] as const, 103 | } 104 | export const grandexchange = { 105 | endpoints: { 106 | category: `http://services.runescape.com/m=itemdb_oldschool/api/catalogue/category.json?category=`, 107 | categoryPrice: `http://services.runescape.com/m=itemdb_oldschool/api/catalogue/items.json?`, 108 | item: `http://services.runescape.com/m=itemdb_oldschool/api/catalogue/detail.json?item=`, 109 | itemGraph: `http://services.runescape.com/m=itemdb_oldschool/api/graph`, 110 | }, 111 | } 112 | 113 | export default { 114 | hiscores, 115 | grandexchange, 116 | } 117 | -------------------------------------------------------------------------------- /src/configs/runescape.ts: -------------------------------------------------------------------------------- 1 | export const bestiary = { 2 | endpoints: { 3 | beast: `http://services.runescape.com/m=itemdb_rs/bestiary/beastData.json?beastid=`, 4 | beastTerm: `http://services.runescape.com/m=itemdb_rs/bestiary/beastSearch.json?term=`, 5 | beastLetter: `http://services.runescape.com/m=itemdb_rs/bestiary/bestiaryNames.json?letter=`, 6 | areas: `http://services.runescape.com/m=itemdb_rs/bestiary/areaNames.json`, 7 | beastArea: `http://services.runescape.com/m=itemdb_rs/bestiary/areaBeasts.json?identifier=`, 8 | beastSlayer: `http://services.runescape.com/m=itemdb_rs/bestiary/slayerBeasts.json?identifier=`, 9 | beastWeakness: `http://services.runescape.com/m=itemdb_rs/bestiary/weaknessBeasts.json?identifier=`, 10 | beastLevel: `http://services.runescape.com/m=itemdb_rs/bestiary/levelGroup.json?identifier=`, 11 | slayerCategories: `http://services.runescape.com/m=itemdb_rs/bestiary/bestiary/slayerCatNames.json`, 12 | weaknesses: `http://services.runescape.com/m=itemdb_rs/bestiary/bestiary/weaknessNames.json`, 13 | }, 14 | searchMethods: [ 15 | "terms", 16 | "firstLetter", 17 | "area", 18 | "slayerCategory", 19 | "weakness", 20 | "levelRange", 21 | ] as const, 22 | slayerCategories: [ 23 | { id: 41, name: "Aberrant spectres" }, 24 | { id: 42, name: "Abyssal demons" }, 25 | { id: 133, name: "Acheron mammoths" }, 26 | { id: 127, name: "Adamant dragons" }, 27 | { id: 117, name: "Airut" }, 28 | { id: 79, name: "Ankou" }, 29 | { id: 95, name: "Aquanites" }, 30 | { id: 115, name: "Ascension members" }, 31 | { id: 114, name: "Aviansies" }, 32 | { id: 38, name: "Banshees" }, 33 | { id: 43, name: "Basilisks" }, 34 | { id: 8, name: "Bats" }, 35 | { id: 13, name: "Bears" }, 36 | { id: 5, name: "Birds" }, 37 | { id: 30, name: "Black demons" }, 38 | { id: 27, name: "Black dragons" }, 39 | { id: 48, name: "Bloodveld" }, 40 | { id: 25, name: "Blue dragons" }, 41 | { id: 84, name: "Brine rats" }, 42 | { id: 58, name: "Bronze dragons" }, 43 | { id: 132, name: "Camel warriors" }, 44 | { id: 78, name: "Catablepon" }, 45 | { id: 63, name: "Cave bugs" }, 46 | { id: 37, name: "Cave crawlers" }, 47 | { id: 80, name: "Cave horrors" }, 48 | { id: 62, name: "Cave slimes" }, 49 | { id: 118, name: "Celestial dragons" }, 50 | { id: 134, name: "Chaos giants" }, 51 | { id: 44, name: "Cockatrice" }, 52 | { id: 152, name: "Corrupted creatures" }, 53 | { id: 143, name: "Corrupted dust devils" }, 54 | { id: 144, name: "Corrupted kalphites" }, 55 | { id: 142, name: "Corrupted lizards" }, 56 | { id: 141, name: "Corrupted scarabs" }, 57 | { id: 140, name: "Corrupted scorpions" }, 58 | { id: 145, name: "Corrupted worker" }, 59 | { id: 6, name: "Cows" }, 60 | { id: 39, name: "Crawling hands" }, 61 | { id: 175, name: "Creatures of Daemonheim" }, 62 | { id: 154, name: "Creatures of the Lost Grove" }, 63 | { id: 113, name: "Cres's creations" }, 64 | { id: 149, name: "Crocodile akh" }, 65 | { id: 65, name: "Crocodiles" }, 66 | { id: 129, name: "Crystal shapeshifters" }, 67 | { id: 108, name: "Cyclopes" }, 68 | { id: 35, name: "Dagannoth" }, 69 | { id: 66, name: "Dark beasts" }, 70 | { id: 174, name: "Demons" }, 71 | { id: 68, name: "Desert lizards" }, 72 | { id: 104, name: "Desert strykewyrms" }, 73 | { id: 171, name: "Dinosaurs" }, 74 | { id: 22, name: "Dogs" }, 75 | { id: 173, name: "Dragons" }, 76 | { id: 49, name: "Dust devils" }, 77 | { id: 57, name: "Dwarves" }, 78 | { id: 54, name: "Earth warriors" }, 79 | { id: 124, name: "Edimmu" }, 80 | { id: 56, name: "Elves" }, 81 | { id: 147, name: "Feline akh" }, 82 | { id: 69, name: "Fever spiders" }, 83 | { id: 16, name: "Fire giants" }, 84 | { id: 77, name: "Fleshcrawlers" }, 85 | { id: 162, name: "Frogs" }, 86 | { id: 99, name: "Fungal magi" }, 87 | { id: 96, name: "Ganodermic creatures" }, 88 | { id: 46, name: "Gargoyles" }, 89 | { id: 111, name: "Gelatinous abominations" }, 90 | { id: 139, name: "Gemstone dragons" }, 91 | { id: 12, name: "Ghosts" }, 92 | { id: 23, name: "Ghouls" }, 93 | { id: 122, name: "Glacors" }, 94 | { id: 2, name: "Goblins" }, 95 | { id: 82, name: "Goraks" }, 96 | { id: 148, name: "Gorilla akh" }, 97 | { id: 29, name: "Greater demons" }, 98 | { id: 24, name: "Green dragons" }, 99 | { id: 97, name: "Grifolapines" }, 100 | { id: 98, name: "Grifolaroos" }, 101 | { id: 112, name: "Grotworms" }, 102 | { id: 70, name: "Harpie bug swarms" }, 103 | { id: 31, name: "Hellhounds" }, 104 | { id: 14, name: "Hill giants" }, 105 | { id: 21, name: "Hobgoblins" }, 106 | { id: 15, name: "Ice giants" }, 107 | { id: 105, name: "Ice strykewyrms" }, 108 | { id: 19, name: "Ice warriors" }, 109 | { id: 75, name: "Icefiends" }, 110 | { id: 151, name: "Imperial guard akh" }, 111 | { id: 40, name: "Infernal mages" }, 112 | { id: 59, name: "Iron dragons" }, 113 | { id: 50, name: "Jellies" }, 114 | { id: 81, name: "Jungle horrors" }, 115 | { id: 103, name: "Jungle strykewyrms" }, 116 | { id: 121, name: "Kal'gerion demons" }, 117 | { id: 53, name: "Kalphite" }, 118 | { id: 73, name: "Killerwatts" }, 119 | { id: 45, name: "Kurask" }, 120 | { id: 126, name: "Lava strykewyrms" }, 121 | { id: 28, name: "Lesser demons" }, 122 | { id: 106, name: "Living rock creatures" }, 123 | { id: 130, name: "Living wyverns" }, 124 | { id: 76, name: "Minotaurs" }, 125 | { id: 94, name: "Mithril dragons" }, 126 | { id: 67, name: "Mogres" }, 127 | { id: 87, name: "Molanisks" }, 128 | { id: 1, name: "Monkeys" }, 129 | { id: 17, name: "Moss giants" }, 130 | { id: 119, name: "Muspah" }, 131 | { id: 109, name: "Mutated jadinkos" }, 132 | { id: 74, name: "Mutated zygomites" }, 133 | { id: 52, name: "Nechryael" }, 134 | { id: 135, name: "Nightmare creatures" }, 135 | { id: 120, name: "Nihil" }, 136 | { id: 20, name: "Ogres" }, 137 | { id: 55, name: "Otherworldly beings" }, 138 | { id: 116, name: "Pigs" }, 139 | { id: 100, name: "Polypore creatures" }, 140 | { id: 47, name: "Pyrefiends" }, 141 | { id: 3, name: "Rats" }, 142 | { id: 26, name: "Red dragons" }, 143 | { id: 161, name: "Revenants" }, 144 | { id: 131, name: "Ripper demons" }, 145 | { id: 51, name: "Rockslugs" }, 146 | { id: 128, name: "Rune dragons" }, 147 | { id: 146, name: "Salawa akh" }, 148 | { id: 85, name: "Scabarites" }, 149 | { id: 150, name: "Scarab akh" }, 150 | { id: 7, name: "Scorpions" }, 151 | { id: 71, name: "Sea snakes" }, 152 | { id: 64, name: "Shades" }, 153 | { id: 125, name: "Shadow creatures" }, 154 | { id: 32, name: "Shadow warriors" }, 155 | { id: 72, name: "Skeletal wyverns" }, 156 | { id: 11, name: "Skeletons" }, 157 | { id: 153, name: "Soul devourers" }, 158 | { id: 4, name: "Spiders" }, 159 | { id: 91, name: "Spiritual mages" }, 160 | { id: 90, name: "Spiritual rangers" }, 161 | { id: 89, name: "Spiritual warriors" }, 162 | { id: 160, name: "Stalker creatures" }, 163 | { id: 60, name: "Steel dragons" }, 164 | { id: 177, name: "Strykewyrms" }, 165 | { id: 83, name: "Suqahs" }, 166 | { id: 86, name: "Terror dogs" }, 167 | { id: 123, name: "Tormented demons" }, 168 | { id: 18, name: "Trolls" }, 169 | { id: 36, name: "Turoth" }, 170 | { id: 101, name: "TzHaar" }, 171 | { id: 34, name: "Vampyres" }, 172 | { id: 172, name: "Vile blooms" }, 173 | { id: 102, name: "Volcanic creatures" }, 174 | { id: 110, name: "Vyrewatch" }, 175 | { id: 61, name: "Wall beasts" }, 176 | { id: 93, name: "Warped terrorbirds" }, 177 | { id: 92, name: "Warped tortoises" }, 178 | { id: 88, name: "Waterfiends" }, 179 | { id: 33, name: "Werewolves" }, 180 | { id: 9, name: "Wolves" }, 181 | { id: 176, name: "Zarosian creatures" }, 182 | { id: 10, name: "Zombies" }, 183 | ] as const, 184 | weaknesses: [ 185 | "None", 186 | "Air", 187 | "Water", 188 | "Earth", 189 | "Fire", 190 | "Stabbing", 191 | "Slashing", 192 | "Crushing", 193 | "Arrow", 194 | "Bolt", 195 | "Thrown", 196 | ] as const, 197 | } 198 | export const clan = { 199 | endpoints: { 200 | members: `http://services.runescape.com/m=clan-hiscores/members_lite.ws`, 201 | }, 202 | } 203 | export const grandexchange = { 204 | endpoints: { 205 | category: `http://services.runescape.com/m=itemdb_rs/api/catalogue/category.json?category=`, 206 | categoryItems: `http://services.runescape.com/m=itemdb_rs/api/catalogue/items.json?`, 207 | item: `http://services.runescape.com/m=itemdb_rs/api/catalogue/detail.json?item=`, 208 | itemGraph: `http://services.runescape.com/m=itemdb_rs/api/graph`, 209 | }, 210 | categories: [ 211 | "Miscellaneous", 212 | "Ammo", 213 | "Arrows", 214 | "Bolts", 215 | "Construction materials", 216 | "Construction projects", 217 | "Cooking ingredients", 218 | "Costumes", 219 | "Crafting materials", 220 | "Familiars", 221 | "Farming produce", 222 | "Fletching materials", 223 | "Food and drink", 224 | "Herblore materials", 225 | "Hunting equipment", 226 | "Hunting produce", 227 | "Jewellery", 228 | "Mage armour", 229 | "Mage weapons", 230 | "Melee armour - low level", 231 | "Melee armour - mid level", 232 | "Melee armour - high level", 233 | "Melee weapons - low level", 234 | "Melee weapons - mid level", 235 | "Melee weapons - high level", 236 | "Mining and smithing", 237 | "Potions", 238 | "Prayer armour", 239 | "Prayer materials", 240 | "Range armour", 241 | "Range weapons", 242 | "Runecrafting", 243 | "Runes, Spells and Teleports", 244 | "Seeds", 245 | "Summoning scrolls", 246 | "Tools and containers", 247 | "Woodcutting product", 248 | "Pocket items", 249 | ] as const, 250 | } 251 | export const hiscores = { 252 | endpoints: { 253 | normal: `http://services.runescape.com/m=hiscore/index_lite.ws`, 254 | ironman: `http://services.runescape.com/m=hiscore_ironman/index_lite.ws`, 255 | hardcore: `http://services.runescape.com/m=hiscore_hardcore_ironman/index_lite.ws`, 256 | }, 257 | activities: [ 258 | "bounty_hunters", 259 | "bh_rogues", 260 | "dominion_tower", 261 | "the_crucible", 262 | "castle_wars_games", 263 | "ba_attackers", 264 | "ba_defenders", 265 | "ba_collectors", 266 | "ba_healers", 267 | "duel_tournament", 268 | "mobilising_armies", 269 | "conquest", 270 | "fist_of_guthix", 271 | "gg_resource_race", 272 | "gg_athletics", 273 | "we2_armadyl_lifetime_contribution", 274 | "we2_bandos_lifetime_contribution", 275 | "we2_armadyl_pvp_kills", 276 | "we2_bandos_pvp_kills", 277 | "heist_guard_level", 278 | "heist_robber_level", 279 | "cfp_5_game_average", 280 | "af15_cow_tipping", 281 | "af15_rats_killed_after_the_miniquest", 282 | "runescore", 283 | "clue_scrolls_easy", 284 | "clue_scrolls_medium", 285 | "clue_scrolls_hard", 286 | "clue_scrolls_elite", 287 | "clue_scrolls_master", 288 | ] as const, 289 | gamemodes: ["normal", "ironman", "hardcore"] as const, 290 | skills: [ 291 | "overall", 292 | "attack", 293 | "defence", 294 | "strength", 295 | "constitution", 296 | "ranged", 297 | "prayer", 298 | "magic", 299 | "cooking", 300 | "woodcutting", 301 | "fletching", 302 | "fishing", 303 | "firemaking", 304 | "crafting", 305 | "smithing", 306 | "mining", 307 | "herblore", 308 | "agility", 309 | "thieving", 310 | "slayer", 311 | "farming", 312 | "runecrafting", 313 | "hunter", 314 | "construction", 315 | "summoning", 316 | "dungeoneering", 317 | "divination", 318 | "invention", 319 | "archaeology", 320 | ] as const, 321 | } 322 | export const miscellaneous = { 323 | endpoints: { 324 | defaultAvatarUrl: `https://secure.runescape.com/m=avatar-rs/default_chat.png?`, 325 | totalUsers: `https://secure.runescape.com/m=account-creation-reports/rsusertotal.ws`, 326 | }, 327 | } 328 | export const runemetrics = { 329 | endpoints: { 330 | profile: `https://apps.runescape.com/runemetrics/profile/profile`, 331 | monthlyXp: `https://apps.runescape.com/runemetrics/xp-monthly`, 332 | quests: `https://apps.runescape.com/runemetrics/quests`, 333 | }, 334 | questStatuses: ["NOT_STARTED", "STARTED", "COMPLETED"] as const, 335 | } 336 | 337 | export default { 338 | bestiary, 339 | clan, 340 | grandexchange, 341 | hiscores, 342 | runemetrics, 343 | miscellaneous, 344 | } 345 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import * as osrs from "./osrs" 2 | import * as bestiary from "./runescape/bestiary" 3 | import * as clan from "./runescape/clan" 4 | import * as grandexchange from "./runescape/grandexchange" 5 | import * as hiscores from "./runescape/hiscores" 6 | import * as miscellaneous from "./runescape/miscellaneous" 7 | import * as runemetrics from "./runescape/runemetrics" 8 | 9 | export { bestiary, clan, grandexchange, hiscores, miscellaneous, runemetrics } 10 | export default { 11 | bestiary, 12 | clan, 13 | grandexchange, 14 | hiscores, 15 | miscellaneous, 16 | runemetrics, 17 | osrs, 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/Oldschool.ts: -------------------------------------------------------------------------------- 1 | import { Jagex } from "../types" 2 | 3 | export class Player { 4 | name: string 5 | activities: Jagex.Hiscores.OSRSPlayerActivites 6 | bosses: Jagex.Hiscores.OSRSPlayerBosses 7 | skills: Jagex.Hiscores.OSRSPlayerSkills 8 | 9 | constructor(name: string, player: Jagex.Hiscores.OSRSPlayerJSON) { 10 | this.name = name 11 | this.activities = player.activities 12 | this.bosses = player.bosses 13 | this.skills = player.skills 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/lib/RuneScape.ts: -------------------------------------------------------------------------------- 1 | import { bestiary, grandexchange, hiscores } from "../configs/runescape" 2 | import { Jagex, RuneScape } from "../types" 3 | import { formatRuneMetricsProfileSkills } from "../utils/Jagex" 4 | 5 | export class Area { 6 | name: string 7 | 8 | constructor(area: Jagex.Bestiary.Area) { 9 | this.name = area 10 | } 11 | } 12 | 13 | export class Beast { 14 | id: number 15 | name: string 16 | examine?: string 17 | members?: boolean 18 | level?: number 19 | attack?: number 20 | defence?: number 21 | magic?: number 22 | ranged?: number 23 | lifepoints?: number 24 | xp?: string 25 | areas?: Jagex.Bestiary.Areas 26 | animations?: Jagex.Bestiary.Animations 27 | size?: number 28 | attackable?: boolean 29 | aggressive?: boolean 30 | poisonous?: boolean 31 | weakness?: Weakness 32 | 33 | constructor(beast: Jagex.Bestiary.Beast) { 34 | this.id = beast.id 35 | this.name = beast.name 36 | this.examine = beast.description 37 | this.members = beast.members 38 | 39 | this.level = beast.level 40 | this.attack = beast.attack 41 | this.defence = beast.defence 42 | this.magic = beast.magic 43 | this.ranged = beast.ranged 44 | this.lifepoints = beast.lifepoints 45 | this.xp = beast.xp 46 | 47 | this.areas = beast.areas 48 | this.animations = beast.animations 49 | 50 | this.size = beast.size 51 | 52 | this.attackable = beast.attackable 53 | this.aggressive = beast.aggressive 54 | this.poisonous = beast.poisonous 55 | this.weakness = beast.weakness ? new Weakness(beast.weakness) : undefined 56 | } 57 | 58 | get meta() { 59 | return { 60 | id: this.id, 61 | name: this.name, 62 | examine: this.examine, 63 | members: this.members, 64 | } 65 | } 66 | 67 | get combatModifiers() { 68 | return { 69 | attackable: this.attackable, 70 | aggressive: this.aggressive, 71 | poisonous: this.poisonous, 72 | weakness: this.weakness, 73 | } 74 | } 75 | 76 | get combatStats() { 77 | return { 78 | magic: this.magic, 79 | defence: this.defence, 80 | ranged: this.ranged, 81 | attack: this.attack, 82 | lifepoints: this.lifepoints, 83 | } 84 | } 85 | } 86 | 87 | export class BeastSearchResult { 88 | id: number 89 | name: string 90 | 91 | constructor(beast: Jagex.Bestiary.BeastBySearch) { 92 | this.id = beast.value 93 | this.name = beast.label 94 | } 95 | } 96 | 97 | export class ClanMember { 98 | name: string 99 | rank: string 100 | experience: number 101 | kills: number 102 | 103 | constructor(member: Jagex.Clan.Member) { 104 | const [name, rank, experience, kills] = member.split(",") 105 | const regex = new RegExp(/\uFFFD/g) 106 | 107 | this.name = name.replace(regex, " ") 108 | this.rank = rank 109 | this.experience = parseInt(experience) 110 | this.kills = parseInt(kills) 111 | } 112 | } 113 | 114 | export class GrandExchangeCategory { 115 | id: number 116 | name: string 117 | 118 | constructor(category: Jagex.GrandExchange.Category | number) { 119 | if (typeof category === "string") { 120 | this.id = grandexchange.categories.indexOf(category) 121 | this.name = category 122 | } else { 123 | this.id = category 124 | this.name = grandexchange.categories[category] 125 | } 126 | } 127 | } 128 | 129 | export class Item { 130 | id: number 131 | name: string 132 | examine: string 133 | category: GrandExchangeCategory 134 | members: boolean 135 | icons: { default: string; large: string } 136 | trends: { 137 | current: { trend: string; price: string } 138 | today: { trend: string; price: string } 139 | day30?: { trend: string; price: string } 140 | day90?: { trend: string; price: string } 141 | day180?: { trend: string; price: string } 142 | } 143 | 144 | constructor(item: Jagex.GrandExchange.Item) { 145 | this.id = item.id 146 | this.name = item.name 147 | this.examine = item.description 148 | this.category = new GrandExchangeCategory(item.type) 149 | this.members = item.members === "true" ? true : false 150 | 151 | this.icons = { 152 | default: item.icon, 153 | large: item.icon_large, 154 | } 155 | 156 | this.trends = { 157 | current: item.current, 158 | today: item.today, 159 | day30: item.day30, 160 | day90: item.day90, 161 | day180: item.day180, 162 | } 163 | } 164 | } 165 | 166 | export class ItemGraph { 167 | id: number 168 | daily: { [key: string]: number } 169 | average: { [key: string]: number } 170 | 171 | constructor(id: number, graph: Jagex.GrandExchange.ItemGraph) { 172 | this.id = id 173 | this.daily = graph.daily 174 | this.average = graph.average 175 | } 176 | } 177 | 178 | export class Player { 179 | name: string 180 | activities: Jagex.Hiscores.PlayerActivites 181 | skills: Jagex.Hiscores.PlayerSkills 182 | 183 | constructor(name: string, player: Jagex.Hiscores.PlayerJSON) { 184 | this.name = name 185 | this.activities = player.activities 186 | this.skills = player.skills 187 | } 188 | } 189 | 190 | export class Quest { 191 | name: string 192 | status: RuneScape.RuneMetrics.QuestStatus 193 | difficulty: number 194 | members: boolean 195 | questPoints: number 196 | eligible: boolean 197 | 198 | constructor(quest: Jagex.RuneMetrics.Quest) { 199 | this.name = quest.title 200 | this.status = quest.status 201 | this.difficulty = quest.difficulty 202 | this.members = quest.members 203 | this.questPoints = quest.questPoints 204 | this.eligible = quest.userEligible 205 | } 206 | } 207 | 208 | export class RuneMetricsMonthlyExperience { 209 | skill: Skill 210 | totalExperience: number 211 | totalGain: number 212 | monthData: any[] 213 | 214 | constructor(monthlyExperienceGain: Jagex.RuneMetrics.MonthlyExperienceGain) { 215 | this.skill = new Skill(monthlyExperienceGain.skillId) 216 | this.totalExperience = monthlyExperienceGain.totalXp 217 | this.totalGain = monthlyExperienceGain.totalGain 218 | this.monthData = monthlyExperienceGain.monthData 219 | } 220 | } 221 | 222 | export class RuneMetricsProfile { 223 | name: string 224 | combatLevel: number 225 | activities: RuneScape.RuneMetrics.ProfileActivities[] 226 | overall: RuneScape.RuneMetrics.ProfileOverall 227 | skills: RuneScape.RuneMetrics.ProfileSkills 228 | quests: RuneScape.RuneMetrics.ProfileQuests 229 | experience_distribution: RuneScape.RuneMetrics.ProfileExperienceDistribution 230 | 231 | constructor(profile: Jagex.RuneMetrics.Profile) { 232 | this.name = profile.name 233 | this.combatLevel = profile.combatlevel 234 | this.experience_distribution = { 235 | magic: profile.magic, 236 | melee: profile.melee, 237 | ranged: profile.ranged, 238 | } 239 | this.overall = { 240 | rank: parseInt(profile.rank.replace(",", "")), 241 | level: profile.totalskill, 242 | experience: profile.totalxp, 243 | } 244 | this.skills = formatRuneMetricsProfileSkills(profile.skillvalues) 245 | this.quests = { 246 | complete: profile.questscomplete, 247 | started: profile.questsstarted, 248 | not_started: profile.questsnotstarted, 249 | } 250 | this.activities = profile.activities.map(({ text, details, date }) => { 251 | return { 252 | title: text, 253 | description: details, 254 | date, 255 | } 256 | }) 257 | } 258 | } 259 | 260 | export class Skill { 261 | id: number 262 | name: string 263 | 264 | constructor(skill: RuneScape.Hiscores.Skill | number) { 265 | if (typeof skill === "string") { 266 | this.id = hiscores.skills.indexOf(skill) 267 | this.name = skill 268 | } else { 269 | this.id = skill 270 | this.name = hiscores.skills[skill] 271 | } 272 | } 273 | } 274 | 275 | export class SlayerCategory { 276 | id: number 277 | name: string 278 | 279 | constructor(category: string | number) { 280 | if (typeof category === "string") { 281 | const [{ id }] = bestiary.slayerCategories.filter( 282 | ({ name }) => name === category 283 | ) 284 | 285 | this.id = id 286 | this.name = category 287 | } else { 288 | const [{ name }] = bestiary.slayerCategories.filter( 289 | ({ id }) => id === category 290 | ) 291 | 292 | this.id = category 293 | this.name = name 294 | } 295 | } 296 | } 297 | 298 | export class Weakness { 299 | id: number 300 | name: string 301 | 302 | constructor(weakness: Jagex.Bestiary.Weakness | number) { 303 | if (typeof weakness === "string") { 304 | this.id = bestiary.weaknesses.indexOf(weakness) 305 | this.name = weakness 306 | } else { 307 | this.id = weakness 308 | this.name = bestiary.weaknesses[weakness] 309 | } 310 | } 311 | } 312 | -------------------------------------------------------------------------------- /src/osrs/grandexchange.ts: -------------------------------------------------------------------------------- 1 | import got from "got" 2 | import { grandexchange } from "../configs/oldschool" 3 | import { Item, ItemGraph } from "../lib/RuneScape" 4 | import { Jagex } from "../types" 5 | 6 | export const getItem = async (id: number) => { 7 | if (typeof id !== "number") { 8 | throw new TypeError("Item ID parameter must be a number") 9 | } 10 | 11 | try { 12 | const response = await got(grandexchange.endpoints.item, { 13 | searchParams: { 14 | item: id, 15 | }, 16 | }).json<{ item: Jagex.GrandExchange.Item }>() 17 | 18 | return new Item(response.item) 19 | } catch (error) { 20 | throw new Error(error) 21 | } 22 | } 23 | 24 | export const getItemGraph = async (id: number) => { 25 | if (typeof id !== "number") { 26 | throw new TypeError("Item ID parameter must be a number") 27 | } 28 | 29 | try { 30 | const response = await got( 31 | `${grandexchange.endpoints.itemGraph}/${id}.json` 32 | ).json() 33 | 34 | return new ItemGraph(id, response) 35 | } catch (error) { 36 | throw new Error(error) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/osrs/hiscores.ts: -------------------------------------------------------------------------------- 1 | import got from "got" 2 | import { hiscores } from "../configs/oldschool" 3 | import { Player } from "../lib/Oldschool" 4 | import { parseJagexOSRSPlayerToJSON } from "../utils/Jagex" 5 | 6 | // type GetPlayerOptions = { 7 | // gamemode: typeof hiscores.gamemodes[number] 8 | // } 9 | 10 | // TODO: Handle 404 (player does not exist) response 11 | export const getPlayer = async ( 12 | name: string, 13 | gamemode: typeof hiscores.gamemodes[number] = "normal" 14 | ) => { 15 | if (typeof name !== "string") { 16 | throw new TypeError("Player name parameter must be a string") 17 | } 18 | 19 | if (![...hiscores.gamemodes].includes(gamemode)) { 20 | throw new Error( 21 | `Invalid gamemode selected. Available options are: ${hiscores.gamemodes.join( 22 | " | " 23 | )}` 24 | ) 25 | } 26 | 27 | try { 28 | const response = await got(hiscores.endpoints[gamemode], { 29 | searchParams: { 30 | player: name, 31 | }, 32 | }) 33 | 34 | const player = parseJagexOSRSPlayerToJSON(response.body) 35 | 36 | return new Player(name, player) 37 | } catch (error) { 38 | throw new Error(error) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/osrs/index.ts: -------------------------------------------------------------------------------- 1 | import * as grandexchange from "./grandexchange" 2 | import * as hiscores from "./hiscores" 3 | 4 | export { hiscores, grandexchange } 5 | export default { 6 | hiscores, 7 | grandexchange, 8 | } 9 | -------------------------------------------------------------------------------- /src/runescape/bestiary.ts: -------------------------------------------------------------------------------- 1 | import got from "got" 2 | import { bestiary } from "../configs/runescape" 3 | import { 4 | Area, 5 | Beast, 6 | BeastSearchResult, 7 | SlayerCategory, 8 | Weakness, 9 | } from "../lib/RuneScape" 10 | import { Jagex, Letter } from "../types" 11 | 12 | export const getAreas = async () => { 13 | try { 14 | const areas = await got(bestiary.endpoints.areas).json< 15 | Jagex.Bestiary.Areas 16 | >() 17 | 18 | return areas.map(area => new Area(area)) 19 | } catch (error) { 20 | throw new Error(error) 21 | } 22 | } 23 | 24 | export const getBeast = async (search: number | BeastSearchResult) => { 25 | if ( 26 | typeof search !== "number" && 27 | search.constructor.name !== "BeastSearchResult" 28 | ) { 29 | throw new TypeError( 30 | "Beast ID parameter must be a number or BeastSearchResult instance" 31 | ) 32 | } 33 | 34 | let searchId: number | undefined = undefined 35 | 36 | if (typeof search === "number") { 37 | searchId = search 38 | } else { 39 | searchId = search.id 40 | } 41 | 42 | try { 43 | const beast = await got(bestiary.endpoints.beast, { 44 | searchParams: { 45 | beastid: searchId, 46 | }, 47 | }).json() 48 | 49 | return new Beast(beast) 50 | } catch (error) { 51 | throw new Error(error) 52 | } 53 | } 54 | 55 | export const getBeastsByArea = async (search: string | Area) => { 56 | if (typeof search !== "string" && search.constructor.name !== "Area") { 57 | throw new TypeError("Search parameter must be a string or Area instance") 58 | } 59 | 60 | let searchId: string | undefined = undefined 61 | 62 | if (typeof search === "string") { 63 | searchId = search 64 | } else { 65 | searchId = search.name 66 | } 67 | try { 68 | const beasts = await got(bestiary.endpoints.beastArea, { 69 | searchParams: { 70 | identifier: searchId, 71 | }, 72 | }).json() 73 | 74 | // TODO: the empty response for this endpoint is [ 'none' ]. Remove it for an empty array 75 | 76 | return beasts.map(beast => new BeastSearchResult(beast)) 77 | } catch (error) { 78 | throw new Error(error) 79 | } 80 | } 81 | 82 | // TODO: validate that the search string is any of the 52 approved strings (26 english letters -- upper and lowercase) 83 | export const getBeastsByFirstLetter = async (search: Letter) => { 84 | if (typeof search !== "string") { 85 | throw new TypeError("Search parameter must be a string") 86 | } 87 | 88 | if (search.length > 1) { 89 | throw new Error("Search query must be a single letter") 90 | } 91 | 92 | try { 93 | const beasts = await got(bestiary.endpoints.beastLetter, { 94 | searchParams: { 95 | letter: search.toUpperCase(), 96 | }, 97 | }).json() 98 | 99 | // TODO: either trim, or clean up the "NPC" objects in the results 100 | 101 | return beasts.map(beast => new BeastSearchResult(beast)) 102 | } catch (error) { 103 | throw new Error(error) 104 | } 105 | } 106 | 107 | export const getBeastsByLevelRange = async (min: number, max: number) => { 108 | if (typeof min !== "number") { 109 | throw new TypeError("Minimum level parameter must be a number") 110 | } 111 | 112 | if (typeof max !== "number") { 113 | throw new TypeError("Maximum level parameter must be a number") 114 | } 115 | 116 | if (min > max) { 117 | throw new Error( 118 | "Maximum level parameter must be greater or equal to the minumum expected level" 119 | ) 120 | } 121 | 122 | try { 123 | const beasts = await got(bestiary.endpoints.beastLevel, { 124 | searchParams: { 125 | identifier: `${min}-${max}`, 126 | }, 127 | }).json() 128 | 129 | return beasts.map(beast => new BeastSearchResult(beast)) 130 | } catch (error) { 131 | throw new Error(error) 132 | } 133 | } 134 | 135 | export const getBeastsBySlayerCategory = async ( 136 | search: number | SlayerCategory 137 | ) => { 138 | if ( 139 | typeof search !== "number" && 140 | search.constructor.name !== "SlayerCategory" 141 | ) { 142 | throw new TypeError( 143 | "Search parameter must be a number or SlayerCategory instance" 144 | ) 145 | } 146 | 147 | let searchId: number | undefined = undefined 148 | 149 | if (typeof search === "number") { 150 | searchId = search 151 | } else { 152 | searchId = search.id 153 | } 154 | 155 | try { 156 | const beasts = await got(bestiary.endpoints.beastSlayer, { 157 | searchParams: { 158 | identifier: searchId, 159 | }, 160 | }).json() 161 | 162 | return beasts.map(beast => new BeastSearchResult(beast)) 163 | } catch (error) { 164 | throw new Error(error) 165 | } 166 | } 167 | 168 | export const getBeastsByTerms = async (search: string) => { 169 | if (typeof search !== "string") { 170 | throw new TypeError("Search parameter must be a string") 171 | } 172 | 173 | try { 174 | const beasts = await got(bestiary.endpoints.beastTerm, { 175 | searchParams: { 176 | term: search, 177 | }, 178 | }).json() 179 | 180 | return beasts.map(beast => new BeastSearchResult(beast)) 181 | } catch (error) { 182 | throw new Error(error) 183 | } 184 | } 185 | 186 | export const getBeastsByWeakness = async (search: number | Weakness) => { 187 | if (typeof search !== "number" && search.constructor.name !== "Weakness") { 188 | throw new TypeError( 189 | "Search parameter must be a number or Weakness instance" 190 | ) 191 | } 192 | 193 | let searchId: number | undefined = undefined 194 | 195 | if (typeof search === "number") { 196 | searchId = search 197 | } else { 198 | searchId = search.id 199 | } 200 | 201 | try { 202 | const beasts = await got(bestiary.endpoints.beastWeakness, { 203 | searchParams: { 204 | identifier: searchId, 205 | }, 206 | }).json() 207 | 208 | return beasts.map(beast => new BeastSearchResult(beast)) 209 | } catch (error) { 210 | throw new Error(error) 211 | } 212 | } 213 | 214 | export const getSlayerCategories = async () => { 215 | try { 216 | const categories = await got(bestiary.endpoints.slayerCategories).json< 217 | Jagex.Bestiary.SlayerCategories 218 | >() 219 | 220 | return Object.values(categories).map( 221 | category => new SlayerCategory(category) 222 | ) 223 | } catch (error) { 224 | throw new Error(error) 225 | } 226 | } 227 | 228 | export const getWeaknesses = async () => { 229 | try { 230 | const weaknesses = await got(bestiary.endpoints.weaknesses).json< 231 | Jagex.Bestiary.Weaknesses 232 | >() 233 | 234 | return Object.values(weaknesses).map(i => new Weakness(i)) 235 | } catch (error) { 236 | throw new Error(error) 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /src/runescape/clan.ts: -------------------------------------------------------------------------------- 1 | import got from "got" 2 | import { clan } from "../configs/runescape" 3 | import { ClanMember } from "../lib/RuneScape" 4 | import { Jagex } from "../types" 5 | import { parseJagexClanToArray } from "../utils/Jagex" 6 | 7 | export const getMembers = async (search: string) => { 8 | if (typeof search !== "string") { 9 | throw new TypeError("Search parameter must be a string") 10 | } 11 | 12 | try { 13 | const members = await got(clan.endpoints.members, { 14 | searchParams: { 15 | clanName: search, 16 | }, 17 | }) 18 | 19 | const membersArray = parseJagexClanToArray(members.body) 20 | 21 | return membersArray.map( 22 | (member: Jagex.Clan.Member) => new ClanMember(member) 23 | ) 24 | } catch (error) { 25 | throw new Error(error) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/runescape/grandexchange.ts: -------------------------------------------------------------------------------- 1 | import got from "got" 2 | import { grandexchange } from "../configs/runescape" 3 | import { GrandExchangeCategory, Item, ItemGraph } from "../lib/RuneScape" 4 | import { AlphaNumeric, Jagex } from "../types" 5 | 6 | export const getCategories = async () => { 7 | return await new Promise(resolve => 8 | resolve( 9 | grandexchange.categories.map( 10 | category => new GrandExchangeCategory(category) 11 | ) 12 | ) 13 | ) 14 | } 15 | 16 | export const getCategoryCounts = async ( 17 | category: number | GrandExchangeCategory 18 | ) => { 19 | if ( 20 | typeof category !== "number" && 21 | category.constructor.name !== "GrandExchangeCategory" 22 | ) { 23 | throw new TypeError( 24 | "Category parameter must be a number or GrandExchangeCategory instance" 25 | ) 26 | } 27 | 28 | let categoryId: number | undefined = undefined 29 | 30 | if (typeof category === "number") { 31 | categoryId = category 32 | } else { 33 | categoryId = category.id 34 | } 35 | 36 | try { 37 | const response = await got(grandexchange.endpoints.category, { 38 | searchParams: { 39 | category: categoryId, 40 | }, 41 | }).json() 42 | 43 | return response.alpha 44 | } catch (error) { 45 | throw new Error(error) 46 | } 47 | } 48 | 49 | export const getCategoryCountsByPrefix = async ( 50 | categoryId: number | GrandExchangeCategory, 51 | prefix: AlphaNumeric, 52 | page = 1 53 | ) => { 54 | if ( 55 | typeof categoryId !== "number" && 56 | categoryId.constructor.name !== "GrandExchangeCategory" 57 | ) { 58 | throw new TypeError( 59 | "Category parameter must be a number or GrandExchangeCategory instance" 60 | ) 61 | } 62 | 63 | let category: number | undefined = undefined 64 | let alpha: string | undefined = undefined 65 | 66 | if (typeof categoryId === "number") { 67 | category = categoryId 68 | } else { 69 | category = categoryId.id 70 | } 71 | 72 | if (typeof prefix === "number") { 73 | alpha = encodeURIComponent("#") 74 | } else { 75 | alpha = prefix.toLowerCase() 76 | } 77 | 78 | try { 79 | const response = await got(grandexchange.endpoints.categoryItems, { 80 | searchParams: { 81 | category, 82 | alpha, 83 | page, 84 | }, 85 | }).json() 86 | 87 | return response.items 88 | } catch (error) { 89 | throw new Error(error) 90 | } 91 | } 92 | 93 | export const getItem = async (id: number) => { 94 | if (typeof id !== "number") { 95 | throw new TypeError("Item ID parameter must be a number") 96 | } 97 | 98 | try { 99 | const response = await got(grandexchange.endpoints.item, { 100 | searchParams: { 101 | item: id, 102 | }, 103 | }).json<{ item: Jagex.GrandExchange.Item }>() 104 | 105 | return new Item(response.item) 106 | } catch (error) { 107 | throw new Error(error) 108 | } 109 | } 110 | 111 | export const getItemGraph = async (id: number) => { 112 | if (typeof id !== "number") { 113 | throw new TypeError("Item ID parameter must be a number") 114 | } 115 | 116 | try { 117 | const response = await got( 118 | `${grandexchange.endpoints.itemGraph}/${id}.json` 119 | ).json() 120 | 121 | return new ItemGraph(id, response) 122 | } catch (error) { 123 | throw new Error(error) 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/runescape/hiscores.ts: -------------------------------------------------------------------------------- 1 | import got from "got" 2 | import { hiscores } from "../configs/runescape" 3 | import { Player } from "../lib/RuneScape" 4 | import { parseJagexPlayerToJSON } from "../utils/Jagex" 5 | 6 | // type GetPlayerOptions = { 7 | // gamemode: typeof hiscores.gamemodes[number] 8 | // } 9 | 10 | // TODO: Handle 404 (player does not exist) response 11 | export const getPlayer = async ( 12 | name: string, 13 | gamemode: typeof hiscores.gamemodes[number] = "normal" 14 | ) => { 15 | if (typeof name !== "string") { 16 | throw new TypeError("Player name parameter must be a string") 17 | } 18 | 19 | if (![...hiscores.gamemodes].includes(gamemode)) { 20 | throw new Error( 21 | `Invalid gamemode selected. Available options are: ${hiscores.gamemodes.join( 22 | " | " 23 | )}` 24 | ) 25 | } 26 | 27 | try { 28 | const response = await got(hiscores.endpoints[gamemode], { 29 | searchParams: { 30 | player: name, 31 | }, 32 | }) 33 | 34 | const player = parseJagexPlayerToJSON(response.body) 35 | 36 | return new Player(name, player) 37 | } catch (error) { 38 | throw new Error(error) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/runescape/index.ts: -------------------------------------------------------------------------------- 1 | import * as bestiary from "./bestiary" 2 | import * as clan from "./clan" 3 | import * as grandexchange from "./grandexchange" 4 | import * as hiscores from "./hiscores" 5 | import * as miscellaneous from "./miscellaneous" 6 | import * as runemetrics from "./runemetrics" 7 | 8 | export { bestiary, clan, grandexchange, hiscores, miscellaneous, runemetrics } 9 | -------------------------------------------------------------------------------- /src/runescape/miscellaneous.ts: -------------------------------------------------------------------------------- 1 | import got from "got" 2 | import { miscellaneous } from "../configs/runescape" 3 | import { Jagex } from "../types" 4 | 5 | export const getAvatar = async (name: string) => { 6 | if (typeof name !== "string") { 7 | return new TypeError("Search parameter must be a string") 8 | } 9 | 10 | try { 11 | const response = await got.get( 12 | `https://secure.runescape.com/m=avatar-rs/g=runescape/${encodeURI( 13 | name 14 | )}/chat.png` 15 | ) 16 | 17 | return response.url 18 | } catch (error) { 19 | return miscellaneous.endpoints.defaultAvatarUrl 20 | } 21 | } 22 | 23 | export const getTotalUsers = async () => { 24 | try { 25 | const response = await got(miscellaneous.endpoints.totalUsers).json< 26 | Jagex.Miscellaneous.TotalUsers 27 | >() 28 | 29 | return response.accounts 30 | } catch (error) { 31 | throw new Error(error) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/runescape/runemetrics.ts: -------------------------------------------------------------------------------- 1 | import got from "got" 2 | import { runemetrics } from "../configs/runescape" 3 | import { 4 | Quest, 5 | RuneMetricsMonthlyExperience, 6 | RuneMetricsProfile, 7 | Skill, 8 | } from "../lib/RuneScape" 9 | import { Jagex } from "../types" 10 | 11 | // TODO: Fail spectacularly if their profile is "unavailable" (may just mean they're private) 12 | export const getMonthlyXp = async (name: string, skill: number | Skill) => { 13 | if (typeof name !== "string") { 14 | throw new TypeError("Player name parameter must be a string") 15 | } 16 | 17 | if (typeof skill !== "number" && skill.constructor.name !== "Skill") { 18 | throw new TypeError("Skill parameter must be a number or Skill instance") 19 | } 20 | 21 | let skillId: number | undefined = undefined 22 | 23 | if (typeof skill === "number") { 24 | skillId = skill 25 | } else { 26 | skillId = skill.id 27 | } 28 | 29 | try { 30 | const response = await got(runemetrics.endpoints.monthlyXp, { 31 | searchParams: { 32 | searchName: name, 33 | skillid: skillId, 34 | }, 35 | }).json() 36 | 37 | return new RuneMetricsMonthlyExperience(response.monthlyXpGain[0]) 38 | } catch (error) { 39 | throw new Error(error) 40 | } 41 | } 42 | 43 | export const getProfile = async (name: string) => { 44 | if (typeof name !== "string") { 45 | throw new TypeError("Player name parameter must be a string") 46 | } 47 | 48 | try { 49 | const profile = await got(runemetrics.endpoints.profile, { 50 | searchParams: { 51 | activities: 20, 52 | user: name, 53 | }, 54 | }).json() 55 | 56 | return new RuneMetricsProfile(profile) 57 | } catch (error) { 58 | throw new Error(error) 59 | } 60 | } 61 | 62 | export const getQuests = async (name: string) => { 63 | if (typeof name !== "string") { 64 | throw new TypeError("Player name parameter must be a string") 65 | } 66 | 67 | try { 68 | const response = await got(runemetrics.endpoints.quests, { 69 | searchParams: { 70 | user: name, 71 | }, 72 | }).json() 73 | 74 | return response.quests.map(quest => new Quest(quest)) 75 | } catch (error) { 76 | throw new Error(error) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import { 2 | bestiary, 3 | grandexchange, 4 | hiscores, 5 | runemetrics, 6 | } from "./configs/runescape" 7 | import * as osrs from "./configs/oldschool" 8 | 9 | export namespace Jagex { 10 | export namespace Bestiary { 11 | export type Animations = { 12 | attack?: number 13 | death?: number 14 | } 15 | export type Area = string 16 | export type Areas = Area[] 17 | export type Beast = { 18 | magic?: number 19 | defence?: number 20 | ranged?: number 21 | attack?: number 22 | level?: number 23 | size?: number 24 | members?: boolean 25 | animations?: Animations 26 | name: string 27 | xp?: string 28 | description?: string 29 | areas?: Areas 30 | id: number 31 | aggressive?: boolean 32 | poisonous?: boolean 33 | attackable?: boolean 34 | weakness?: Weakness 35 | lifepoints?: number 36 | } 37 | export type BeastBySearch = { 38 | label: string 39 | value: number 40 | } 41 | export type BeastSearchMethods = typeof bestiary.searchMethods[number] 42 | export type SlayerCategories = { 43 | "Aberrant spectres": number 44 | "Abyssal demons": number 45 | "Acheron mammoths": number 46 | "Adamant dragons": number 47 | Airut: number 48 | Ankou: number 49 | Aquanites: number 50 | "Ascension members": number 51 | Aviansies: number 52 | Banshees: number 53 | Basilisks: number 54 | Bats: number 55 | Bears: number 56 | Birds: number 57 | "Black demons": number 58 | "Black dragons": number 59 | Bloodveld: number 60 | "Blue dragons": number 61 | "Brine rats": number 62 | "Bronze dragons": number 63 | "Camel warriors": number 64 | Catablepon: number 65 | "Cave bugs": number 66 | "Cave crawlers": number 67 | "Cave horrors": number 68 | "Cave slimes": number 69 | "Celestial dragons": number 70 | "Chaos giants": number 71 | Cockatrice: number 72 | "Corrupted creatures": number 73 | "Corrupted dust devils": number 74 | "Corrupted kalphites": number 75 | "Corrupted lizards": number 76 | "Corrupted scarabs": number 77 | "Corrupted scorpions": number 78 | "Corrupted worker": number 79 | Cows: number 80 | "Crawling hands": number 81 | "Creatures of Daemonheim": number 82 | "Creatures of the Lost Grove": number 83 | "Cres's creations": number 84 | "Crocodile akh": number 85 | Crocodiles: number 86 | "Crystal shapeshifters": number 87 | Cyclopes: number 88 | Dagannoth: number 89 | "Dark beasts": number 90 | Demons: number 91 | "Desert lizards": number 92 | "Desert strykewyrms": number 93 | Dinosaurs: number 94 | Dogs: number 95 | Dragons: number 96 | "Dust devils": number 97 | Dwarves: number 98 | "Earth warriors": number 99 | Edimmu: number 100 | Elves: number 101 | "Feline akh": number 102 | "Fever spiders": number 103 | "Fire giants": number 104 | Fleshcrawlers: number 105 | Frogs: number 106 | "Fungal magi": number 107 | "Ganodermic creatures": number 108 | Gargoyles: number 109 | "Gelatinous abominations": number 110 | "Gemstone dragons": number 111 | Ghosts: number 112 | Ghouls: number 113 | Glacors: number 114 | Goblins: number 115 | Goraks: number 116 | "Gorilla akh": number 117 | "Greater demons": number 118 | "Green dragons": number 119 | Grifolapines: number 120 | Grifolaroos: number 121 | Grotworms: number 122 | "Harpie bug swarms": number 123 | Hellhounds: number 124 | "Hill giants": number 125 | Hobgoblins: number 126 | "Ice giants": number 127 | "Ice strykewyrms": number 128 | "Ice warriors": number 129 | Icefiends: number 130 | "Imperial guard akh": number 131 | "Infernal mages": number 132 | "Iron dragons": number 133 | Jellies: number 134 | "Jungle horrors": number 135 | "Jungle strykewyrms": number 136 | "Kal'gerion demons": number 137 | Kalphite: number 138 | Killerwatts: number 139 | Kurask: number 140 | "Lava strykewyrms": number 141 | "Lesser demons": number 142 | "Living rock creatures": number 143 | "Living wyverns": number 144 | Minotaurs: number 145 | "Mithril dragons": number 146 | Mogres: number 147 | Molanisks: number 148 | Monkeys: number 149 | "Moss giants": number 150 | Muspah: number 151 | "Mutated jadinkos": number 152 | "Mutated zygomites": number 153 | Nechryael: number 154 | "Nightmare creatures": number 155 | Nihil: number 156 | Ogres: number 157 | "Otherworldly beings": number 158 | Pigs: number 159 | "Polypore creatures": number 160 | Pyrefiends: number 161 | Rats: number 162 | "Red dragons": number 163 | Revenants: number 164 | "Ripper demons": number 165 | Rockslugs: number 166 | "Rune dragons": number 167 | "Salawa akh": number 168 | Scabarites: number 169 | "Scarab akh": number 170 | Scorpions: number 171 | "Sea snakes": number 172 | Shades: number 173 | "Shadow creatures": number 174 | "Shadow warriors": number 175 | "Skeletal wyverns": number 176 | Skeletons: number 177 | "Soul devourers": number 178 | Spiders: number 179 | "Spiritual mages": number 180 | "Spiritual rangers": number 181 | "Spiritual warriors": number 182 | "Stalker creatures": number 183 | "Steel dragons": number 184 | Strykewyrms: number 185 | Suqahs: number 186 | "Terror dogs": number 187 | "Tormented demons": number 188 | Trolls: number 189 | Turoth: number 190 | TzHaar: number 191 | Vampyres: number 192 | "Vile blooms": number 193 | "Volcanic creatures": number 194 | Vyrewatch: number 195 | "Wall beasts": number 196 | "Warped terrorbirds": number 197 | "Warped tortoises": number 198 | Waterfiends: number 199 | Werewolves: number 200 | Wolves: number 201 | "Zarosian creatures": number 202 | Zombies: number 203 | } 204 | export type Weakness = typeof bestiary.weaknesses[number] 205 | export type Weaknesses = { 206 | None: number 207 | Air: number 208 | Water: number 209 | Earth: number 210 | Fire: number 211 | Stabbing: number 212 | Slashing: number 213 | Crushing: number 214 | Arrow: number 215 | Bolt: number 216 | Thrown: number 217 | } 218 | } 219 | export namespace Clan { 220 | export type Member = string 221 | export type Members = string 222 | } 223 | export namespace GrandExchange { 224 | export type Category = typeof grandexchange.categories[number] 225 | export type CategoryItemsCount = { 226 | types: never[] 227 | alpha: { letter: string; items: number }[] 228 | } 229 | export type CategoryItems = { 230 | total: number 231 | items: Item[] 232 | } 233 | export type Item = { 234 | icon: string 235 | icon_large: string 236 | id: number 237 | type: Jagex.GrandExchange.Category 238 | typeIcon: string 239 | name: string 240 | description: string 241 | current: ItemTrend 242 | today: ItemTrend 243 | members: string 244 | day30?: ItemTrend 245 | day90?: ItemTrend 246 | day180?: ItemTrend 247 | } 248 | 249 | type ItemTrend = { 250 | trend: string 251 | price: string 252 | } 253 | export type ItemGraph = { 254 | daily: { 255 | [key: string]: number 256 | } 257 | average: { 258 | [key: string]: number 259 | } 260 | } 261 | } 262 | export namespace Hiscores { 263 | // RuneScape 3 264 | type Activities = typeof hiscores.activities[number] 265 | type Activity = { 266 | rank: number 267 | count: number 268 | } 269 | export type Chathead = string 270 | export type Player = string 271 | export type PlayerJSON = { 272 | activities: PlayerActivites 273 | skills: PlayerSkills 274 | } 275 | export type PlayerActivites = { 276 | [key in K]: Activity 277 | } 278 | export type PlayerSkills = { [key in K]: Skill } 279 | type Skills = typeof hiscores.skills[number] 280 | type Skill = { 281 | rank: number 282 | level: number 283 | experience: number 284 | } 285 | export type Gamemode = typeof hiscores.gamemodes[number] 286 | 287 | // Oldschool RuneScape 288 | type OSRSActivities = typeof osrs.hiscores.activities[number] 289 | type OSRSActivity = { 290 | rank: number 291 | count: number 292 | } 293 | type OSRSBosses = typeof osrs.hiscores.bosses[number] 294 | type OSRSBoss = { 295 | rank: number 296 | count: number 297 | } 298 | export type OSRSPlayer = string 299 | export type OSRSPlayerJSON = { 300 | activities: OSRSPlayerActivites 301 | bosses: OSRSPlayerBosses 302 | skills: OSRSPlayerSkills 303 | } 304 | export type OSRSPlayerActivites = { 305 | [key in K]: OSRSActivity 306 | } 307 | export type OSRSPlayerBosses = { 308 | [key in K]: OSRSBoss 309 | } 310 | export type OSRSPlayerSkills = { 311 | [key in K]: OSRSSkill 312 | } 313 | type OSRSSkills = typeof osrs.hiscores.skills[number] 314 | type OSRSSkill = { 315 | rank: number 316 | level: number 317 | experience: number 318 | } 319 | export type OSRSGamemode = typeof osrs.hiscores.gamemodes[number] 320 | } 321 | export namespace Miscellaneous { 322 | export type TotalUsers = { accounts: number; accountsformatted: string } 323 | } 324 | export namespace RuneMetrics { 325 | export type MonthlyExperience = { 326 | monthlyXpGain: MonthlyExperienceGain[] 327 | loggedIn: string 328 | } 329 | export type MonthlyExperienceGain = { 330 | skillId: number 331 | totalXp: number 332 | averageXpGain: number 333 | totalGain: number 334 | monthData: any[] 335 | } 336 | export type Profile = { 337 | magic: number 338 | questsstarted: number 339 | totalskill: number 340 | questscomplete: number 341 | questsnotstarted: number 342 | totalxp: number 343 | ranged: number 344 | activities: ProfileActivities[] 345 | skillvalues: ProfileSkills[] 346 | name: string 347 | rank: string 348 | melee: number 349 | combatlevel: number 350 | loggedIn: string 351 | } 352 | type ProfileActivities = { 353 | date: string 354 | details: string 355 | text: string 356 | } 357 | export type ProfileSkills = { 358 | level: number 359 | xp: number 360 | rank: number 361 | id: number 362 | } 363 | export type Quests = { 364 | quests: Quest[] 365 | loggedIn: string 366 | } 367 | export type Quest = { 368 | title: string 369 | status: RuneScape.RuneMetrics.QuestStatus 370 | difficulty: number 371 | members: boolean 372 | questPoints: number 373 | userEligible: boolean 374 | } 375 | } 376 | } 377 | 378 | export namespace RuneScape { 379 | export namespace GrandExchange {} 380 | 381 | export namespace Hiscores { 382 | export type Skill = typeof hiscores.skills[number] 383 | } 384 | 385 | export namespace RuneMetrics { 386 | export type ProfileActivities = { 387 | title: string 388 | description: string 389 | date: string 390 | } 391 | export type ProfileSkills = { 392 | [key in K]: { 393 | rank: number 394 | level: number 395 | experience: number 396 | } 397 | } 398 | 399 | export type ProfileQuests = { 400 | complete: number 401 | started: number 402 | not_started: number 403 | } 404 | 405 | export type ProfileOverall = { 406 | rank: number 407 | level: number 408 | experience: number 409 | } 410 | 411 | export type ProfileExperienceDistribution = { 412 | magic: number 413 | melee: number 414 | ranged: number 415 | } 416 | 417 | export type QuestStatus = typeof runemetrics.questStatuses[number] 418 | } 419 | } 420 | 421 | // TODO: Extract this to it's own package @pqt/types 422 | export type AlphaNumeric = LowercaseLetter | UppercaseLetter | SingleNumeric 423 | export type Letter = LowercaseLetter | UppercaseLetter 424 | export type LowercaseLetter = 425 | | "a" 426 | | "b" 427 | | "c" 428 | | "d" 429 | | "e" 430 | | "f" 431 | | "g" 432 | | "h" 433 | | "i" 434 | | "j" 435 | | "k" 436 | | "l" 437 | | "m" 438 | | "n" 439 | | "o" 440 | | "p" 441 | | "q" 442 | | "r" 443 | | "s" 444 | | "t" 445 | | "u" 446 | | "v" 447 | | "w" 448 | | "x" 449 | | "y" 450 | | "z" 451 | 452 | export type UppercaseLetter = 453 | | "A" 454 | | "B" 455 | | "C" 456 | | "D" 457 | | "E" 458 | | "F" 459 | | "G" 460 | | "H" 461 | | "I" 462 | | "J" 463 | | "K" 464 | | "L" 465 | | "M" 466 | | "N" 467 | | "O" 468 | | "P" 469 | | "Q" 470 | | "R" 471 | | "S" 472 | | "T" 473 | | "U" 474 | | "V" 475 | | "W" 476 | | "X" 477 | | "Y" 478 | | "Z" 479 | 480 | export type SingleNumeric = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 481 | -------------------------------------------------------------------------------- /src/utils/Jagex.ts: -------------------------------------------------------------------------------- 1 | import { hiscores } from "../configs/runescape" 2 | import * as osrs from "../configs/oldschool" 3 | import { Jagex } from "../types" 4 | 5 | /** 6 | * RuneScape 3 7 | */ 8 | const defaultSkillTree: Jagex.Hiscores.PlayerSkills = { 9 | overall: { rank: 0, level: 1, experience: 0 }, 10 | attack: { rank: 0, level: 1, experience: 0 }, 11 | defence: { rank: 0, level: 1, experience: 0 }, 12 | strength: { rank: 0, level: 1, experience: 0 }, 13 | constitution: { rank: 0, level: 1, experience: 0 }, 14 | ranged: { rank: 0, level: 1, experience: 0 }, 15 | prayer: { rank: 0, level: 1, experience: 0 }, 16 | magic: { rank: 0, level: 1, experience: 0 }, 17 | cooking: { rank: 0, level: 1, experience: 0 }, 18 | woodcutting: { rank: 0, level: 1, experience: 0 }, 19 | fletching: { rank: 0, level: 1, experience: 0 }, 20 | fishing: { rank: 0, level: 1, experience: 0 }, 21 | firemaking: { rank: 0, level: 1, experience: 0 }, 22 | crafting: { rank: 0, level: 1, experience: 0 }, 23 | smithing: { rank: 0, level: 1, experience: 0 }, 24 | mining: { rank: 0, level: 1, experience: 0 }, 25 | herblore: { rank: 0, level: 1, experience: 0 }, 26 | agility: { rank: 0, level: 1, experience: 0 }, 27 | thieving: { rank: 0, level: 1, experience: 0 }, 28 | slayer: { rank: 0, level: 1, experience: 0 }, 29 | farming: { rank: 0, level: 1, experience: 0 }, 30 | runecrafting: { rank: 0, level: 1, experience: 0 }, 31 | hunter: { rank: 0, level: 1, experience: 0 }, 32 | construction: { rank: 0, level: 1, experience: 0 }, 33 | summoning: { rank: 0, level: 1, experience: 0 }, 34 | dungeoneering: { rank: 0, level: 1, experience: 0 }, 35 | divination: { rank: 0, level: 1, experience: 0 }, 36 | invention: { rank: 0, level: 1, experience: 0 }, 37 | archaeology: { rank: 0, level: 1, experience: 0 }, 38 | } 39 | const separateIntoLines = (jagexPlayer: string): string[] => { 40 | return jagexPlayer.split("\n") 41 | } 42 | const formatActivities = (activitiesArray: string[]) => { 43 | const activities: Jagex.Hiscores.PlayerActivites = { 44 | bounty_hunters: { rank: 0, count: 0 }, 45 | bh_rogues: { rank: 0, count: 0 }, 46 | dominion_tower: { rank: 0, count: 0 }, 47 | the_crucible: { rank: 0, count: 0 }, 48 | castle_wars_games: { rank: 0, count: 0 }, 49 | ba_attackers: { rank: 0, count: 0 }, 50 | ba_defenders: { rank: 0, count: 0 }, 51 | ba_collectors: { rank: 0, count: 0 }, 52 | ba_healers: { rank: 0, count: 0 }, 53 | duel_tournament: { rank: 0, count: 0 }, 54 | mobilising_armies: { rank: 0, count: 0 }, 55 | conquest: { rank: 0, count: 0 }, 56 | fist_of_guthix: { rank: 0, count: 0 }, 57 | gg_resource_race: { rank: 0, count: 0 }, 58 | gg_athletics: { rank: 0, count: 0 }, 59 | we2_armadyl_lifetime_contribution: { rank: 0, count: 0 }, 60 | we2_bandos_lifetime_contribution: { rank: 0, count: 0 }, 61 | we2_armadyl_pvp_kills: { rank: 0, count: 0 }, 62 | we2_bandos_pvp_kills: { rank: 0, count: 0 }, 63 | heist_guard_level: { rank: 0, count: 0 }, 64 | heist_robber_level: { rank: 0, count: 0 }, 65 | cfp_5_game_average: { rank: 0, count: 0 }, 66 | af15_cow_tipping: { rank: 0, count: 0 }, 67 | af15_rats_killed_after_the_miniquest: { rank: 0, count: 0 }, 68 | runescore: { rank: 0, count: 0 }, 69 | clue_scrolls_easy: { rank: 0, count: 0 }, 70 | clue_scrolls_medium: { rank: 0, count: 0 }, 71 | clue_scrolls_hard: { rank: 0, count: 0 }, 72 | clue_scrolls_elite: { rank: 0, count: 0 }, 73 | clue_scrolls_master: { rank: 0, count: 0 }, 74 | } 75 | 76 | hiscores.activities.map((activityName, index) => { 77 | const [rank, count] = activitiesArray[index].split(",") 78 | activities[activityName] = { rank: parseInt(rank), count: parseInt(count) } 79 | }) 80 | 81 | return activities 82 | } 83 | const formatSkills = (skillsArray: string[]) => { 84 | const skills = { ...defaultSkillTree } 85 | 86 | hiscores.skills.map((skillName, index) => { 87 | const [rank, level, experience] = skillsArray[index].split(",") 88 | skills[skillName] = { 89 | rank: parseInt(rank), 90 | level: parseInt(level), 91 | experience: parseInt(experience), 92 | } 93 | }) 94 | 95 | return skills 96 | } 97 | export const formatRuneMetricsProfileSkills = ( 98 | skillsArray: Jagex.RuneMetrics.ProfileSkills[] 99 | ) => { 100 | const skills = { ...defaultSkillTree } 101 | 102 | hiscores.skills.map((skillName, index) => { 103 | const { rank, level, xp: experience } = skillsArray.find( 104 | skill => skill.id === index 105 | ) || { rank: 0, level: 1, xp: 0 } 106 | 107 | skills[skillName] = { 108 | rank, 109 | level, 110 | experience, 111 | } 112 | }) 113 | 114 | return skills 115 | } 116 | export const parseJagexPlayerToJSON = ( 117 | jagexPlayer: string 118 | ): Jagex.Hiscores.PlayerJSON => { 119 | const lines = separateIntoLines(jagexPlayer) 120 | const [skillsStartIndex, skillsEndIndex] = [0, hiscores.skills.length] 121 | const [activitiesStartIndex, activitiesEndIndex] = [ 122 | hiscores.skills.length, 123 | hiscores.skills.length + hiscores.activities.length, 124 | ] 125 | 126 | const activities = formatActivities([ 127 | ...lines.slice(activitiesStartIndex, activitiesEndIndex), 128 | ]) 129 | const skills = formatSkills([ 130 | ...lines.slice(skillsStartIndex, skillsEndIndex), 131 | ]) 132 | 133 | return { 134 | activities, 135 | skills, 136 | } 137 | } 138 | export const parseJagexClanToArray = ( 139 | jagexClan: string 140 | ): Jagex.Clan.Member[] => { 141 | const lines = separateIntoLines(jagexClan) 142 | return [...lines.slice(1, -1)] 143 | } 144 | 145 | /** 146 | * Oldschool RuneScape 147 | */ 148 | 149 | const formatOSRSSkills = (skillsArray: string[]) => { 150 | const skills = { 151 | overall: { rank: 0, level: 1, experience: 0 }, 152 | attack: { rank: 0, level: 1, experience: 0 }, 153 | defence: { rank: 0, level: 1, experience: 0 }, 154 | strength: { rank: 0, level: 1, experience: 0 }, 155 | constitution: { rank: 0, level: 1, experience: 0 }, 156 | ranged: { rank: 0, level: 1, experience: 0 }, 157 | prayer: { rank: 0, level: 1, experience: 0 }, 158 | magic: { rank: 0, level: 1, experience: 0 }, 159 | cooking: { rank: 0, level: 1, experience: 0 }, 160 | woodcutting: { rank: 0, level: 1, experience: 0 }, 161 | fletching: { rank: 0, level: 1, experience: 0 }, 162 | fishing: { rank: 0, level: 1, experience: 0 }, 163 | firemaking: { rank: 0, level: 1, experience: 0 }, 164 | crafting: { rank: 0, level: 1, experience: 0 }, 165 | smithing: { rank: 0, level: 1, experience: 0 }, 166 | mining: { rank: 0, level: 1, experience: 0 }, 167 | herblore: { rank: 0, level: 1, experience: 0 }, 168 | agility: { rank: 0, level: 1, experience: 0 }, 169 | thieving: { rank: 0, level: 1, experience: 0 }, 170 | slayer: { rank: 0, level: 1, experience: 0 }, 171 | farming: { rank: 0, level: 1, experience: 0 }, 172 | runecrafting: { rank: 0, level: 1, experience: 0 }, 173 | hunter: { rank: 0, level: 1, experience: 0 }, 174 | construction: { rank: 0, level: 1, experience: 0 }, 175 | } 176 | 177 | osrs.hiscores.skills.map((skillName, index) => { 178 | const [rank, level, experience] = skillsArray[index].split(",") 179 | skills[skillName] = { 180 | rank: parseInt(rank), 181 | level: parseInt(level), 182 | experience: parseInt(experience), 183 | } 184 | }) 185 | 186 | return skills 187 | } 188 | const formatOSRSActivities = (activitiesArray: string[]) => { 189 | const activities: Jagex.Hiscores.OSRSPlayerActivites = { 190 | league_points: { rank: 0, count: 0 }, 191 | bounty_hunter_hunter: { rank: 0, count: 0 }, 192 | bounty_hunter_rogue: { rank: 0, count: 0 }, 193 | clue_scrolls_all: { rank: 0, count: 0 }, 194 | clue_scrolls_beginner: { rank: 0, count: 0 }, 195 | clue_scrolls_easy: { rank: 0, count: 0 }, 196 | clue_scrolls_medium: { rank: 0, count: 0 }, 197 | clue_scrolls_hard: { rank: 0, count: 0 }, 198 | clue_scrolls_elite: { rank: 0, count: 0 }, 199 | clue_scrolls_master: { rank: 0, count: 0 }, 200 | last_man_standing: { rank: 0, count: 0 }, 201 | } 202 | 203 | osrs.hiscores.activities.map((activityName, index) => { 204 | const [rank, count] = activitiesArray[index].split(",") 205 | activities[activityName] = { rank: parseInt(rank), count: parseInt(count) } 206 | }) 207 | 208 | return activities 209 | } 210 | const formatOSRSBosses = (activitiesArray: string[]) => { 211 | const bosses: Jagex.Hiscores.OSRSPlayerBosses = { 212 | abyssal_sire: { rank: 0, count: 0 }, 213 | alchemical_hydra: { rank: 0, count: 0 }, 214 | barrows_chests: { rank: 0, count: 0 }, 215 | bryophyta: { rank: 0, count: 0 }, 216 | callisto: { rank: 0, count: 0 }, 217 | cerberus: { rank: 0, count: 0 }, 218 | chambers_of_xeric: { rank: 0, count: 0 }, 219 | chambers_of_xeric_challenge_mode: { rank: 0, count: 0 }, 220 | chaos_elemental: { rank: 0, count: 0 }, 221 | chaos_fanatic: { rank: 0, count: 0 }, 222 | commander_zilyana: { rank: 0, count: 0 }, 223 | corporeal_beast: { rank: 0, count: 0 }, 224 | crazy_archaeologist: { rank: 0, count: 0 }, 225 | dagannoth_prime: { rank: 0, count: 0 }, 226 | dagannoth_rex: { rank: 0, count: 0 }, 227 | dagannoth_supreme: { rank: 0, count: 0 }, 228 | deranged_archaeologist: { rank: 0, count: 0 }, 229 | general_graardor: { rank: 0, count: 0 }, 230 | giant_mole: { rank: 0, count: 0 }, 231 | grotesque_guardians: { rank: 0, count: 0 }, 232 | hespori: { rank: 0, count: 0 }, 233 | kalphite_queen: { rank: 0, count: 0 }, 234 | king_black_dragon: { rank: 0, count: 0 }, 235 | kraken: { rank: 0, count: 0 }, 236 | kreearra: { rank: 0, count: 0 }, 237 | kril_tsutsaroth: { rank: 0, count: 0 }, 238 | mimic: { rank: 0, count: 0 }, 239 | nightmare: { rank: 0, count: 0 }, 240 | obor: { rank: 0, count: 0 }, 241 | sarachnis: { rank: 0, count: 0 }, 242 | scorpia: { rank: 0, count: 0 }, 243 | skotizo: { rank: 0, count: 0 }, 244 | the_gauntlet: { rank: 0, count: 0 }, 245 | the_corrupted_gauntlet: { rank: 0, count: 0 }, 246 | theatre_of_blood: { rank: 0, count: 0 }, 247 | thermonuclear_smoke_devil: { rank: 0, count: 0 }, 248 | tzkal_zuk: { rank: 0, count: 0 }, 249 | tztok_jad: { rank: 0, count: 0 }, 250 | venenatis: { rank: 0, count: 0 }, 251 | vetion: { rank: 0, count: 0 }, 252 | vorkath: { rank: 0, count: 0 }, 253 | wintertodt: { rank: 0, count: 0 }, 254 | zalcano: { rank: 0, count: 0 }, 255 | zulrah: { rank: 0, count: 0 }, 256 | } 257 | 258 | osrs.hiscores.bosses.map((bossName, index) => { 259 | const [rank, count] = activitiesArray[index].split(",") 260 | bosses[bossName] = { rank: parseInt(rank), count: parseInt(count) } 261 | }) 262 | 263 | return bosses 264 | } 265 | export const parseJagexOSRSPlayerToJSON = ( 266 | jagexPlayer: string 267 | ): Jagex.Hiscores.OSRSPlayerJSON => { 268 | const lines = separateIntoLines(jagexPlayer) 269 | const [skillsStartIndex, skillsEndIndex] = [0, osrs.hiscores.skills.length] 270 | const [activitiesStartIndex, activitiesEndIndex] = [ 271 | osrs.hiscores.skills.length, 272 | osrs.hiscores.skills.length + osrs.hiscores.activities.length, 273 | ] 274 | const [bossesStartIndex, bossesEndIndex] = [ 275 | osrs.hiscores.skills.length + osrs.hiscores.activities.length, 276 | osrs.hiscores.skills.length + 277 | osrs.hiscores.activities.length + 278 | osrs.hiscores.bosses.length, 279 | ] 280 | 281 | const activities = formatOSRSActivities([ 282 | ...lines.slice(activitiesStartIndex, activitiesEndIndex), 283 | ]) 284 | const bosses = formatOSRSBosses([ 285 | ...lines.slice(bossesStartIndex, bossesEndIndex), 286 | ]) 287 | const skills = formatOSRSSkills([ 288 | ...lines.slice(skillsStartIndex, skillsEndIndex), 289 | ]) 290 | 291 | return { 292 | activities, 293 | bosses, 294 | skills, 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | // "incremental": true, /* Enable incremental compilation */ 5 | "target": "ES2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 6 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 7 | // "lib": [], /* Specify library files to be included in the compilation. */ 8 | // "allowJs": true, /* Allow javascript files to be compiled. */ 9 | // "checkJs": true, /* Report errors in .js files. */ 10 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 11 | "declaration": true, /* Generates corresponding '.d.ts' file. */ 12 | "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 13 | "sourceMap": true, /* Generates corresponding '.map' file. */ 14 | // "outFile": "./", /* Concatenate and emit output to single file. */ 15 | "outDir": "./build", /* Redirect output structure to the directory. */ 16 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 17 | // "composite": true, /* Enable project compilation */ 18 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 19 | // "removeComments": true, /* Do not emit comments to output. */ 20 | // "noEmit": true, /* Do not emit outputs. */ 21 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 22 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 23 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 24 | 25 | /* Strict Type-Checking Options */ 26 | "strict": true, /* Enable all strict type-checking options. */ 27 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 28 | // "strictNullChecks": true, /* Enable strict null checks. */ 29 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 30 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 31 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 32 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 33 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 34 | 35 | /* Additional Checks */ 36 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 37 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 38 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 39 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 40 | 41 | /* Module Resolution Options */ 42 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 43 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 44 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 45 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 46 | // "typeRoots": [], /* List of folders to include type definitions from. */ 47 | // "types": [], /* Type declaration files to be included in compilation. */ 48 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 49 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 50 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 51 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 52 | "resolveJsonModule": true, 53 | 54 | /* Source Map Options */ 55 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 56 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 57 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 58 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 59 | 60 | /* Experimental Options */ 61 | "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 62 | "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 63 | 64 | /* Advanced Options */ 65 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 66 | } 67 | } --------------------------------------------------------------------------------