mirror of
				https://github.com/maxswa/osrs-json-hiscores.git
				synced 2025-10-15 10:19:04 +00:00 
			
		
		
		
	Compare commits
	
		
			103 Commits
		
	
	
		
			rsn-hyphen
			...
			v2.13.0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | f80b417121 | ||
|   | 415fe2f02b | ||
|   | 968799176f | ||
|   | f45ae67813 | ||
|   | 7724636b40 | ||
|   | 3954b04acc | ||
|   | ee7389455c | ||
|   | 54c11635e0 | ||
|   | dc799cf363 | ||
|   | d4c7aaff2a | ||
|   | 03c089ebff | ||
|   | 96641d875a | ||
|   | b2bdd3d2ce | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | a4ddfaf199 | ||
|   | efbcdfc709 | ||
|   | fe913aba1f | ||
|   | 1497653bab | ||
|   | f76de38eb1 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 2f1d10b309 | ||
|   | f08424cb81 | ||
|   | 69088db3b6 | ||
|   | 9cc89735cd | ||
|   | a126ae4fc7 | ||
|   | 52f3df1838 | ||
|   | 2891bbddc4 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | e2ea2fe33d | ||
|   | b7ae812f45 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 240628d9cc | ||
|   | b754663f6d | ||
|   | 0678f7c979 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 2a60283a93 | ||
|   | b912731cf9 | ||
|   | cf44c57ec6 | ||
|   | 3f34c9ab61 | ||
|   | 1ef73b6b0c | ||
|   | 08ecf68689 | ||
|   | ab3ee70237 | ||
|   | cad20d41cb | ||
|   | 2877b9c8a1 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | afbb22749a | ||
|   | eebf76a34c | ||
|   | e60442a7ce | ||
|   | 1132a085e1 | ||
|   | a2a34a46e3 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 71a76a12fd | ||
|   | 3a72315498 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | c1c9833ad0 | ||
|   | 9fe03746e1 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 66a4fb47a6 | ||
|   | 83c44148a7 | ||
|   | 44dea6b35c | ||
|   | 0bf491638c | ||
|   | febca534b0 | ||
|   | 8daee5c39b | ||
|   | f98cf8aaaa | ||
|   | 5d9f6a6bac | ||
|   | 8737b50a82 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 1255e7e3d2 | ||
|   | f4135d0b0f | ||
|   | 6135ff459c | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 9fb7e4cebc | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 11350b2444 | ||
|   | e0d6c5b20f | ||
|   | c8a4440ac3 | ||
|   | f3781cf142 | ||
|   | 5fbc04217b | ||
|   | eb5d0ba49f | ||
|   | c68e3bebea | ||
|   | aa6fd6f39a | ||
|   | cc9aae8ced | ||
|   | 12882b4e34 | ||
|   | b17a8b9299 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 50f2c19257 | ||
|   | d892f8e67b | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | af5664ae6e | ||
|   | 20c5c2f730 | ||
|   | 04e59cff04 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | ad2249ba76 | ||
|   | 5c52bfd883 | ||
|   | 947d994cdf | ||
|   | 130446ab74 | ||
|   | f96e7e3e2d | ||
|   | 90d66b6b2c | ||
|   | 14cedb5877 | ||
|   | 4703812b52 | ||
|   | a11ac96f68 | ||
|   | f97caa6a15 | ||
|   | e7e54741d2 | ||
|   | 062e5e02b2 | ||
|   | ed7036f9c0 | ||
|   | 74aba3e818 | ||
|   | 09fe7ed539 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 7a78e1ea0a | ||
|   | 7a4163aed2 | ||
|   | 85464d02fe | ||
|   | 592d82f86b | ||
|   | a28a9e253e | ||
|   | 0bd15f2402 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | ed8ad51b4d | ||
|   | 311ea88c61 | ||
|   | e056d052a5 | ||
|   | b230560442 | ||
|   | b2c6efecbd | 
							
								
								
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| *.html linguist-vendored | ||||
							
								
								
									
										22
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								README.md
									
									
									
									
									
								
							| @@ -3,6 +3,7 @@ | ||||
| [](https://www.npmjs.com/package/osrs-json-hiscores) | ||||
| [](https://npm-stat.com/charts.html?package=osrs-json-hiscores) | ||||
| [](https://github.com/maxswa/osrs-json-hiscores/blob/master/src/types.ts) | ||||
| [](https://github.com/maxswa/osrs-json-hiscores/actions/workflows/main.yml?query=branch%3Amain) | ||||
|  | ||||
| **The Old School Runescape API wrapper that does more!** | ||||
|  | ||||
| @@ -108,7 +109,9 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses | ||||
| | Bounty Hunter (Rogue)  |     `rogueBH`     | | ||||
| | Bounty Hunter (Hunter) |    `hunterBH`     | | ||||
| | Last Man Standing      | `lastManStanding` | | ||||
| | PvP Arena              |    `pvpArena`     | | ||||
| | Soul Wars Zeal         |  `soulWarsZeal`   | | ||||
| | Rifts Closed           |   `riftsClosed`   | | ||||
|  | ||||
| ### Leagues | ||||
|  | ||||
| @@ -119,15 +122,17 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses | ||||
| ### Bosses | ||||
|  | ||||
| | Boss Name                         |             Param              | | ||||
| | -------------------------------- | :----------------------------: | | ||||
| | --------------------------------- | :----------------------------: | | ||||
| | Abyssal Sire                      |         `abyssalSire`          | | ||||
| | Alchemical Hydra                  |       `alchemicalHydra`        | | ||||
| | Artio                             |            `artio`             | | ||||
| | Barrows Chests                    |           `barrows`            | | ||||
| | Bryophyta                         |          `bryophyta`           | | ||||
| | Callisto                          |           `callisto`           | | ||||
| | Cal'varion                        |          `calvarion`           | | ||||
| | Cerberus                          |           `cerberus`           | | ||||
| | Chambers Of Xeric                 |       `chambersOfXeric`        | | ||||
| | Chambers Of Xeric Challenge Mode | `chambersOfXericChallengeMode` | | ||||
| | Chambers Of Xeric: Challenge Mode | `chambersOfXericChallengeMode` | | ||||
| | Chaos Elemental                   |        `chaosElemental`        | | ||||
| | Chaos Fanatic                     |         `chaosFanatic`         | | ||||
| | Commander Zilyana                 |       `commanderZilyana`       | | ||||
| @@ -147,16 +152,23 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses | ||||
| | Kreearra                          |           `kreeArra`           | | ||||
| | K'ril Tsutsaroth                  |        `krilTsutsaroth`        | | ||||
| | Mimic                             |            `mimic`             | | ||||
| | Nex                               |             `nex`              | | ||||
| | The Nightmare of Ashihama         |          `nightmare`           | | ||||
| | Phosani's Nightmare               |      `phosanisNightmare`       | | ||||
| | Obor                              |             `obor`             | | ||||
| | Phantom Muspah                    |        `phantomMuspah`         | | ||||
| | Sarachnis                         |          `sarachnis`           | | ||||
| | Scorpia                           |           `scorpia`            | | ||||
| | Skotizo                           |           `skotizo`            | | ||||
| | Spindel                           |           `spindel`            | | ||||
| | Tempoross                         |          `tempoross`           | | ||||
| | Gauntlet                         |           `gauntlet`           | | ||||
| | Corrupted Gauntlet               |      `corruptedGauntlet`       | | ||||
| | The Gauntlet                      |           `gauntlet`           | | ||||
| | The Corrupted Gauntlet            |      `corruptedGauntlet`       | | ||||
| | Theatre Of Blood                  |        `theatreOfBlood`        | | ||||
| | Theatre Of Blood: Hard Mode       |    `theatreOfBloodHardMode`    | | ||||
| | Thermonuclear Smoke Devil         |   `thermonuclearSmokeDevil`    | | ||||
| | Tombs of Amascut                  |        `tombsOfAmascut`        | | ||||
| | Tombs of Amascut: Expert Mode     |   `tombsOfAmascutExpertMode`   | | ||||
| | TzKal-Zuk                         |           `tzKalZuk`           | | ||||
| | TzTok-Jad                         |           `tzTokJad`           | | ||||
| | Venenatis                         |          `venenatis`           | | ||||
| @@ -188,7 +200,9 @@ Activities consist of all levels of clue scrolls as well as minigames and bosses | ||||
|     leaguePoints: {}, | ||||
|     bountyHunter: {}, | ||||
|     lastManStanding: {}, | ||||
|     pvpArena: {}, | ||||
|     soulWarsZeal: {}, | ||||
|     riftsClosed: {}, | ||||
|     bosses: {} | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										1393
									
								
								__tests__/attackTopPage.html
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1393
									
								
								__tests__/attackTopPage.html
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1435
									
								
								__tests__/b0atyNamePage.html
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1435
									
								
								__tests__/b0atyNamePage.html
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,12 +1,56 @@ | ||||
| import axios from 'axios'; | ||||
| import { readFileSync } from 'fs'; | ||||
|  | ||||
| import { | ||||
|   parseStats, | ||||
|   getSkillPage, | ||||
|   getStats, | ||||
|   getStatsByGamemode, | ||||
|   getRSNFormat, | ||||
|   Stats | ||||
|   Stats, | ||||
|   getPlayerTableURL, | ||||
|   getSkillPageURL, | ||||
|   getStatsURL, | ||||
|   BOSSES, | ||||
|   INVALID_FORMAT_ERROR | ||||
| } from '../src/index'; | ||||
|  | ||||
| const B0ATY_NAME = 'B0ATY'; | ||||
| const B0ATY_FORMATTED_NAME = 'B0aty'; | ||||
| const LYNX_TITAN_SPACE_NAME = 'lYnX tiTaN'; | ||||
| const LYNX_TITAN_UNDERSCORE_NAME = 'lYnX_tiTaN'; | ||||
| const LYNX_TITAN_HYPHEN_NAME = 'lYnX-tiTaN'; | ||||
| const LYNX_TITAN_FORMATTED_NAME = 'Lynx Titan'; | ||||
|  | ||||
| const attackTopPage = readFileSync(`${__dirname}/attackTopPage.html`, 'utf8'); | ||||
| const b0atyNamePage = readFileSync(`${__dirname}/b0atyNamePage.html`, 'utf8'); | ||||
| const lynxTitanStats = readFileSync(`${__dirname}/lynxTitanStats.csv`, 'utf8'); | ||||
| const lynxTitanNamePage = readFileSync( | ||||
|   `${__dirname}/lynxTitanNamePage.html`, | ||||
|   'utf8' | ||||
| ); | ||||
|  | ||||
| jest.spyOn(axios, 'get').mockImplementation((url) => { | ||||
|   const lynxUrls = [ | ||||
|     getPlayerTableURL('main', LYNX_TITAN_SPACE_NAME), | ||||
|     getPlayerTableURL('main', LYNX_TITAN_UNDERSCORE_NAME), | ||||
|     getPlayerTableURL('main', LYNX_TITAN_HYPHEN_NAME) | ||||
|   ]; | ||||
|   if (lynxUrls.includes(url)) { | ||||
|     return Promise.resolve({ data: lynxTitanNamePage }); | ||||
|   } | ||||
|   if (getPlayerTableURL('main', B0ATY_NAME) === url) { | ||||
|     return Promise.resolve({ data: b0atyNamePage }); | ||||
|   } | ||||
|   if (getSkillPageURL('main', 'attack', 1) === url) { | ||||
|     return Promise.resolve({ data: attackTopPage }); | ||||
|   } | ||||
|   if (getStatsURL('main', LYNX_TITAN_FORMATTED_NAME) === url) { | ||||
|     return Promise.resolve({ status: 200, data: lynxTitanStats }); | ||||
|   } | ||||
|   throw new Error(`No mock response for URL: ${url}`); | ||||
| }); | ||||
|  | ||||
| test('Parse CSV to json', () => { | ||||
|   const csv = `246,2277,1338203419 | ||||
|   615,99,77438259 | ||||
| @@ -43,12 +87,16 @@ test('Parse CSV to json', () => { | ||||
|   392,250 | ||||
|   1,6143 | ||||
|   4814,898 | ||||
|   13,4057 | ||||
|   37,225 | ||||
|   25,1110 | ||||
|   382,2780 | ||||
|   944,3000 | ||||
|   704,892 | ||||
|   1981,1452 | ||||
|   4981,23 | ||||
|   888,1046 | ||||
|   444,792 | ||||
|   613,4856 | ||||
|   102,4038 | ||||
|   156,334 | ||||
| @@ -71,16 +119,23 @@ test('Parse CSV to json', () => { | ||||
|   625,2391 | ||||
|   120,2981 | ||||
|   1,109 | ||||
|   99,39002 | ||||
|   3,22666 | ||||
|   2,84 | ||||
|   26,323 | ||||
|   44,6233 | ||||
|   201,1101 | ||||
|   82,3404 | ||||
|   5085,61 | ||||
|   678,903 | ||||
|   18823,23 | ||||
|   63,375 | ||||
|   2870,6 | ||||
|   6984,138 | ||||
|   23,923141 | ||||
|   4043,2000 | ||||
|   4073,1020 | ||||
|   289,13070 | ||||
|   489,8 | ||||
|   967,47 | ||||
|   11155,223 | ||||
| @@ -123,7 +178,9 @@ test('Parse CSV to json', () => { | ||||
|       rogue: { rank: 89912, score: 37 } | ||||
|     }, | ||||
|     lastManStanding: { rank: 4814, score: 898 }, | ||||
|     pvpArena: { rank: 13, score: 4057 }, | ||||
|     soulWarsZeal: { rank: 37, score: 225 }, | ||||
|     riftsClosed: { rank: 25, score: 1110 }, | ||||
|     clues: { | ||||
|       all: { rank: 32, score: 12148 }, | ||||
|       beginner: { rank: 3105, score: 76 }, | ||||
| @@ -136,9 +193,11 @@ test('Parse CSV to json', () => { | ||||
|     bosses: { | ||||
|       abyssalSire: { rank: 382, score: 2780 }, | ||||
|       alchemicalHydra: { rank: 944, score: 3000 }, | ||||
|       artio: { rank: 704, score: 892 }, | ||||
|       barrows: { rank: 1981, score: 1452 }, | ||||
|       bryophyta: { rank: 4981, score: 23 }, | ||||
|       callisto: { rank: 888, score: 1046 }, | ||||
|       calvarion: { rank: 444, score: 792 }, | ||||
|       cerberus: { rank: 613, score: 4856 }, | ||||
|       chambersOfXeric: { rank: 102, score: 4038 }, | ||||
|       chambersOfXericChallengeMode: { rank: 156, score: 334 }, | ||||
| @@ -161,16 +220,23 @@ test('Parse CSV to json', () => { | ||||
|       kreeArra: { rank: 625, score: 2391 }, | ||||
|       krilTsutsaroth: { rank: 120, score: 2981 }, | ||||
|       mimic: { rank: 1, score: 109 }, | ||||
|       nex: { rank: 99, score: 39002 }, | ||||
|       nightmare: { rank: 3, score: 22666 }, | ||||
|       phosanisNightmare: { rank: 2, score: 84 }, | ||||
|       obor: { rank: 26, score: 323 }, | ||||
|       phantomMuspah: { rank: 44, score: 6233 }, | ||||
|       sarachnis: { rank: 201, score: 1101 }, | ||||
|       scorpia: { rank: 82, score: 3404 }, | ||||
|       skotizo: { rank: 5085, score: 61 }, | ||||
|       spindel: { rank: 678, score: 903 }, | ||||
|       tempoross: { rank: 18823, score: 23 }, | ||||
|       gauntlet: { rank: 63, score: 375 }, | ||||
|       corruptedGauntlet: { rank: 2870, score: 6 }, | ||||
|       theatreOfBlood: { rank: 6984, score: 138 }, | ||||
|       theatreOfBloodHardMode: { rank: 23, score: 923141 }, | ||||
|       thermonuclearSmokeDevil: { rank: 4043, score: 2000 }, | ||||
|       tombsOfAmascut: { rank: 4073, score: 1020 }, | ||||
|       tombsOfAmascutExpertMode: { rank: 289, score: 13070 }, | ||||
|       tzKalZuk: { rank: 489, score: 8 }, | ||||
|       tzTokJad: { rank: 967, score: 47 }, | ||||
|       venenatis: { rank: 11155, score: 223 }, | ||||
| @@ -185,35 +251,41 @@ test('Parse CSV to json', () => { | ||||
|   expect(parseStats(csv)).toStrictEqual(expectedOutput); | ||||
| }); | ||||
|  | ||||
| test('Parse CSV with unknown activity', () => { | ||||
|   const statsWithUnknownActivity = `${lynxTitanStats} | ||||
|     -1,-1`; | ||||
|   expect(() => parseStats(statsWithUnknownActivity)).toThrow( | ||||
|     INVALID_FORMAT_ERROR | ||||
|   ); | ||||
| }); | ||||
|  | ||||
| test('Parse invalid CSV', () => { | ||||
|   expect(() => parseStats('invalid')).toThrow(INVALID_FORMAT_ERROR); | ||||
| }); | ||||
|  | ||||
| describe('Get name format', () => { | ||||
|   it('gets a name with a space', async () => { | ||||
|     jest.setTimeout(30000); | ||||
|     const data = await getRSNFormat('lYnX tiTaN'); | ||||
|     expect(data).toBe('Lynx Titan'); | ||||
|     const data = await getRSNFormat(LYNX_TITAN_SPACE_NAME); | ||||
|     expect(data).toBe(LYNX_TITAN_FORMATTED_NAME); | ||||
|   }); | ||||
|   it('gets a name with an underscore', async () => { | ||||
|     jest.setTimeout(30000); | ||||
|     const data = await getRSNFormat('lYnX_tiTaN'); | ||||
|     expect(data).toBe('Lynx Titan'); | ||||
|     const data = await getRSNFormat(LYNX_TITAN_UNDERSCORE_NAME); | ||||
|     expect(data).toBe(LYNX_TITAN_FORMATTED_NAME); | ||||
|   }); | ||||
|   it('gets a name with a hyphen', async () => { | ||||
|     jest.setTimeout(30000); | ||||
|     const data = await getRSNFormat('lYnX-tiTaN'); | ||||
|     expect(data).toBe('Lynx Titan'); | ||||
|     const data = await getRSNFormat(LYNX_TITAN_HYPHEN_NAME); | ||||
|     expect(data).toBe(LYNX_TITAN_FORMATTED_NAME); | ||||
|   }); | ||||
|   it('gets a name with a number', async () => { | ||||
|     jest.setTimeout(30000); | ||||
|     const data = await getRSNFormat('B0ATY'); | ||||
|     expect(data).toBe('B0aty'); | ||||
|     const data = await getRSNFormat(B0ATY_NAME); | ||||
|     expect(data).toBe(B0ATY_FORMATTED_NAME); | ||||
|   }); | ||||
|   it('throws an error for a name with invalid characters', async () => { | ||||
|     jest.setTimeout(30000); | ||||
|     await expect(getRSNFormat('b&aty')).rejects.toBeTruthy(); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| test('Get attack top page', async () => { | ||||
|   jest.setTimeout(30000); | ||||
|   const data = await getSkillPage('attack'); | ||||
|   expect(data).toMatchObject([ | ||||
|     { | ||||
| @@ -395,7 +467,6 @@ test('Get attack top page', async () => { | ||||
| }); | ||||
|  | ||||
| test('Get non-existent player', async () => { | ||||
|   jest.setTimeout(30000); | ||||
|   getStats('fishy').catch((err) => { | ||||
|     if (err.response) { | ||||
|       expect(err.response.status).toBe(404); | ||||
| @@ -404,8 +475,10 @@ test('Get non-existent player', async () => { | ||||
| }); | ||||
|  | ||||
| test('Get stats by gamemode', async () => { | ||||
|   jest.setTimeout(30000); | ||||
|   const { skills } = await getStatsByGamemode('Lynx Titan'); | ||||
|   const { skills, bosses } = await getStatsByGamemode( | ||||
|     LYNX_TITAN_FORMATTED_NAME | ||||
|   ); | ||||
|  | ||||
|   expect(skills).toMatchObject({ | ||||
|     overall: { rank: expect.any(Number), level: 2277, xp: 4600000000 }, | ||||
|     attack: { rank: expect.any(Number), level: 99, xp: 200000000 }, | ||||
| @@ -432,4 +505,56 @@ test('Get stats by gamemode', async () => { | ||||
|     hunter: { rank: expect.any(Number), level: 99, xp: 200000000 }, | ||||
|     construction: { rank: expect.any(Number), level: 99, xp: 200000000 } | ||||
|   }); | ||||
|  | ||||
|   const bossKeys = Object.keys(bosses); | ||||
|   expect(bossKeys).toStrictEqual(BOSSES); | ||||
|  | ||||
|   expect.assertions(2); | ||||
| }); | ||||
|  | ||||
| describe('Get stats options', () => { | ||||
|   const rsn = 'player'; | ||||
|   let axiosMock: jest.Mock; | ||||
|   beforeEach(() => { | ||||
|     axios.get = jest.fn( | ||||
|       (url) => | ||||
|         new Promise<any>((resolve) => | ||||
|           resolve( | ||||
|             url === getPlayerTableURL('main', rsn) | ||||
|               ? { data: lynxTitanNamePage } | ||||
|               : { status: 200, data: lynxTitanStats } | ||||
|           ) | ||||
|         ) | ||||
|     ); | ||||
|     axiosMock = axios.get as any; | ||||
|     axiosMock.mockClear(); | ||||
|   }); | ||||
|   it('fetches all gamemodes and formatted RSN when no options provided', async () => { | ||||
|     await getStats(rsn); | ||||
|     expect(axiosMock.mock.calls.map((val) => val[0])).toEqual([ | ||||
|       getStatsURL('main', rsn), | ||||
|       getPlayerTableURL('main', rsn), | ||||
|       getStatsURL('ironman', rsn), | ||||
|       getStatsURL('hardcore', rsn), | ||||
|       getStatsURL('ultimate', rsn) | ||||
|     ]); | ||||
|   }); | ||||
|   it('skips fetching formatted RSN when option is provided', async () => { | ||||
|     await getStats(rsn, { shouldGetFormattedRsn: false }); | ||||
|     expect( | ||||
|       axiosMock.mock.calls.some( | ||||
|         (val) => val[0] === getPlayerTableURL('main', rsn) | ||||
|       ) | ||||
|     ).toBeFalsy(); | ||||
|   }); | ||||
|   it('skips fetching game mode when option is provided', async () => { | ||||
|     await getStats(rsn, { | ||||
|       otherGamemodes: ['ironman', 'ultimate'] | ||||
|     }); | ||||
|     expect( | ||||
|       axiosMock.mock.calls.some( | ||||
|         (val) => val[0] === getStatsURL('hardcore', rsn) | ||||
|       ) | ||||
|     ).toBeFalsy(); | ||||
|   }); | ||||
| }); | ||||
|   | ||||
							
								
								
									
										1391
									
								
								__tests__/lynxTitanNamePage.html
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1391
									
								
								__tests__/lynxTitanNamePage.html
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										92
									
								
								__tests__/lynxTitanStats.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								__tests__/lynxTitanStats.csv
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | ||||
| 1,2277,4600000000 | ||||
| 15,99,200000000 | ||||
| 28,99,200000000 | ||||
| 18,99,200000000 | ||||
| 7,99,200000000 | ||||
| 8,99,200000000 | ||||
| 11,99,200000000 | ||||
| 32,99,200000000 | ||||
| 159,99,200000000 | ||||
| 15,99,200000000 | ||||
| 12,99,200000000 | ||||
| 9,99,200000000 | ||||
| 48,99,200000000 | ||||
| 4,99,200000000 | ||||
| 3,99,200000000 | ||||
| 25,99,200000000 | ||||
| 5,99,200000000 | ||||
| 23,99,200000000 | ||||
| 12,99,200000000 | ||||
| 2,99,200000000 | ||||
| 19,99,200000000 | ||||
| 7,99,200000000 | ||||
| 4,99,200000000 | ||||
| 4,99,200000000 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| 347584,22 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| -1,-1 | ||||
| Can't render this file because it has a wrong number of fields in line 25. | 
							
								
								
									
										14
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "osrs-json-hiscores", | ||||
|   "version": "2.4.0", | ||||
|   "version": "2.13.0", | ||||
|   "description": "The Old School Runescape API wrapper that does more!", | ||||
|   "main": "lib/index.js", | ||||
|   "types": "lib/index.d.ts", | ||||
| @@ -8,7 +8,7 @@ | ||||
|     "lib/**/*" | ||||
|   ], | ||||
|   "scripts": { | ||||
|     "dev": "watch 'yarn run build' src", | ||||
|     "dev": "yarn build --watch", | ||||
|     "build": "tsc", | ||||
|     "format": "prettier --write \"src/**/*.ts\"", | ||||
|     "lint": "eslint --fix \"src/**/*.ts\"", | ||||
| @@ -30,6 +30,9 @@ | ||||
|     "stats", | ||||
|     "skills" | ||||
|   ], | ||||
|   "publishConfig": { | ||||
|     "registry": "https://registry.npmjs.org" | ||||
|   }, | ||||
|   "author": "maxswa", | ||||
|   "license": "ISC", | ||||
|   "bugs": { | ||||
| @@ -52,7 +55,7 @@ | ||||
|     "root": true, | ||||
|     "parser": "@typescript-eslint/parser", | ||||
|     "parserOptions": { | ||||
|       "project": "./tsconfig.json" | ||||
|       "project": "./tsconfig.eslint.json" | ||||
|     }, | ||||
|     "plugins": [ | ||||
|       "@typescript-eslint" | ||||
| @@ -106,10 +109,9 @@ | ||||
|     "husky": "^5.2.0", | ||||
|     "jest": "^26.6.3", | ||||
|     "lint-staged": "^10.5.4", | ||||
|     "np": "6.5.0", | ||||
|     "np": "^7.7.0", | ||||
|     "prettier": "^2.2.1", | ||||
|     "ts-jest": "^26.5.4", | ||||
|     "typescript": "^4.2.3", | ||||
|     "watch": "^1.0.2" | ||||
|     "typescript": "^4.2.3" | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										112
									
								
								src/hiscores.ts
									
									
									
									
									
								
							
							
						
						
									
										112
									
								
								src/hiscores.ts
									
									
									
									
									
								
							| @@ -1,4 +1,5 @@ | ||||
| import { JSDOM } from 'jsdom'; | ||||
| import { AxiosResponse } from 'axios'; | ||||
| import { BinaryData, JSDOM } from 'jsdom'; | ||||
| import { | ||||
|   Player, | ||||
|   Activity, | ||||
| @@ -12,7 +13,8 @@ import { | ||||
|   PlayerSkillRow, | ||||
|   ActivityName, | ||||
|   PlayerActivityRow, | ||||
|   Bosses | ||||
|   Bosses, | ||||
|   GetStatsOptions | ||||
| } from './types'; | ||||
| import { | ||||
|   getStatsURL, | ||||
| @@ -27,7 +29,9 @@ import { | ||||
|   rsnFromElement, | ||||
|   getActivityPageURL, | ||||
|   httpGet, | ||||
|   BOSSES | ||||
|   BOSSES, | ||||
|   INVALID_FORMAT_ERROR, | ||||
|   validateRSN | ||||
| } from './utils'; | ||||
|  | ||||
| /** | ||||
| @@ -37,24 +41,19 @@ import { | ||||
|  * @returns Formatted version of the rsn. | ||||
|  */ | ||||
| export async function getRSNFormat(rsn: string): Promise<string> { | ||||
|   if (typeof rsn !== 'string') { | ||||
|     throw Error('RSN must be a string'); | ||||
|   } else if (!/^[a-zA-Z0-9 _-]+$/.test(rsn)) { | ||||
|     throw Error('RSN contains invalid character'); | ||||
|   } else if (rsn.length > 12 || rsn.length < 1) { | ||||
|     throw Error('RSN must be between 1 and 12 characters'); | ||||
|   } | ||||
|   validateRSN(rsn); | ||||
|  | ||||
|   const url = getPlayerTableURL('main', rsn); | ||||
|   try { | ||||
|     const response = await httpGet(url); | ||||
|     const dom = new JSDOM(response.data); | ||||
|     const spans = dom.window.document.querySelectorAll( | ||||
|       'span[style="color:#AA0022;"]' | ||||
|     const response = await httpGet<string | Buffer | BinaryData | undefined>( | ||||
|       url | ||||
|     ); | ||||
|     if (spans.length >= 2) { | ||||
|       const nameSpan = spans[1]; | ||||
|       return rsnFromElement(nameSpan); | ||||
|     const dom = new JSDOM(response.data); | ||||
|     const anchor = dom.window.document.querySelector( | ||||
|       '.personal-hiscores__row.personal-hiscores__row--type-highlight a' | ||||
|     ); | ||||
|     if (anchor) { | ||||
|       return rsnFromElement(anchor); | ||||
|     } | ||||
|     throw Error('Player not found'); | ||||
|   } catch { | ||||
| @@ -74,6 +73,13 @@ export function parseStats(csv: string): Stats { | ||||
|     .filter((entry) => !!entry) | ||||
|     .map((stat) => stat.split(',')); | ||||
|  | ||||
|   if ( | ||||
|     splitCSV.length !== | ||||
|     SKILLS.length + BH_MODES.length + CLUES.length + BOSSES.length + 5 | ||||
|   ) { | ||||
|     throw Error(INVALID_FORMAT_ERROR); | ||||
|   } | ||||
|  | ||||
|   const skillObjects: Skill[] = splitCSV | ||||
|     .filter((stat) => stat.length === 3) | ||||
|     .map((stat) => { | ||||
| @@ -100,7 +106,12 @@ export function parseStats(csv: string): Stats { | ||||
|   const [leaguePoints] = activityObjects.splice(0, 1); | ||||
|   const bhObjects = activityObjects.splice(0, BH_MODES.length); | ||||
|   const clueObjects = activityObjects.splice(0, CLUES.length); | ||||
|   const [lastManStanding, soulWarsZeal] = activityObjects.splice(0, 2); | ||||
|   const [ | ||||
|     lastManStanding, | ||||
|     pvpArena, | ||||
|     soulWarsZeal, | ||||
|     riftsClosed | ||||
|   ] = activityObjects.splice(0, 4); | ||||
|   const bossObjects = activityObjects.splice(0, BOSSES.length); | ||||
|  | ||||
|   const skills: Skills = skillObjects.reduce<Skills>((prev, curr, index) => { | ||||
| @@ -132,7 +143,9 @@ export function parseStats(csv: string): Stats { | ||||
|     leaguePoints, | ||||
|     bountyHunter, | ||||
|     lastManStanding, | ||||
|     pvpArena, | ||||
|     soulWarsZeal, | ||||
|     riftsClosed, | ||||
|     clues, | ||||
|     bosses | ||||
|   }; | ||||
| @@ -149,25 +162,36 @@ export function parseStats(csv: string): Stats { | ||||
|  * @param rsn Username of the player. | ||||
|  * @returns Player object. | ||||
|  */ | ||||
| export async function getStats(rsn: string): Promise<Player> { | ||||
|   if (typeof rsn !== 'string') { | ||||
|     throw Error('RSN must be a string'); | ||||
|   } else if (!/^[a-zA-Z0-9 _]+$/.test(rsn)) { | ||||
|     throw Error('RSN contains invalid character'); | ||||
|   } else if (rsn.length > 12 || rsn.length < 1) { | ||||
|     throw Error('RSN must be between 1 and 12 characters'); | ||||
|   } | ||||
| export async function getStats( | ||||
|   rsn: string, | ||||
|   options?: GetStatsOptions | ||||
| ): Promise<Player> { | ||||
|   validateRSN(rsn); | ||||
|   const otherGamemodes = options?.otherGamemodes ?? [ | ||||
|     'ironman', | ||||
|     'hardcore', | ||||
|     'ultimate' | ||||
|   ]; | ||||
|   const shouldGetFormattedRsn = options?.shouldGetFormattedRsn ?? true; | ||||
|  | ||||
|   const mainRes = await httpGet(getStatsURL('main', rsn)); | ||||
|   const mainRes = await httpGet<string>(getStatsURL('main', rsn)); | ||||
|   if (mainRes.status === 200) { | ||||
|     const otherResponses = await Promise.all([ | ||||
|       httpGet(getStatsURL('ironman', rsn)).catch((err) => err), | ||||
|       httpGet(getStatsURL('hardcore', rsn)).catch((err) => err), | ||||
|       httpGet(getStatsURL('ultimate', rsn)).catch((err) => err), | ||||
|       getRSNFormat(rsn).catch(() => undefined) | ||||
|     ]); | ||||
|  | ||||
|     const [ironRes, hcRes, ultRes, formattedName] = otherResponses; | ||||
|     const emptyResponse: AxiosResponse<string> = { | ||||
|       status: 404, | ||||
|       data: '', | ||||
|       statusText: '', | ||||
|       headers: {}, | ||||
|       config: {} | ||||
|     }; | ||||
|     const getModeStats = async ( | ||||
|       mode: Extract<Gamemode, 'ironman' | 'hardcore' | 'ultimate'> | ||||
|     ): Promise<AxiosResponse<string>> => | ||||
|       otherGamemodes.includes(mode) | ||||
|         ? httpGet<string>(getStatsURL(mode, rsn)).catch((err) => err) | ||||
|         : emptyResponse; | ||||
|     const formattedName = shouldGetFormattedRsn | ||||
|       ? await getRSNFormat(rsn).catch(() => undefined) | ||||
|       : undefined; | ||||
|  | ||||
|     const player: Player = { | ||||
|       name: formattedName ?? rsn, | ||||
| @@ -178,8 +202,11 @@ export async function getStats(rsn: string): Promise<Player> { | ||||
|     }; | ||||
|     player.main = parseStats(mainRes.data); | ||||
|  | ||||
|     const ironRes = await getModeStats('ironman'); | ||||
|     if (ironRes.status === 200) { | ||||
|       player.ironman = parseStats(ironRes.data); | ||||
|       const hcRes = await getModeStats('hardcore'); | ||||
|       const ultRes = await getModeStats('ultimate'); | ||||
|       if (hcRes.status === 200) { | ||||
|         player.mode = 'hardcore'; | ||||
|         player.hardcore = parseStats(hcRes.data); | ||||
| @@ -237,16 +264,11 @@ export async function getStatsByGamemode( | ||||
|   rsn: string, | ||||
|   mode: Gamemode = 'main' | ||||
| ): Promise<Stats> { | ||||
|   if (typeof rsn !== 'string') { | ||||
|     throw Error('RSN must be a string'); | ||||
|   } else if (!/^[a-zA-Z0-9 _]+$/.test(rsn)) { | ||||
|     throw Error('RSN contains invalid character'); | ||||
|   } else if (rsn.length > 12 || rsn.length < 1) { | ||||
|     throw Error('RSN must be between 1 and 12 characters'); | ||||
|   } else if (!GAMEMODES.includes(mode)) { | ||||
|   validateRSN(rsn); | ||||
|   if (!GAMEMODES.includes(mode)) { | ||||
|     throw Error('Invalid game mode'); | ||||
|   } | ||||
|   const response = await httpGet(getStatsURL(mode, rsn)); | ||||
|   const response = await httpGet<string>(getStatsURL(mode, rsn)); | ||||
|   if (response.status !== 200) { | ||||
|     throw Error('Player not found'); | ||||
|   } | ||||
| @@ -269,7 +291,7 @@ export async function getSkillPage( | ||||
|   } | ||||
|   const url = getSkillPageURL(mode, skill, page); | ||||
|  | ||||
|   const response = await httpGet(url); | ||||
|   const response = await httpGet<string | Buffer | BinaryData | undefined>(url); | ||||
|   const dom = new JSDOM(response.data); | ||||
|   const playersHTML = dom.window.document.querySelectorAll( | ||||
|     '.personal-hiscores__row' | ||||
| @@ -317,7 +339,7 @@ export async function getActivityPage( | ||||
|   } | ||||
|   const url = getActivityPageURL(mode, activity, page); | ||||
|  | ||||
|   const response = await httpGet(url); | ||||
|   const response = await httpGet<string | Buffer | BinaryData | undefined>(url); | ||||
|   const dom = new JSDOM(response.data); | ||||
|   const playersHTML = dom.window.document.querySelectorAll( | ||||
|     '.personal-hiscores__row' | ||||
|   | ||||
							
								
								
									
										31
									
								
								src/types.ts
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								src/types.ts
									
									
									
									
									
								
							| @@ -5,7 +5,10 @@ export type Gamemode = | ||||
|   | 'hardcore' | ||||
|   | 'deadman' | ||||
|   | 'seasonal' | ||||
|   | 'tournament'; | ||||
|   | 'tournament' | ||||
|   | 'skiller' | ||||
|   | 'oneDefence' | ||||
|   | 'freshStart'; | ||||
|  | ||||
| export interface Skill { | ||||
|   rank: number; | ||||
| @@ -64,9 +67,11 @@ export type BH = { [Type in BHType]: Activity }; | ||||
| export type Boss = | ||||
|   | 'abyssalSire' | ||||
|   | 'alchemicalHydra' | ||||
|   | 'artio' | ||||
|   | 'barrows' | ||||
|   | 'bryophyta' | ||||
|   | 'callisto' | ||||
|   | 'calvarion' | ||||
|   | 'cerberus' | ||||
|   | 'chambersOfXeric' | ||||
|   | 'chambersOfXericChallengeMode' | ||||
| @@ -89,16 +94,23 @@ export type Boss = | ||||
|   | 'kreeArra' | ||||
|   | 'krilTsutsaroth' | ||||
|   | 'mimic' | ||||
|   | 'nex' | ||||
|   | 'nightmare' | ||||
|   | 'phosanisNightmare' | ||||
|   | 'obor' | ||||
|   | 'phantomMuspah' | ||||
|   | 'sarachnis' | ||||
|   | 'scorpia' | ||||
|   | 'skotizo' | ||||
|   | 'spindel' | ||||
|   | 'tempoross' | ||||
|   | 'gauntlet' | ||||
|   | 'corruptedGauntlet' | ||||
|   | 'theatreOfBlood' | ||||
|   | 'theatreOfBloodHardMode' | ||||
|   | 'thermonuclearSmokeDevil' | ||||
|   | 'tombsOfAmascut' | ||||
|   | 'tombsOfAmascutExpertMode' | ||||
|   | 'tzKalZuk' | ||||
|   | 'tzTokJad' | ||||
|   | 'venenatis' | ||||
| @@ -115,7 +127,9 @@ export type ActivityName = | ||||
|   | 'hunterBH' | ||||
|   | 'rogueBH' | ||||
|   | 'lastManStanding' | ||||
|   | 'pvpArena' | ||||
|   | 'soulWarsZeal' | ||||
|   | 'riftsClosed' | ||||
|   | 'allClues' | ||||
|   | 'beginnerClues' | ||||
|   | 'easyClues' | ||||
| @@ -131,7 +145,9 @@ export interface Stats { | ||||
|   leaguePoints: Activity; | ||||
|   bountyHunter: BH; | ||||
|   lastManStanding: Activity; | ||||
|   pvpArena: Activity; | ||||
|   soulWarsZeal: Activity; | ||||
|   riftsClosed: Activity; | ||||
|   bosses: Bosses; | ||||
| } | ||||
| export type Modes = { [M in Gamemode]?: Stats }; | ||||
| @@ -153,3 +169,16 @@ export interface PlayerActivityRow extends Activity { | ||||
|   name: string; | ||||
|   dead: boolean; | ||||
| } | ||||
|  | ||||
| export interface GetStatsOptions { | ||||
|   /** | ||||
|    * Other game modes to fetch ranks for. | ||||
|    * @defaultvalue `['ironman', 'hardcore', 'ultimate']` | ||||
|    */ | ||||
|   otherGamemodes?: Extract<Gamemode, 'ironman' | 'hardcore' | 'ultimate'>[]; | ||||
|   /** | ||||
|    * If true, the formatted RSN will be fetched. Otherwise it will return the provided, unformatted RSN. | ||||
|    * @defaultvalue `true` | ||||
|    */ | ||||
|   shouldGetFormattedRsn?: boolean; | ||||
| } | ||||
|   | ||||
| @@ -22,7 +22,10 @@ export const GAMEMODE_URL: GamemodeUrl = { | ||||
|   ultimate: `${BASE_URL}_ultimate/`, | ||||
|   deadman: `${BASE_URL}_deadman/`, | ||||
|   seasonal: `${BASE_URL}_seasonal/`, | ||||
|   tournament: `${BASE_URL}_tournament/` | ||||
|   tournament: `${BASE_URL}_tournament/`, | ||||
|   skiller: `${BASE_URL}_skiller/`, | ||||
|   oneDefence: `${BASE_URL}_skiller_defence/`, | ||||
|   freshStart: `${BASE_URL}_fresh_start/` | ||||
| }; | ||||
| export const SKILLS: SkillName[] = [ | ||||
|   'overall', | ||||
| @@ -72,9 +75,11 @@ export const GAMEMODES: Gamemode[] = [ | ||||
| export const BOSSES: Boss[] = [ | ||||
|   'abyssalSire', | ||||
|   'alchemicalHydra', | ||||
|   'artio', | ||||
|   'barrows', | ||||
|   'bryophyta', | ||||
|   'callisto', | ||||
|   'calvarion', | ||||
|   'cerberus', | ||||
|   'chambersOfXeric', | ||||
|   'chambersOfXericChallengeMode', | ||||
| @@ -97,16 +102,23 @@ export const BOSSES: Boss[] = [ | ||||
|   'kreeArra', | ||||
|   'krilTsutsaroth', | ||||
|   'mimic', | ||||
|   'nex', | ||||
|   'nightmare', | ||||
|   'phosanisNightmare', | ||||
|   'obor', | ||||
|   'phantomMuspah', | ||||
|   'sarachnis', | ||||
|   'scorpia', | ||||
|   'skotizo', | ||||
|   'spindel', | ||||
|   'tempoross', | ||||
|   'gauntlet', | ||||
|   'corruptedGauntlet', | ||||
|   'theatreOfBlood', | ||||
|   'theatreOfBloodHardMode', | ||||
|   'thermonuclearSmokeDevil', | ||||
|   'tombsOfAmascut', | ||||
|   'tombsOfAmascutExpertMode', | ||||
|   'tzKalZuk', | ||||
|   'tzTokJad', | ||||
|   'venenatis', | ||||
| @@ -128,7 +140,9 @@ export const ACTIVITIES: ActivityName[] = [ | ||||
|   'eliteClues', | ||||
|   'masterClues', | ||||
|   'lastManStanding', | ||||
|   'pvpArena', | ||||
|   'soulWarsZeal', | ||||
|   'riftsClosed', | ||||
|   ...BOSSES | ||||
| ]; | ||||
|  | ||||
| @@ -139,9 +153,11 @@ export type FormattedBossNames = { | ||||
| export const FORMATTED_BOSS_NAMES: FormattedBossNames = { | ||||
|   abyssalSire: 'Abyssal Sire', | ||||
|   alchemicalHydra: 'Alchemical Hydra', | ||||
|   artio: 'Artio', | ||||
|   barrows: 'Barrows Chests', | ||||
|   bryophyta: 'Bryophyta', | ||||
|   callisto: 'Callisto', | ||||
|   calvarion: "Cal'varion", | ||||
|   cerberus: 'Cerberus', | ||||
|   chambersOfXeric: 'Chambers of Xeric', | ||||
|   chambersOfXericChallengeMode: 'Chambers of Xeric: Challenge Mode', | ||||
| @@ -164,16 +180,23 @@ export const FORMATTED_BOSS_NAMES: FormattedBossNames = { | ||||
|   kreeArra: "Kree'Arra", | ||||
|   krilTsutsaroth: "K'ril Tsutsaroth", | ||||
|   mimic: 'Mimic', | ||||
|   nex: 'Nex', | ||||
|   nightmare: 'The Nightmare of Ashihama', | ||||
|   phosanisNightmare: "Phosani's Nightmare", | ||||
|   obor: 'Obor', | ||||
|   phantomMuspah: 'Phantom Muspah', | ||||
|   sarachnis: 'Sarachnis', | ||||
|   scorpia: 'Scorpia', | ||||
|   skotizo: 'Skotizo', | ||||
|   spindel: 'Spindel', | ||||
|   tempoross: 'Tempoross', | ||||
|   gauntlet: 'The Gauntlet', | ||||
|   corruptedGauntlet: 'The Corrupted Gauntlet', | ||||
|   theatreOfBlood: 'Theatre of Blood', | ||||
|   theatreOfBloodHardMode: 'Theatre of Blood: Hard Mode', | ||||
|   thermonuclearSmokeDevil: 'Thermonuclear Smoke Devil', | ||||
|   tombsOfAmascut: 'Tombs of Amascut', | ||||
|   tombsOfAmascutExpertMode: 'Tombs of Amascut: Expert Mode', | ||||
|   tzKalZuk: 'TzKal-Zuk', | ||||
|   tzTokJad: 'TzTok-Jad', | ||||
|   venenatis: 'Venenatis', | ||||
| @@ -239,5 +262,9 @@ export const FORMATTED_BH_NAMES: FormattedBHNames = { | ||||
| }; | ||||
|  | ||||
| export const FORMATTED_LMS = 'Last Man Standing'; | ||||
| export const FORMATTED_PVP_ARENA = 'PvP Arena'; | ||||
| export const FORMATTED_SOUL_WARS = 'Soul Wars Zeal'; | ||||
| export const FORMATTED_LEAGUE_POINTS = 'League Points'; | ||||
| export const FORMATTED_RIFTS_CLOSED = 'Rifts Closed'; | ||||
|  | ||||
| export const INVALID_FORMAT_ERROR = 'Invalid hiscores format'; | ||||
|   | ||||
| @@ -96,10 +96,25 @@ export const rsnFromElement = (el: Element | null) => { | ||||
|  * @param url URL to run a `GET` request against. | ||||
|  * @returns Axios response. | ||||
|  */ | ||||
| export const httpGet = (url: string) => | ||||
|   axios.get(url, { | ||||
| export const httpGet = <Response>(url: string) => | ||||
|   axios.get<Response>(url, { | ||||
|     headers: { | ||||
|       // without User-Agent header requests may be rejected by DDoS protection mechanism | ||||
|       'User-Agent': ua.firefox(80) | ||||
|     } | ||||
|   }); | ||||
|  | ||||
| /** | ||||
|  * Validates that a provided RSN has the same username restrictions as Jagex. | ||||
|  * @param rsn Username to validate. | ||||
|  * @throws Error if the RSN fails validation. | ||||
|  */ | ||||
| export const validateRSN = (rsn: string) => { | ||||
|   if (typeof rsn !== 'string') { | ||||
|     throw Error('RSN must be a string'); | ||||
|   } else if (!/^[a-zA-Z0-9 _-]+$/.test(rsn)) { | ||||
|     throw Error('RSN contains invalid character'); | ||||
|   } else if (rsn.length > 12 || rsn.length < 1) { | ||||
|     throw Error('RSN must be between 1 and 12 characters'); | ||||
|   } | ||||
| }; | ||||
|   | ||||
							
								
								
									
										11
									
								
								tsconfig.eslint.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								tsconfig.eslint.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| { | ||||
|   "compilerOptions": { | ||||
|     "target": "es5", | ||||
|     "module": "commonjs", | ||||
|     "declaration": true, | ||||
|     "outDir": "./lib", | ||||
|     "strict": true | ||||
|   }, | ||||
|   "include": ["src", "**/__tests__/*"], | ||||
|   "exclude": ["node_modules"] | ||||
| } | ||||
| @@ -4,7 +4,12 @@ | ||||
|     "module": "commonjs", | ||||
|     "declaration": true, | ||||
|     "outDir": "./lib", | ||||
|     "strict": true | ||||
|     "strict": true, | ||||
|     "lib": ["ES2015", "DOM", "DOM.Iterable"], | ||||
|     "typeRoots": [ | ||||
|       "./node_modules/@types", | ||||
|       "./src/@types" | ||||
|     ] | ||||
|   }, | ||||
|   "include": ["src"], | ||||
|   "exclude": ["node_modules", "**/__tests__/*"] | ||||
|   | ||||
		Reference in New Issue
	
	Block a user