This repository has been archived by the owner on Dec 11, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
339 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
node_modules/ | ||
.git | ||
oldreborn/ | ||
.github/ | ||
.eslintcache | ||
.env | ||
build/ | ||
.eslintcache | ||
.git | ||
.gitignore |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import Errors from '../Errors'; | ||
|
||
class RateLimit { | ||
initialized: number; | ||
requests: any; | ||
requestQueue: any; | ||
options: any; | ||
lastResetHappenedAt: any; | ||
resetTimer: any; | ||
cooldownTime: any; | ||
client: any; | ||
static options: any; | ||
static requests: any; | ||
static lastResetHappenedAt: number; | ||
static reset: any; | ||
constructor() { | ||
this.initialized = 0; | ||
} | ||
|
||
static async rateLimitManager() { | ||
if (!this.initialized) return; | ||
this.requests++; | ||
this.requestQueue.unshift(Date.now()); | ||
if ('NONE' === this.options.rateLimit || !this.requestQueue.length) return; | ||
if ('AUTO' === this.options.rateLimit && this.requests <= this.options.keyLimit / 2) return; | ||
const cooldown = this.computeCooldownTime(); | ||
this.requestQueue[0] = Date.now() + cooldown; | ||
return await new Promise((r) => setTimeout(r, cooldown)); | ||
} | ||
|
||
static sync(data: any): void { | ||
this.options.keyLimit = parseInt(data.get('ratelimit-limit'), 10) || this.options.keyLimit; | ||
this.requests = parseInt(data.get('ratelimit-remaining'), 10) || this.requests; | ||
if ( | ||
data.get('ratelimit-reset') && | ||
Math.round(Date.now() / 1000) - (300 - parseInt(data.get('ratelimit-reset'), 10)) !== | ||
Math.round(this.lastResetHappenedAt / 1000) | ||
) { | ||
clearTimeout(this.resetTimer); | ||
this.resetTimer = setTimeout(this.reset.bind(this), parseInt(data.get('ratelimit-reset'), 10) * 1000); | ||
} | ||
} | ||
static resetTimer(resetTimer: any) { | ||
throw new Error('Method not implemented.'); | ||
} | ||
|
||
computeCooldownTime() { | ||
const overhead = this.requestQueue[1] <= Date.now() ? 0 : this.requestQueue[1] - Date.now(); | ||
const multiplier = Math.floor(this.requests / this.options.keyLimit) + 1; | ||
return ( | ||
overhead + | ||
(-overhead - Date.now() + 300000 * multiplier + this.lastResetHappenedAt) / | ||
(this.options.keyLimit * multiplier - this.requests) | ||
); | ||
} | ||
|
||
reset() { | ||
this.requests = this.requests - this.options.keyLimit; | ||
if (0 > this.requests) this.requests = 0; | ||
this.lastResetHappenedAt = Date.now(); | ||
this.resetTimer = setTimeout(this.reset.bind(this), 300000); | ||
this.requestQueue = this.requestQueue.filter((x: any) => x >= Date.now()); | ||
} | ||
|
||
rateLimitMonitor() { | ||
this.resetTimer = setTimeout(this.reset.bind(this), 1000 * 300); | ||
} | ||
|
||
init(keyInfo: any, options: any, client: any) { | ||
this.options = options; | ||
this.requests = 0; | ||
this.cooldownTime = 300000 / this.options.keyLimit; | ||
this.requestQueue = []; | ||
this.client = client; | ||
return keyInfo | ||
.then((info: any) => { | ||
this.requests = info.requestsInPastMin; | ||
this.lastResetHappenedAt = Date.now() - (300 - info.resetsAfter) * 1000; | ||
this.resetTimer = setTimeout(this.rateLimitMonitor.bind(this), 1000 * info.resetsAfter); | ||
this.initialized = 1; | ||
}) | ||
.catch(() => { | ||
client.emit('warn', Errors.RATE_LIMIT_INIT_ERROR); | ||
this.requests = 0; | ||
this.lastResetHappenedAt = Date.now(); | ||
this.rateLimitMonitor(); | ||
this.initialized = 1; | ||
}); | ||
// Still make the requests per min possible | ||
} | ||
} | ||
|
||
export default RateLimit; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
const BASE_URL = 'https://api.hypixel.net/v2'; | ||
import Cache from './defaultCache'; | ||
import Errors from '../Errors'; | ||
import axios from 'axios'; | ||
|
||
function validateCustomCache(cache: any) { | ||
return Boolean(cache.set && cache.get && cache.delete && cache.keys); | ||
} | ||
|
||
class Requests { | ||
cached: any; | ||
client: any; | ||
constructor(client: any, cache: any) { | ||
if (cache && !validateCustomCache(cache)) throw new Error(Errors.INVALID_CACHE_HANDLER); | ||
this.cached = cache || new Cache(); | ||
this.client = client; | ||
} | ||
async request(endpoint: any, options: any = {}) { | ||
options.headers = { 'API-Key': this.client.key, ...options.headers }; | ||
const res = await axios.get(BASE_URL + endpoint, options); | ||
if (500 <= res.status && 528 > res.status) { | ||
throw new Error( | ||
Errors.ERROR_STATUSTEXT.replace(/{statustext}/, `Server Error : ${res.status} ${res.statusText}`) | ||
); | ||
} | ||
const parsedRes = await res.data.json().catch(() => { | ||
throw new Error(Errors.INVALID_RESPONSE_BODY); | ||
}); | ||
if (400 === res.status) { | ||
throw new Error( | ||
Errors.ERROR_CODE_CAUSE.replace(/{code}/, '400 Bad Request').replace(/{cause}/, parsedRes.cause || '') | ||
); | ||
} | ||
if (403 === res.status) throw new Error(Errors.INVALID_API_KEY); | ||
if (422 === res.status) throw new Error(Errors.UNEXPECTED_ERROR); | ||
if (429 === res.status) throw new Error(Errors.RATE_LIMIT_EXCEEDED); | ||
if (200 !== res.status) throw new Error(Errors.ERROR_STATUSTEXT.replace(/{statustext}/, res.statusText)); | ||
if (!parsedRes.success && !endpoint.startsWith('/housing')) { | ||
throw new Error(Errors.SOMETHING_WENT_WRONG.replace(/{cause}/, res.statusText)); | ||
} | ||
// eslint-disable-next-line no-underscore-dangle | ||
parsedRes._headers = res.headers; | ||
parsedRes.raw = Boolean(options.raw); | ||
if (options.noCaching) return parsedRes; | ||
// split by question mark : first part is /path, remove / | ||
if (this.client.options.cache && this.client.options.cacheFilter(endpoint.split('?')[0].slice(1))) { | ||
if (this.client.options.cacheSize < (await this.cached.size())) { | ||
await this.cached.delete(Array.from(await this.cached.keys())[0]); | ||
} | ||
await this.cached.delete(endpoint); | ||
await this.cached.set(endpoint, parsedRes); | ||
if (0 <= this.client.options.hypixelCacheTime) { | ||
setTimeout(() => this.cached.delete(endpoint), 1000 * this.client.options.hypixelCacheTime); | ||
} | ||
} | ||
return parsedRes; | ||
} | ||
|
||
get cache() { | ||
return this.cached; | ||
} | ||
|
||
async sweepCache(amount: any) { | ||
if (!amount || amount >= (await this.cached.size())) return await this.cached.clear(); | ||
return await Promise.all( | ||
Array.from(await this.cached.keys()) | ||
.slice((await this.cached.size()) - amount) | ||
.map((x) => this.cached.delete(x)) | ||
); | ||
} | ||
} | ||
|
||
export default Requests; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { ClientOptions } from '../typings'; | ||
import Errors from '../Errors'; | ||
|
||
/** | ||
* Validation Class, used internally to validate provided arguments | ||
*/ | ||
class Validation { | ||
static validateOptions(options: ClientOptions): void { | ||
if ('number' !== typeof options.hypixelCacheTime) throw new Error(Errors.CACHE_TIME_MUST_BE_A_NUMBER); | ||
if ('number' !== typeof options.mojangCacheTime) throw new Error(Errors.CACHE_TIME_MUST_BE_A_NUMBER); | ||
if ('number' !== typeof options.cacheSize) throw new Error(Errors.CACHE_LIMIT_MUST_BE_A_NUMBER); | ||
if ('string' !== typeof options.rateLimit || !['AUTO', 'HARD', 'NONE'].includes(options.rateLimit)) { | ||
throw new Error(Errors.INVALID_RATE_LIMIT_OPTION); | ||
} | ||
if ('number' !== typeof options.keyLimit) throw new Error(Errors.INVALID_KEY_LIMIT_OPTION); | ||
if ('boolean' !== typeof options.syncWithHeaders) throw new Error(Errors.INVALID_HEADER_SYNC_OPTION); | ||
if ('object' !== typeof options.headers) throw new Error(Errors.INVALID_HEADERS); | ||
if ('boolean' !== typeof options.silent) throw new Error(Errors.INVALID_SILENT_OPTION); | ||
if ('boolean' !== typeof options.checkForUpdates) throw new Error(Errors.INVALID_UPDATE_OPTION); | ||
if (!['boolean', 'string'].includes(typeof options.useThirdPartyAPI)) { | ||
throw new Error(Errors.INVALID_THIRD_PARTY_API_OPTION); | ||
} | ||
} | ||
|
||
static parseOptions(options: ClientOptions): ClientOptions { | ||
if ('object' !== typeof options || null === options) throw new Error(Errors.OPTIONS_MUST_BE_AN_OBJECT); | ||
return { | ||
cache: options.cache ?? true, | ||
hypixelCacheTime: options.hypixelCacheTime ?? 60, | ||
mojangCacheTime: options.mojangCacheTime ?? 600, | ||
cacheSize: (-1 === options.cacheSize ? Infinity : options.cacheSize) || Infinity, | ||
rateLimit: options.rateLimit ?? 'AUTO', | ||
keyLimit: options.keyLimit ?? 60, | ||
syncWithHeaders: Boolean(options.syncWithHeaders), | ||
headers: options.headers ?? {}, | ||
silent: Boolean(options.silent), | ||
checkForUpdates: options.checkForUpdates ?? true, | ||
useThirdPartyAPI: options.useThirdPartyAPI ?? false | ||
}; | ||
} | ||
|
||
static validateKey(key: string): string { | ||
if (!key) throw new Error(Errors.NO_API_KEY); | ||
if ('string' !== typeof key) throw new Error(Errors.KEY_MUST_BE_A_STRING); | ||
return key; | ||
} | ||
|
||
static cacheSuboptions(input: any): boolean { | ||
if ('object' !== typeof input || null === input) return false; | ||
if (!input.noCacheCheck && !input.noCaching && !input.raw) return false; | ||
return true; | ||
} | ||
|
||
static validateNodeVersion() { | ||
const versionMatch = process.version.match(/v(\d{2})\.\d{1,}\.\d+/); | ||
const nodeVersion = parseInt(versionMatch ? versionMatch[1] : '', 10); | ||
if (12 > nodeVersion) throw new Error(Errors.NODE_VERSION_ERR); | ||
} | ||
} | ||
|
||
export default Validation; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.