hypixel-api-reborn 8.3.0 → 10.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +2 -2
  2. package/package.json +9 -9
  3. package/src/API/getAPIStatus.js +2 -1
  4. package/src/API/getBoosters.js +1 -0
  5. package/src/API/getFriends.js +1 -0
  6. package/src/API/getGameCounts.js +1 -0
  7. package/src/API/getGuild.js +4 -2
  8. package/src/API/getKeyInfo.js +1 -0
  9. package/src/API/getLeaderboards.js +1 -0
  10. package/src/API/getPlayer.js +18 -3
  11. package/src/API/getRankedSkyWars.js +11 -0
  12. package/src/API/getRecentGames.js +1 -0
  13. package/src/API/getStatus.js +1 -0
  14. package/src/API/getWatchdogStats.js +1 -0
  15. package/src/API/index.js +23 -9
  16. package/src/API/skyblock/getEndedSkyblockAuctions.js +1 -0
  17. package/src/API/skyblock/getSkyblockAuctionsByPlayer.js +1 -0
  18. package/src/API/skyblock/getSkyblockBazaar.js +1 -0
  19. package/src/API/skyblock/getSkyblockMember.js +1 -0
  20. package/src/API/skyblock/getSkyblockNews.js +1 -0
  21. package/src/API/skyblock/getSkyblockProfiles.js +1 -0
  22. package/src/Client.js +57 -13
  23. package/src/Errors.js +3 -1
  24. package/src/Private/defaultCache.js +78 -0
  25. package/src/Private/rateLimit.js +4 -4
  26. package/src/Private/requests.js +24 -9
  27. package/src/Private/updater.js +45 -0
  28. package/src/Private/validate.js +6 -4
  29. package/src/structures/Game.js +3 -3
  30. package/src/structures/Guild/Guild.js +7 -1
  31. package/src/structures/MiniGames/Arcade.js +8 -8
  32. package/src/structures/MiniGames/BedWars.js +22 -0
  33. package/src/structures/MiniGames/Duels.js +291 -60
  34. package/src/structures/MiniGames/SkyWars.js +14 -6
  35. package/src/structures/MiniGames/SkyWarsRanked.js +45 -0
  36. package/src/structures/MiniGames/TurboKartRacers.js +130 -0
  37. package/src/structures/Player.js +15 -23
  38. package/src/structures/ServerInfo.js +1 -1
  39. package/src/structures/SkyBlock/News/SkyblockNews.js +4 -4
  40. package/src/structures/SkyBlock/SkyblockInventoryItem.js +29 -1
  41. package/src/structures/SkyBlock/SkyblockMember.js +25 -13
  42. package/src/utils/Constants.js +14 -0
  43. package/src/utils/index.js +13 -9
  44. package/src/utils/rgbToHexColor.js +8 -0
  45. package/src/utils/romanize.js +13 -0
  46. package/src/utils/varInt.js +1 -1
  47. package/typings/index.d.ts +423 -358
package/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
  <img src="https://flat.badgen.net/npm/license/hypixel-api-reborn">
11
11
  <a href="https://github.com/Hypixel-API-Reborn/hypixel-api-reborn"><img src="https://flat.badgen.net/github/stars/hypixel-api-reborn/hypixel-api-reborn"></a>
12
12
  <a href="https://www.npmjs.com/package/hypixel-api-reborn"><img src="https://nodei.co/npm/hypixel-api-reborn.png?compact=true"></a>
13
- <h2>This is the only Hypixel API wrapper for Node.js with intellisense support</h2>
13
+ <h2>A feature-rich Hypixel API wrapper for Node.js</h2>
14
14
  <br>
15
15
  </div>
16
16
 
@@ -47,7 +47,7 @@ hypixel.getGuild('name', 'The Foundation').then(guild => {
47
47
  ```
48
48
  For more examples go to our [documentation](https://hypixel.stavzdev.me/).
49
49
  ## Changelog
50
- [v8.2.0](https://github.com/Hypixel-API-Reborn/hypixel-api-reborn/releases/tag/8.2.0)
50
+ [v10.0.0](https://github.com/Hypixel-API-Reborn/hypixel-api-reborn/releases/tag/10.0.0)
51
51
 
52
52
  ### Try it now
53
53
  **[Code Sandbox](https://codesandbox.io/s/clever-babbage-xqmfw?file=/src/index.js)**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypixel-api-reborn",
3
- "version": "8.3.0",
3
+ "version": "10.0.0",
4
4
  "description": "Feature-rich Hypixel API wrapper for Node.js",
5
5
  "main": "./src/index.js",
6
6
  "types": "./typings/index.d.ts",
@@ -32,7 +32,7 @@
32
32
  "author": "StavZ",
33
33
  "dependencies": {
34
34
  "node-fetch": "^2.6.1",
35
- "object-path": "^0.11.5",
35
+ "object-path": "^0.11.8",
36
36
  "prismarine-nbt": "^1.6.0",
37
37
  "rss-parser": "^3.12.0"
38
38
  },
@@ -43,16 +43,16 @@
43
43
  },
44
44
  "publisher": "StavZ",
45
45
  "devDependencies": {
46
- "@types/node": "^16.3.1",
47
- "@typescript-eslint/eslint-plugin": "^4.28.2",
48
- "@typescript-eslint/parser": "^4.28.2",
46
+ "@types/node": "^16.10.2",
47
+ "@typescript-eslint/eslint-plugin": "^4.32.0",
48
+ "@typescript-eslint/parser": "^4.32.0",
49
49
  "chai": "^4.3.4",
50
- "dtslint": "^4.1.2",
51
- "eslint": "^7.30.0",
50
+ "dtslint": "^4.1.6",
51
+ "eslint": "^7.32.0",
52
52
  "eslint-config-google": "^0.14.0",
53
- "mocha": "^9.0.2",
53
+ "mocha": "^9.1.2",
54
54
  "node-env-run": "^4.0.2",
55
55
  "path": "^0.12.7",
56
- "typescript": "^4.3.5"
56
+ "typescript": "^4.4.3"
57
57
  }
58
58
  }
@@ -1,7 +1,8 @@
1
1
  const Rss = require('rss-parser');
2
2
  const Parser = new Rss();
3
- module.exports = async function () {
3
+ module.exports = async function (options) {
4
4
  const Status = require('../structures/APIStatus.js');
5
5
  const parsed = await Parser.parseURL('https://status.hypixel.net/history.rss');
6
+ if (options.raw) return parsed;
6
7
  return new Status(parsed);
7
8
  };
@@ -1,5 +1,6 @@
1
1
  module.exports = async function () {
2
2
  const Booster = require('../structures/Boosters/Booster');
3
3
  const res = await this._makeRequest('/boosters');
4
+ if (res.raw) return res;
4
5
  return res.boosters.length ? res.boosters.map((b) => new Booster(b)).reverse() : [];
5
6
  };
@@ -5,6 +5,7 @@ module.exports = async function (query) {
5
5
  const Friend = require('../structures/Friend');
6
6
  query = await toUuid(query);
7
7
  const res = await this._makeRequest(`/friends?uuid=${query}`);
8
+ if (res.raw) return res;
8
9
  if (res.records.length && res.records.length > 0) {
9
10
  return res.records.map((f) => new Friend(f, query));
10
11
  } else {
@@ -1,5 +1,6 @@
1
1
  const GameCounts = require('../structures/GameCounts');
2
2
  module.exports = async function () {
3
3
  const res = await this._makeRequest('/counts');
4
+ if (res.raw) return res;
4
5
  return new GameCounts(res);
5
6
  };
@@ -5,12 +5,14 @@ module.exports = async function (searchParameter, query) {
5
5
  if (!query) throw new Error(Errors.NO_GUILD_QUERY);
6
6
  const Guild = require('../structures/Guild/Guild');
7
7
  if (searchParameter === 'id' && !isGuildID(query)) throw new Error(Errors.INVALID_GUILD_ID);
8
- if (searchParameter === 'player') query = await toUuid(query);
8
+ const isPlayerQuery = searchParameter === 'player';
9
+ if (isPlayerQuery) query = await toUuid(query);
9
10
  if (!['id', 'name', 'player'].includes(searchParameter)) throw new Error(Errors.INVALID_GUILD_SEARCH_PARAMETER);
10
11
  const res = await this._makeRequest(`/guild?${searchParameter}=${encodeURI(query)}`);
12
+ if (res.raw) return res;
11
13
  if (!res.guild && searchParameter !== 'player') {
12
14
  throw new Error(Errors.GUILD_DOES_NOT_EXIST);
13
15
  }
14
16
 
15
- return res.guild ? new Guild(res.guild) : null;
17
+ return res.guild ? new Guild(res.guild, isPlayerQuery ? query : null) : null;
16
18
  };
@@ -2,6 +2,7 @@ const Errors = require('../Errors');
2
2
  module.exports = async function () {
3
3
  const KeyInfo = require('../structures/KeyInfo');
4
4
  const res = await this._makeRequest('/key');
5
+ if (res.raw) return res;
5
6
  if (!res.success) {
6
7
  throw new Error(Errors.SOMETHING_WENT_WRONG.replace(/{cause}/, res.cause));
7
8
  }
@@ -2,6 +2,7 @@ const Errors = require('../Errors');
2
2
  module.exports = async function () {
3
3
  const Leaderboard = require('../structures/Leaderboard');
4
4
  const res = await this._makeRequest('/leaderboards');
5
+ if (res.raw) return res;
5
6
  if (!res.leaderboards) throw new Error(Errors.SOMETHING_WENT_WRONG.replace(/{cause}/, 'Try again.'));
6
7
  const lbnames = Object.create(require('../utils/Constants').leaderboardNames);
7
8
  // eslint-disable-next-line guard-for-in
@@ -1,12 +1,27 @@
1
1
  const Errors = require('../Errors');
2
2
  const toUuid = require('../utils/toUuid');
3
3
  const getGuild = require('./getGuild');
4
- module.exports = async function (query, options = { guild: false }) {
4
+ const getRecentGames = require('./getRecentGames');
5
+ const getRankedSkyWars = require('./getRankedSkyWars');
6
+ module.exports = async function (query, options = { guild: false, recentGames: false, currentRankedSW: false }) {
5
7
  if (!query) throw new Error(Errors.NO_NICKNAME_UUID);
6
8
  const Player = require('../structures/Player');
7
9
  query = await toUuid(query);
8
10
  const res = await this._makeRequest(`/player?uuid=${query}`);
11
+ if (res.raw) return res;
9
12
  if (query && !res.player) throw new Error(Errors.PLAYER_HAS_NEVER_LOGGED);
10
- res.player.guild = options.guild ? await getGuild.call(this, 'player', query) : null;
11
- return new Player(res.player, this);
13
+ let guild = null;
14
+ let recentGames = null;
15
+ let rankedSW = null;
16
+ if (options.guild) {
17
+ guild = getGuild.call(this, 'player', query);
18
+ }
19
+ if (options.recentGames) {
20
+ recentGames = getRecentGames.call(this, query);
21
+ }
22
+ if (options.currentRankedSW) {
23
+ rankedSW = getRankedSkyWars.call(this, query);
24
+ }
25
+ [guild, recentGames, rankedSW] = await Promise.all([guild, recentGames, rankedSW]);
26
+ return new Player(res.player, { guild, recentGames, rankedSW });
12
27
  };
@@ -0,0 +1,11 @@
1
+ const Errors = require('../Errors');
2
+ const toUuid = require('../utils/toUuid');
3
+ module.exports = async function (query) {
4
+ if (!query) throw new Error(Errors.NO_NICKNAME_UUID);
5
+ const SkyWarsRanked = require('../structures/MiniGames/SkyWarsRanked');
6
+ query = await toUuid(query);
7
+ const res = await this._makeRequest(`/player/ranked/skywars?uuid=${query}`);
8
+ if (res.raw) return res;
9
+ if (!res.result) return null;
10
+ return new SkyWarsRanked(res.result);
11
+ };
@@ -8,6 +8,7 @@ module.exports = async function (query, playerData) {
8
8
  query = await toUuid(query);
9
9
 
10
10
  const res = await this._makeRequest(`/recentgames?uuid=${query}`);
11
+ if (res.raw) return res;
11
12
  if (res.games === []) {
12
13
  if (!playerData) throw new Error(Errors.PLAYER_IS_INACTIVE);
13
14
  if (Date.now() - playerData.lastLogoutTimestamp < day3) throw new Error(Errors.PLAYER_DISABLED_ENDPOINT);
@@ -3,5 +3,6 @@ module.exports = async function (query) {
3
3
  const Status = require('../structures/Status');
4
4
  query = await toUuid(query);
5
5
  const res = await this._makeRequest(`/status?uuid=${query}`);
6
+ if (res.raw) return res;
6
7
  return new Status(res.session);
7
8
  };
@@ -1,5 +1,6 @@
1
1
  module.exports = async function () {
2
2
  const WatchdogStats = require('../structures/Watchdog/Stats');
3
3
  const res = await this._makeRequest('/punishmentstats');
4
+ if (res.raw) return res;
4
5
  return new WatchdogStats(res);
5
6
  };
package/src/API/index.js CHANGED
@@ -1,9 +1,23 @@
1
- /* eslint-disable no-extend-native */
2
- const fs = require('fs');
3
- // eslint-disable-next-line no-path-concat
4
- const curDir = __dirname + '/';
5
- module.exports = Array.from(fs.readdirSync(curDir, { withFileTypes: true }))
6
- .filter((x) => x.name !== 'index.js')
7
- .map((x) => x.isDirectory() ? Array.from(fs.readdirSync(curDir + x.name)).map((y) => ([y.split('.')[0], x.name + '/' + y])) : [[x.name.split('.')[0], x.name]])
8
- .flat(1)
9
- .reduce((pV, cV) => Object.assign(pV, { [cV[0]]: require(curDir + cV[1]) }), {});
1
+ module.exports = {
2
+ getAPIStatus: require('./getAPIStatus'),
3
+ getBoosters: require('./getBoosters'),
4
+ getFriends: require('./getFriends'),
5
+ getGameCounts: require('./getGameCounts'),
6
+ getGuild: require('./getGuild'),
7
+ getKeyInfo: require('./getKeyInfo'),
8
+ getLeaderboards: require('./getLeaderboards'),
9
+ getPlayer: require('./getPlayer'),
10
+ getRankedSkyWars: require('./getRankedSkyWars'),
11
+ getRecentGames: require('./getRecentGames'),
12
+ getServerInfo: require('./getServerInfo'),
13
+ getStatus: require('./getStatus'),
14
+ getWatchdogStats: require('./getWatchdogStats'),
15
+
16
+ getEndedSkyblockAuctions: require('./skyblock/getEndedSkyblockAuctions'),
17
+ getSkyblockAuctions: require('./skyblock/getSkyblockAuctions'),
18
+ getSkyblockAuctionsByPlayer: require('./skyblock/getSkyblockAuctionsByPlayer'),
19
+ getSkyblockBazaar: require('./skyblock/getSkyblockBazaar'),
20
+ getSkyblockMember: require('./skyblock/getSkyblockMember'),
21
+ getSkyblockNews: require('./skyblock/getSkyblockNews'),
22
+ getSkyblockProfiles: require('./skyblock/getSkyblockProfiles')
23
+ };
@@ -2,6 +2,7 @@ const PartialAuction = require('../../structures/SkyBlock/Auctions/PartialAuctio
2
2
  const AuctionInfo = require('../../structures/SkyBlock/Auctions/AuctionInfo');
3
3
  module.exports = async function (includeItemBytes = false) {
4
4
  const res = await this._makeRequest('/skyblock/auctions_ended', false);
5
+ if (res.raw) return res;
5
6
  return {
6
7
  info: new AuctionInfo({ ...res, totalAuctions: res.auctions.length, totalPages: 1 }),
7
8
  auctions: res.auctions.length ? res.auctions.map((a) => new PartialAuction(a, includeItemBytes)) : []
@@ -5,6 +5,7 @@ module.exports = async function (query, includeItemBytes = false) {
5
5
  const Auction = require('../../structures/SkyBlock/Auctions/Auction');
6
6
  query = await toUuid(query);
7
7
  const res = await this._makeRequest(`/skyblock/auction?player=${query}`);
8
+ if (res.raw) return res;
8
9
 
9
10
  return res.auctions.length ? res.auctions.map((a) => new Auction(a, includeItemBytes)) : [];
10
11
  };
@@ -2,6 +2,7 @@ module.exports = async function () {
2
2
  const Product = require('../../structures/SkyBlock/Bazzar/Product');
3
3
 
4
4
  const res = await this._makeRequest('/skyblock/bazaar');
5
+ if (res.raw) return res;
5
6
 
6
7
  const productsKeys = Object.keys(res.products);
7
8
 
@@ -6,6 +6,7 @@ module.exports = async function (query, options = { fetchPlayer: false }) {
6
6
  if (!query) throw new Error(Errors.NO_NICKNAME_UUID);
7
7
  query = await toUuid(query);
8
8
  const res = await this._makeRequest(`/skyblock/profiles?uuid=${query}`);
9
+ if (res.raw) return res;
9
10
  if (!res.profiles || !res.profiles.length) {
10
11
  return new Map();
11
12
  }
@@ -1,5 +1,6 @@
1
1
  const SkyblockNews = require('../../structures/SkyBlock/News/SkyblockNews');
2
2
  module.exports = async function () {
3
3
  const res = await this._makeRequest('/skyblock/news');
4
+ if (res.raw) return res;
4
5
  return res.items.map((i) => new SkyblockNews(i));
5
6
  };
@@ -6,6 +6,7 @@ module.exports = async function (query, options = { fetchPlayer: false }) {
6
6
  if (!query) throw new Error(Errors.NO_NICKNAME_UUID);
7
7
  query = await toUuid(query);
8
8
  const res = await this._makeRequest(`/skyblock/profiles?uuid=${query}`);
9
+ if (res.raw) return res;
9
10
 
10
11
  if (!res.profiles || !res.profiles.length) {
11
12
  return [];
package/src/Client.js CHANGED
@@ -2,7 +2,8 @@
2
2
  /* eslint-disable max-len */
3
3
  const validate = new (require('./Private/validate'))();
4
4
  const rateLimit = new (require('./Private/rateLimit'))();
5
- const requests = new (require('./Private/requests'))();
5
+ const Requests = require('./Private/requests');
6
+ const updater = new (require('./Private/updater'))();
6
7
  const Errors = require('./Errors');
7
8
  const API = require('./API/index');
8
9
  const EventEmitter = require('events');
@@ -17,6 +18,7 @@ class Client extends EventEmitter {
17
18
  */
18
19
  constructor (key, options = {}) {
19
20
  super();
21
+ this.requests = new Requests(this, options.cacheHandler);
20
22
  // eslint-disable-next-line no-console
21
23
  if (options && !options.silent) this.on('warn', console.warn);
22
24
  // Test to check for multiple instances of client
@@ -28,17 +30,29 @@ class Client extends EventEmitter {
28
30
  this.key = validate.validateKey(key);
29
31
  this.options = validate.parseOptions(options);
30
32
  validate.validateOptions(this.options);
31
- // eslint-disable-next-line no-return-assign
32
- Object.keys(API).forEach((func) => Client.prototype[func] = function (...args) {
33
- return API[func].apply({ _makeRequest: this._makeRequest.bind(this, { ...(validate.cacheSuboptions(args[args.length - 1]) ? args[args.length - 1] : {}) }), ...this }, args);
34
- });
33
+ // eslint-disable-next-line guard-for-in
34
+ for (const func in API) {
35
+ Client.prototype[func] = function (...args) {
36
+ const lastArg = args[args.length - 1];
37
+ return API[func].apply(
38
+ {
39
+ _makeRequest: this._makeRequest.bind(this, { ...(validate.cacheSuboptions(lastArg) ? lastArg : {}) }),
40
+ ...this
41
+ },
42
+ args);
43
+ };
44
+
45
+ if (this.options.checkForUpdates) {
46
+ updater.checkForUpdates();
47
+ }
48
+ }
35
49
  /**
36
50
  * All cache entries
37
51
  * @type {Map<string,object>}
38
52
  */
39
- this.cache = requests.cache;
53
+ this.cache = this.requests.cache;
40
54
  clients.push(this);
41
- rateLimit.init(this.getKeyInfo(), this.options, this).then(()=>this.emit('ready'));
55
+ rateLimit.init(this.getKeyInfo(), this.options, this).then(() => this.emit('ready'));
42
56
  }
43
57
  /**
44
58
  * Private function - make request
@@ -50,10 +64,10 @@ class Client extends EventEmitter {
50
64
  */
51
65
  async _makeRequest (options, url, useRateLimitManager = true) {
52
66
  if (!url) return;
53
- if (url !== '/key' && !options.noCacheCheck && requests.cache.has(url)) return requests.cache.get(url);
67
+ if (url !== '/key' && !options.noCacheCheck && await this.requests.cache.has(url)) return Object.assign(await this.requests.cache.get(url), { raw: !!options.raw });
54
68
  if (useRateLimitManager) await rateLimit.rateLimitManager();
55
- this.emit('outgoingRequest', url, {...options, headers: {...options.headers, ...this.options.headers}});
56
- const result = await requests.request.call(this, url, {...options, headers: {...options.headers, ...this.options.headers}});
69
+ this.emit('outgoingRequest', url, { ...options, headers: { ...options.headers, ...this.options.headers } });
70
+ const result = await this.requests.request.call(this.requests, url, { ...options, headers: { ...options.headers, ...this.options.headers } });
57
71
  if (this.options.syncWithHeaders) rateLimit.sync(result._headers);
58
72
  return result;
59
73
  }
@@ -72,8 +86,7 @@ class Client extends EventEmitter {
72
86
  * // This example gets the first 100 friends of a player and gets their stats.
73
87
  * hypixel.once('ready',()=>{
74
88
  * hypixel.getFriends('StavZDev')
75
- * .then(friends => friends.map(x=>x.uuid).slice(0, 100))
76
- * .then(hypixel.getPlayer)
89
+ * .then(friends => friends.map(x=>x.uuid).slice(0, 100).map(hypixel.getPlayer))
77
90
  * .catch(console.log);
78
91
  * })
79
92
  */
@@ -350,41 +363,72 @@ class Client extends EventEmitter {
350
363
  * })
351
364
  * .catch(console.log)
352
365
  */
366
+ /**
367
+ * Allows you to get Ranked SkyWars data for current season of a player
368
+ * @method
369
+ * @name Client#getRankedSkyWars
370
+ * @param {string} query Player nickname or uuid
371
+ * @param {MethodOptions} [options={}] Options
372
+ * @return {Promise<SkyWarsRanked>}
373
+ * @example
374
+ * hypixel.getRankedSkyWars('gypu').then((ranked) => {
375
+ * console.log(ranked.position); // 4
376
+ * }).catch(console.log);
377
+ * @example
378
+ * // if player has no stats for current ranked season
379
+ * hypixel.getRankedSkyWars('StavZDev').then((ranked) => {
380
+ * console.log(ranked); // null
381
+ * }).catch(console.log) // throws 404 error;
382
+ */
353
383
  /**
354
384
  * Delete x (by default all) cache entries
355
385
  * @param {?number} amount Amount of cache to delete
356
386
  * @return {Promise<void|boolean[]>}
357
387
  */
358
388
  sweepCache (amount) {
359
- return requests.sweepCache(amount);
389
+ return this.requests.sweepCache(amount);
360
390
  }
361
391
  }
362
392
  /**
363
393
  * @typedef {object} ClientOptions
364
394
  * @prop {boolean} [cache=false] Enable/Disable request caching.
365
395
  * @prop {number} [cacheTime=60] Amount of time in seconds to cache the requests.
396
+ * @prop {CacheHandler} [cacheHandler] Custom Cache Handler
366
397
  * @prop {AUTO|HARD|NONE} [rateLimit='AUTO'] Rate limit mode.
367
398
  * @prop {boolean} [syncWithHeaders=false] Sync with headers rate limit information. Usually not necessary nor recommended ( because of latency )
368
399
  * @prop {number} [keyLimit=120] Key limit of your key.
369
400
  * @prop {number} [cacheSize=-1] The amount how many results will be cached. (`-1` for infinity)
370
401
  * @prop {boolean} [silent=false] Don't automatically put warnings into console.
371
402
  * @prop {object} [headers={}] Extra Headers ( like User-Agent ) to add to request.
403
+ * @prop {boolean} [checkForUpdates=false] Enable/Disable check for new version of hypixel-api-reborn.
404
+ */
405
+ const defaultCache = require('./Private/defaultCache.js');
406
+ /**
407
+ * @typedef {defaultCache} Cache
408
+ */
409
+ /**
410
+ * @typedef {Cache} CacheHandler
372
411
  */
373
412
  /**
374
413
  * @typedef {object} MethodOptions
414
+ * @property {boolean} [raw=false] Raw data
375
415
  * @property {boolean} [noCacheCheck=false] Disable/Enable cache checking
376
416
  * @property {boolean} [noCaching=false] Disable/Enable writing to cache
377
417
  * @prop {object} [headers={}] Extra Headers ( like User-Agent ) to add to request. Overrides the headers globally provided.
378
418
  */
379
419
  /**
380
420
  * @typedef {object} PlayerMethodOptions
421
+ * @property {boolean} [raw=false] Raw data
381
422
  * @property {boolean} [noCacheCheck=false] Disable/Enable cache checking
382
423
  * @property {boolean} [noCaching=false] Disable/Enable writing to cache
383
424
  * @property {boolean} [guild=false] Disable/Enable request for player's guild
425
+ * @property {boolean} [recentGames=false] Disable/Enable request for player's recent game
426
+ * @property {boolean} [currentRankedSW=false] Disable/Enable request for player's current ranked SkyWars rating. Previous ratings will always show mindless of this option.
384
427
  * @prop {object} [headers={}] Extra Headers ( like User-Agent ) to add to request. Overrides the headers globally provided.
385
428
  */
386
429
  /**
387
430
  * @typedef {object} SkyblockMethodOptions
431
+ * @property {boolean} [raw=false] Raw data
388
432
  * @property {?boolean} [noCacheCheck=false] Disable/Enable cache checking
389
433
  * @property {?boolean} [noCaching=false] Disable/Enable writing to cache
390
434
  * @property {?boolean} [fetchPlayer=false] Disable/Enable player profile request for each member
package/src/Errors.js CHANGED
@@ -30,8 +30,10 @@ module.exports = {
30
30
  INVALID_HEADER_SYNC_OPTION: '[hypixel-api-reborn] Invalid Value for maxSyncHeaders : must be a boolean',
31
31
  INVALID_BURST_OPTION: '[hypixel-api-reborn] maxBurstRequests provided in Client options must be a number',
32
32
  NODE_VERSION_ERR: '[hypixel-api-reborn] You are using a version of Nodejs that doesn\'t support certain features we use. Please upgrade to version 14 or above.',
33
+ UPDATER_REQUEST_NOT_OK: '[hypixel-api-reborn] An error occured checking for updates.',
33
34
  CONNECTION_ERROR: '[hypixel-api-reborn] Failed to connect.',
34
35
  RATE_LIMIT_INIT_ERROR: '[hypixel-api-reborn] An error happened whilst initializing rate limit. We strongly recommend restarting the code as this can lead to desynchronization.',
35
36
  MULTIPLE_INSTANCES: '[hypixel-api-reborn] Multiple instances of hypixel-api-reborn are found so we merged them for you. Please refrain from spawning multiple instances in the future. For more information, join our Discord Server https://discord.gg/NSEBNMM.',
36
- INVALID_HEADERS: '[hypixel-api-reborn] Invalid Headers are provided in ClientOptions. For help join our Discord Server https://discord.gg/NSEBNMM'
37
+ INVALID_HEADERS: '[hypixel-api-reborn] Invalid Headers are provided in ClientOptions. For help join our Discord Server https://discord.gg/NSEBNMM',
38
+ INVALID_CACHE_HANDLER: '[hypixel-api-reborn] An invalid cache handler is provideed. For help join our Discord Server https://discord.gg/NSEBNMM'
37
39
  };
@@ -0,0 +1,78 @@
1
+ /**
2
+ * This is the default cache implementation, using a Map.
3
+ * All cache implementations must have these methods below, they can be async :
4
+ * set(key, value) : Sets key to value
5
+ * has(key) : Whether there is an entry to this key. (can be an alias to get)
6
+ * get(key) : Gets by key (nullish value if not found)
7
+ * delete(key) : Deletes by key
8
+ * keys() : Array of keys, ordered by time of creation. To help you, the key will be deleted and set in case of an update
9
+ * size(): Size of cache (number of keys)
10
+ * clear(): Deletes all cache entries
11
+ * See JSDoc for more info
12
+ */
13
+ class Cache {
14
+ /**
15
+ * Constructor
16
+ */
17
+ constructor() {
18
+ this.storage = new Map();
19
+ }
20
+ /**
21
+ * Sets an entry
22
+ * @param {string} key String key
23
+ * @param {*} value Any value
24
+ * @return {boolean}
25
+ */
26
+ set(key, value) {
27
+ return this.storage.set(key, value);
28
+ }
29
+ /**
30
+ * Check if there's an entry in the cache that has this key
31
+ * This doesn't have to return boolean, just a truthy/falsy value
32
+ * @param {string} key String key
33
+ * @return {boolean} Whether this key exists
34
+ */
35
+ has(key) {
36
+ return this.storage.has(key);
37
+ }
38
+ /**
39
+ * Gets an entry, return a nullish value if not found
40
+ * @param {string} key String key
41
+ * @return {*}
42
+ */
43
+ get(key) {
44
+ return this.storage.get(key);
45
+ }
46
+ /**
47
+ * Deletes an entry
48
+ * @param {string} key String key
49
+ * @return {boolean} Preferably, returns a boolean to check if the deletion is actually successful
50
+ */
51
+ delete(key) {
52
+ return this.storage.delete(key);
53
+ }
54
+ /**
55
+ * Returns Array of string (not an iterator preferably, it can break)
56
+ * @return {string[]}
57
+ */
58
+ keys() {
59
+ return Array.from(this.storage.keys());
60
+ }
61
+ /**
62
+ * Returns size of cache
63
+ * @return {number}
64
+ */
65
+ size() {
66
+ return this.storage.size;
67
+ }
68
+ /**
69
+ * Clears cache
70
+ * @return {void}
71
+ */
72
+ clear() {
73
+ this.storage.clear();
74
+ }
75
+ }
76
+
77
+ module.exports = Cache;
78
+
@@ -21,11 +21,11 @@ module.exports = class RateLimit {
21
21
  }
22
22
 
23
23
  sync (data) {
24
- this.options.keyLimit = parseInt(data.get('ratelimit-limit')) || this.options.keyLimit;
25
- this.requests = parseInt(data.get('ratelimit-remaining')) || this.requests;
26
- if (data.get('ratelimit-reset') && Math.round(Date.now() / 1000) - (60 - parseInt(data.get('ratelimit-reset'))) != Math.round(this.lastResetHappenedAt / 1000)) {
24
+ this.options.keyLimit = parseInt(data.get('ratelimit-limit'), 10) || this.options.keyLimit;
25
+ this.requests = parseInt(data.get('ratelimit-remaining'), 10) || this.requests;
26
+ if (data.get('ratelimit-reset') && Math.round(Date.now() / 1000) - (60 - parseInt(data.get('ratelimit-reset'), 10)) !== Math.round(this.lastResetHappenedAt / 1000)) {
27
27
  clearTimeout(this.resetTimer);
28
- this.resetTimer = setTimeout(this.reset.bind(this), parseInt(data.get('ratelimit-reset')) * 1000);
28
+ this.resetTimer = setTimeout(this.reset.bind(this), parseInt(data.get('ratelimit-reset'), 10) * 1000);
29
29
  }
30
30
  }
31
31
 
@@ -2,10 +2,19 @@
2
2
  const fetch = require('node-fetch');
3
3
  const BASE_URL = 'https://api.hypixel.net';
4
4
  const Errors = require('../Errors');
5
- const cached = new Map();
5
+ const Cache = require('./defaultCache');
6
+
6
7
  module.exports = class Requests {
8
+ constructor(client, cache) {
9
+ if (cache && !this.validateCustomCache()) throw new Error(Errors.INVALID_CACHE_HANDLER);
10
+ /**
11
+ * @type {Cache}
12
+ */
13
+ this.cached = cache || new Cache();
14
+ this.client = client;
15
+ }
7
16
  async request (endpoint, options = {}) {
8
- options.headers = {'API-Key': this.key, ...options.headers};
17
+ options.headers = {'API-Key': this.client.key, ...options.headers};
9
18
  const res = await fetch(BASE_URL + endpoint, options);
10
19
  if (res.status >= 500 && res.status < 528) throw new Error(Errors.ERROR_STATUSTEXT.replace(/{statustext}/, `Server Error : ${res.status} ${res.statusText}`));
11
20
  const parsedRes = await res.json().catch(() => {
@@ -18,22 +27,28 @@ module.exports = class Requests {
18
27
  throw new Error(Errors.SOMETHING_WENT_WRONG.replace(/{cause}/, res.cause));
19
28
  }
20
29
  parsedRes._headers = res.headers;
30
+ parsedRes.raw = !!options.raw;
21
31
  if (options.noCaching) return parsedRes;
22
32
  // split by question mark : first part is /path, remove /
23
- if (this.options.cache && this.options.cacheFilter(endpoint.split('?')[0].slice(1))) {
24
- if (this.options.cacheSize < cached.size) cached.delete(cached.keys().next().value); // Map and its special "iterators"
25
- cached.set(endpoint, parsedRes);
26
- if (this.options.cacheTime >= 0) setTimeout(() => cached.delete(endpoint), 1000 * this.options.cacheTime);
33
+ if (this.client.options.cache && this.client.options.cacheFilter(endpoint.split('?')[0].slice(1))) {
34
+ if (this.client.options.cacheSize < await this.cached.size()) await this.cached.delete(Array.from(await this.cached.keys())[0]);
35
+ await this.cached.delete(endpoint);
36
+ await this.cached.set(endpoint, parsedRes);
37
+ if (this.client.options.cacheTime >= 0) setTimeout(() => this.cached.delete(endpoint), 1000 * this.client.options.cacheTime);
27
38
  }
28
39
  return parsedRes;
29
40
  }
30
41
 
31
42
  get cache () {
32
- return cached;
43
+ return this.cached;
33
44
  }
34
45
 
35
46
  async sweepCache (amount) {
36
- if (!amount || amount >= cached.size) return cached.clear();
37
- return Array.from(cached.keys()).slice(cached.size - amount).map((x) => cached.delete(x));
47
+ if (!amount || amount >= await this.cached.size()) return await this.cached.clear();
48
+ return await Promise.all(Array.from(await this.cached.keys()).slice(await this.cached.size() - amount).map((x) => this.cached.delete(x)));
49
+ }
50
+
51
+ validateCustomCache(cache) {
52
+ return !!(cache.set && cache.get && cache.delete && cache.keys);
38
53
  }
39
54
  };