aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authormat <github@matdoes.dev>2022-03-25 21:13:13 -0500
committermat <github@matdoes.dev>2022-03-25 21:13:13 -0500
commit6b94d0110a52a355758432ff6cd14c80553471ea (patch)
tree8113e5ee126b56f2d7e31309b5fa1fd65d008a77 /src
parent7f4e9c598d4697e63602bc36f9034610cac84604 (diff)
downloadskyblock-api-6b94d0110a52a355758432ff6cd14c80553471ea.tar.gz
skyblock-api-6b94d0110a52a355758432ff6cd14c80553471ea.tar.bz2
skyblock-api-6b94d0110a52a355758432ff6cd14c80553471ea.zip
Add typed-hypixel-api as a dep and change some code
Diffstat (limited to 'src')
-rw-r--r--src/cleaners/player.ts4
-rw-r--r--src/cleaners/rank.ts10
-rw-r--r--src/cleaners/skyblock/election.ts2
-rw-r--r--src/cleaners/skyblock/member.ts5
-rw-r--r--src/cleaners/skyblock/profile.ts15
-rw-r--r--src/cleaners/skyblock/profiles.ts13
-rw-r--r--src/hypixel.ts88
-rw-r--r--src/hypixelApi.ts148
-rw-r--r--src/hypixelCached.ts16
9 files changed, 125 insertions, 176 deletions
diff --git a/src/cleaners/player.ts b/src/cleaners/player.ts
index 1fd6f85..6467e59 100644
--- a/src/cleaners/player.ts
+++ b/src/cleaners/player.ts
@@ -1,8 +1,8 @@
+import { PlayerDataResponse as HypixelApiPlayerDataResponse } from 'typed-hypixel-api/build/responses/player'
import { cleanPlayerSkyblockProfiles } from './skyblock/profiles.js'
import { cleanSocialMedia, CleanSocialMedia } from './socialmedia.js'
import { CleanBasicProfile } from './skyblock/profile.js'
import { cleanRank, CleanRank } from './rank.js'
-import { HypixelPlayer } from '../hypixelApi.js'
import { undashUuid } from '../util.js'
export interface CleanBasicPlayer {
@@ -16,7 +16,7 @@ export interface CleanPlayer extends CleanBasicPlayer {
profiles?: CleanBasicProfile[]
}
-export async function cleanPlayerResponse(data: HypixelPlayer): Promise<CleanPlayer | null> {
+export async function cleanPlayerResponse(data: HypixelApiPlayerDataResponse['player']): Promise<CleanPlayer | null> {
// Cleans up a 'player' api response
if (!data)
return null // bruh
diff --git a/src/cleaners/rank.ts b/src/cleaners/rank.ts
index 417c8f1..ea7579e 100644
--- a/src/cleaners/rank.ts
+++ b/src/cleaners/rank.ts
@@ -1,7 +1,7 @@
+import { PlayerDataResponse as HypixelApiPlayerDataResponse } from 'typed-hypixel-api/build/responses/player'
import { colorCodeFromName, minecraftColorCodes } from '../util.js'
-import { HypixelPlayer } from '../hypixelApi.js'
-const rankColors: { [ name: string ]: string } = {
+const rankColors: { [name: string]: string } = {
'NONE': '7',
'VIP': 'a',
'VIP+': 'a',
@@ -29,7 +29,7 @@ export function cleanRank({
rankPlusColor,
rank,
prefix
-}: HypixelPlayer): CleanRank {
+}: HypixelApiPlayerDataResponse['player']): CleanRank {
let name: string | undefined
let color: string
let colored: string
@@ -48,7 +48,7 @@ export function cleanRank({
?? packageRank?.replace('_PLUS', '+')
switch (name) {
- // MVP++ is called Superstar for some reason
+ // MVP++ is called Superstar for some reason
case 'SUPERSTAR':
name = 'MVP++'
break
@@ -87,7 +87,7 @@ export function cleanRank({
if (bracketColor)
colored = `§${bracketColor}[${rankColorPrefix}${name}§${bracketColor}]`
else
- colored = `${rankColorPrefix}[${name}]`
+ colored = `${rankColorPrefix}[${name}]`
else
// nons don't have a prefix
colored = `${rankColorPrefix}`
diff --git a/src/cleaners/skyblock/election.ts b/src/cleaners/skyblock/election.ts
index a773f4a..9094e2d 100644
--- a/src/cleaners/skyblock/election.ts
+++ b/src/cleaners/skyblock/election.ts
@@ -39,7 +39,7 @@ function cleanCandidate(data: any, index: number): Candidate {
}
}
-export function cleanElectionResponse(data: any): ElectionData {
+export async function cleanElectionResponse(data: any): Promise<ElectionData> {
const previousCandidates = data.mayor.election.candidates.map(cleanCandidate)
return {
lastUpdated: data.lastUpdated,
diff --git a/src/cleaners/skyblock/member.ts b/src/cleaners/skyblock/member.ts
index 3430eac..e187beb 100644
--- a/src/cleaners/skyblock/member.ts
+++ b/src/cleaners/skyblock/member.ts
@@ -1,8 +1,9 @@
+import { ProfileMember as HypixelApiProfileMember } from 'typed-hypixel-api/build/responses/skyblock/_profile_member'
import { cleanCollections, Collection } from './collections.js'
import { cleanInventories, Inventories } from './inventory.js'
import { cleanFairySouls, FairySouls } from './fairysouls.js'
import { cleanObjectives, Objective } from './objectives.js'
-import { CleanBasicProfile, CleanFullProfileBasicMembers } from './profile.js'
+import { CleanFullProfileBasicMembers } from './profile.js'
import { cleanProfileStats, StatItem } from './stats.js'
import { CleanMinion, cleanMinions } from './minions.js'
import { cleanSlayers, SlayerData } from './slayers.js'
@@ -53,7 +54,7 @@ export async function cleanSkyBlockProfileMemberResponseBasic(member: any): Prom
}
/** Cleans up a member (from skyblock/profile) */
-export async function cleanSkyBlockProfileMemberResponse(member, profileId?: string, included: Included[] | undefined = undefined): Promise<CleanMember | null> {
+export async function cleanSkyBlockProfileMemberResponse(member: HypixelApiProfileMember & { uuid: string }, profileId?: string, included: Included[] | undefined = undefined): Promise<CleanMember | null> {
// profiles.members[]
const inventoriesIncluded = included === undefined || included.includes('inventories')
const player = await cached.fetchPlayer(member.uuid)
diff --git a/src/cleaners/skyblock/profile.ts b/src/cleaners/skyblock/profile.ts
index ed85861..03ff2eb 100644
--- a/src/cleaners/skyblock/profile.ts
+++ b/src/cleaners/skyblock/profile.ts
@@ -1,4 +1,5 @@
import { CleanBasicMember, CleanMember, cleanSkyBlockProfileMemberResponse, cleanSkyBlockProfileMemberResponseBasic } from './member.js'
+import { SkyBlockProfilesResponse as HypixelApiSkyBlockProfilesResponse } from 'typed-hypixel-api/build/responses/skyblock/profiles'
import { CleanMinion, combineMinionArrays, countUniqueMinions } from './minions.js'
import * as constants from '../../constants.js'
import { ApiOptions } from '../../hypixel.js'
@@ -48,7 +49,7 @@ export async function cleanSkyblockProfileResponseLighter(data): Promise<CleanPr
/**
* This function is somewhat costly and shouldn't be called often. Use cleanSkyblockProfileResponseLighter if you don't need all the data
*/
-export async function cleanSkyblockProfileResponse(data: any, options?: ApiOptions): Promise<CleanFullProfile | CleanProfile | null> {
+export async function cleanSkyblockProfileResponse<O extends ApiOptions>(data: HypixelApiSkyBlockProfilesResponse['profiles'][number], options?: O): Promise<(O['basic'] extends true ? CleanProfile : CleanFullProfile) | null> {
// We use Promise.all so it can fetch all the users at once instead of waiting for the previous promise to complete
const promises: Promise<CleanMember | null>[] = []
if (!data) return null
@@ -57,9 +58,9 @@ export async function cleanSkyblockProfileResponse(data: any, options?: ApiOptio
for (const memberUUID in data.members) {
const memberRaw = data.members[memberUUID]
- memberRaw.uuid = memberUUID
+ const memberRawWithUuid = { ...memberRaw, uuid: memberUUID }
promises.push(cleanSkyBlockProfileMemberResponse(
- memberRaw,
+ memberRawWithUuid,
profileId,
[
!options?.basic ? 'stats' : undefined,
@@ -72,11 +73,13 @@ export async function cleanSkyblockProfileResponse(data: any, options?: ApiOptio
const cleanedMembers: CleanMember[] = (await Promise.all(promises)).filter(m => m) as CleanMember[]
if (options?.basic) {
- return {
+ const cleanProfile: CleanProfile = {
uuid: profileId,
name: data.cute_name,
members: cleanedMembers,
}
+ // we have to do this because of the basic checking typing
+ return cleanProfile as any
}
const memberMinions: CleanMinion[][] = []
@@ -93,7 +96,7 @@ export async function cleanSkyblockProfileResponse(data: any, options?: ApiOptio
await constants.setConstantValues({ max_minions: uniqueMinions })
// return more detailed info
- return {
+ const cleanFullProfile: CleanFullProfile = {
uuid: data.profile_id,
name: data.cute_name,
members: cleanedMembers,
@@ -102,6 +105,8 @@ export async function cleanSkyblockProfileResponse(data: any, options?: ApiOptio
minionCount: uniqueMinions,
maxUniqueMinions: maxUniqueMinions ?? 0,
}
+ // we have to do this because of the basic checking typing
+ return cleanFullProfile as any
}
/** A basic profile that only includes the profile uuid and name */
diff --git a/src/cleaners/skyblock/profiles.ts b/src/cleaners/skyblock/profiles.ts
index 20c2104..ddab078 100644
--- a/src/cleaners/skyblock/profiles.ts
+++ b/src/cleaners/skyblock/profiles.ts
@@ -5,8 +5,11 @@ import {
CleanProfile,
cleanSkyblockProfileResponse
} from './profile.js'
+import { SkyBlockProfilesResponse } from 'typed-hypixel-api/build/responses/skyblock/profiles'
+
+export function cleanPlayerSkyblockProfiles(rawProfiles: HypixelPlayerStatsSkyBlockProfiles | undefined): CleanBasicProfile[] {
+ if (!rawProfiles) return []
-export function cleanPlayerSkyblockProfiles(rawProfiles: HypixelPlayerStatsSkyBlockProfiles): CleanBasicProfile[] {
let profiles: CleanBasicProfile[] = []
for (const profile of Object.values(rawProfiles ?? {})) {
profiles.push({
@@ -18,11 +21,11 @@ export function cleanPlayerSkyblockProfiles(rawProfiles: HypixelPlayerStatsSkyBl
}
/** Convert an array of raw profiles into clean profiles */
-export async function cleanSkyblockProfilesResponse(data: any[]): Promise<CleanProfile[]> {
- const promises: Promise<CleanProfile | CleanFullProfile | null>[] = []
- for (const profile of data ?? []) {
+export async function cleanSkyblockProfilesResponse(data: SkyBlockProfilesResponse['profiles']): Promise<CleanFullProfile[]> {
+ const promises: Promise<CleanFullProfile | null>[] = []
+ for (const profile of data) {
promises.push(cleanSkyblockProfileResponse(profile))
}
- const cleanedProfiles: CleanProfile[] = (await Promise.all(promises)).filter(p => p) as CleanProfile[]
+ const cleanedProfiles: CleanFullProfile[] = (await Promise.all(promises)).filter((p): p is CleanFullProfile => p !== null)
return cleanedProfiles
}
diff --git a/src/hypixel.ts b/src/hypixel.ts
index 6691e97..4ae299a 100644
--- a/src/hypixel.ts
+++ b/src/hypixel.ts
@@ -16,15 +16,15 @@ import {
queueUpdateDatabaseMember,
queueUpdateDatabaseProfile
} from './database.js'
+import { cleanElectionResponse, ElectionData } from './cleaners/skyblock/election.js'
import { CleanBasicMember, CleanMemberProfile } from './cleaners/skyblock/member.js'
-import { chooseApiKey, HypixelResponse, sendApiRequest } from './hypixelApi.js'
import { cleanSkyblockProfilesResponse } from './cleaners/skyblock/profiles.js'
import { CleanPlayer, cleanPlayerResponse } from './cleaners/player.js'
+import { chooseApiKey, sendApiRequest } from './hypixelApi.js'
+import typedHypixelApi from 'typed-hypixel-api'
import * as cached from './hypixelCached.js'
import { debug } from './index.js'
-import { sleep } from './util.js'
import { WithId } from 'mongodb'
-import { cleanElectionResponse, ElectionData } from './cleaners/skyblock/election.js'
export type Included = 'profiles' | 'player' | 'stats' | 'inventories' | undefined
@@ -36,7 +36,7 @@ export const maxMinion = 11
/**
* Send a request to api.hypixel.net using a random key, clean it up to be more useable, and return it
- */
+ */
export interface ApiOptions {
mainMemberUuid?: string
@@ -45,30 +45,29 @@ export interface ApiOptions {
}
/** Sends an API request to Hypixel and cleans it up. */
-export async function sendCleanApiRequest({ path, args }, included?: Included[], options?: ApiOptions): Promise<any> {
+export async function sendCleanApiRequest<P extends keyof typeof cleanResponseFunctions>(path: P, args: Omit<typedHypixelApi.Requests[P]['options'], 'key'>, options?: ApiOptions): Promise<Awaited<ReturnType<typeof cleanResponseFunctions[P]>>> {
const key = await chooseApiKey()
- const rawResponse = await sendApiRequest({ path, key, args })
- if (rawResponse.throttled) {
- // if it's throttled, wait a second and try again
- await sleep(1000)
- return await sendCleanApiRequest({ path, args }, included, options)
- }
+ const data = await sendApiRequest(path, { key, ...args })
// clean the response
- return await cleanResponse({ path, data: rawResponse }, options ?? {})
+ return await cleanResponse(path, data, options ?? {})
}
+const cleanResponseFunctions = {
+ 'player': (data, options) => cleanPlayerResponse(data.player),
+ 'skyblock/profile': (data, options) => cleanSkyblockProfileResponse(data.profile, options),
+ 'skyblock/profiles': (data, options) => cleanSkyblockProfilesResponse(data.profiles),
+ 'resources/skyblock/election': (data, options) => cleanElectionResponse(data)
+} as const
-async function cleanResponse({ path, data }: { path: string, data: HypixelResponse }, options: ApiOptions): Promise<any> {
+async function cleanResponse<P extends keyof typeof cleanResponseFunctions>(path: P, data: typedHypixelApi.Requests[P]['response'], options: ApiOptions): Promise<Awaited<ReturnType<typeof cleanResponseFunctions[P]>>> {
// Cleans up an api response
- switch (path) {
- case 'player': return await cleanPlayerResponse(data.player)
- case 'skyblock/profile': return await cleanSkyblockProfileResponse(data.profile, options)
- case 'skyblock/profiles': return await cleanSkyblockProfilesResponse(data.profiles)
- case 'resources/skyblock/election': return await cleanElectionResponse(data)
- }
+ const cleaningFunction: typeof cleanResponseFunctions[P] = cleanResponseFunctions[path]
+ const cleanedData = await cleaningFunction(data, options)
+ return cleanedData as Awaited<ReturnType<typeof cleanResponseFunctions[P]>>
}
+
/* ----------------------------- */
export interface UserAny {
@@ -93,13 +92,13 @@ export interface CleanUser {
* @param included lets you choose what is returned, so there's less processing required on the backend.
* used inclusions: player, profiles
*/
-export async function fetchUser({ user, uuid, username }: UserAny, included: Included[]=['player'], customization?: boolean): Promise<CleanUser | null> {
+export async function fetchUser({ user, uuid, username }: UserAny, included: Included[] = ['player'], customization?: boolean): Promise<CleanUser | null> {
if (!uuid) {
// If the uuid isn't provided, get it
if (!username && !user) return null
uuid = await cached.uuidFromUser((user ?? username)!)
}
- if (!uuid) {
+ if (!uuid) {
// the user doesn't exist.
if (debug) console.debug('error:', user, 'doesnt exist')
return null
@@ -146,7 +145,7 @@ export async function fetchUser({ user, uuid, username }: UserAny, included: Inc
player: playerData,
profiles: profilesData ?? basicProfilesData,
activeProfile: includeProfiles ? activeProfile!?.uuid : undefined,
- online: includeProfiles ? lastOnline > (Date.now() - saveInterval): undefined,
+ online: includeProfiles ? lastOnline > (Date.now() - saveInterval) : undefined,
customization: websiteAccount?.customization
}
}
@@ -219,19 +218,20 @@ export async function fetchMemberProfile(user: string, profile: string, customiz
* @param playerUuid The UUID of the Minecraft player
* @param profileUuid The UUID of the Hypixel SkyBlock profile
*/
- export async function fetchMemberProfileUncached(playerUuid: string, profileUuid: string): Promise<CleanFullProfile> {
- const profile: CleanFullProfile = await sendCleanApiRequest(
- {
- path: 'skyblock/profile',
- args: { profile: profileUuid }
- },
- undefined,
+export async function fetchMemberProfileUncached(playerUuid: string, profileUuid: string): Promise<null | CleanFullProfile> {
+ const profile = await sendCleanApiRequest(
+ 'skyblock/profile',
+ { profile: profileUuid },
{ mainMemberUuid: playerUuid }
)
+ // we check for minions in profile to filter out the CleanProfile type (as opposed to CleanFullProfile)
+ if (!profile || !('minions' in profile)) return null
+
// queue updating the leaderboard positions for the member, eventually
- for (const member of profile.members)
- queueUpdateDatabaseMember(member, profile)
+ if (profile.members)
+ for (const member of profile.members)
+ queueUpdateDatabaseMember(member, profile)
queueUpdateDatabaseProfile(profile)
return profile
@@ -242,13 +242,10 @@ export async function fetchMemberProfile(user: string, profile: string, customiz
* @param playerUuid The UUID of the Minecraft player
* @param profileUuid The UUID of the Hypixel SkyBlock profile
*/
- export async function fetchBasicProfileFromUuidUncached(profileUuid: string): Promise<CleanProfile> {
- const profile: CleanFullProfile = await sendCleanApiRequest(
- {
- path: 'skyblock/profile',
- args: { profile: profileUuid }
- },
- undefined,
+export async function fetchBasicProfileFromUuidUncached(profileUuid: string): Promise<CleanProfile | null> {
+ const profile = await sendCleanApiRequest(
+ 'skyblock/profile',
+ { profile: profileUuid },
{ basic: true }
)
@@ -258,11 +255,8 @@ export async function fetchMemberProfile(user: string, profile: string, customiz
export async function fetchMemberProfilesUncached(playerUuid: string): Promise<CleanFullProfile[]> {
const profiles: CleanFullProfile[] = await sendCleanApiRequest(
- {
- path: 'skyblock/profiles',
- args: { uuid: playerUuid }
- },
- undefined,
+ 'skyblock/profiles',
+ { uuid: playerUuid },
{
// only the inventories for the main player are generated, this is for optimization purposes
mainMemberUuid: playerUuid
@@ -299,10 +293,10 @@ export async function fetchElection(): Promise<ElectionData> {
}
isFetchingElection = true
- const election: ElectionData = await sendCleanApiRequest({
- path: 'resources/skyblock/election',
- args: {}
- })
+ const election: ElectionData = await sendCleanApiRequest(
+ 'resources/skyblock/election',
+ {}
+ )
isFetchingElection = false
cachedElectionData = election
diff --git a/src/hypixelApi.ts b/src/hypixelApi.ts
index 13866f9..186cec6 100644
--- a/src/hypixelApi.ts
+++ b/src/hypixelApi.ts
@@ -1,22 +1,16 @@
/**
* Fetch the raw Hypixel API
*/
-import { jsonToQuery, shuffle, sleep } from './util.js'
-import * as nodeFetch from 'node-fetch'
-import fetch from 'node-fetch'
+import { shuffle, sleep } from './util.js'
+import typedHypixelApi from 'typed-hypixel-api'
import { Agent } from 'https'
if (!process.env.hypixel_keys)
// if there's no hypixel keys in env, run dotenv
(await import('dotenv')).config()
-// We need to create an agent to prevent memory leaks and to only do dns lookups once
-const httpsAgent = new Agent({
- keepAlive: true
-})
-
-/** This array should only ever contain one item because using multiple hypixel api keys isn't allowed :) */
+/** This array should only ever contain one item because using multiple hypixel api keys isn't allowed :) */
const apiKeys = process.env?.hypixel_keys?.split(' ') ?? []
interface KeyUsage {
@@ -25,11 +19,9 @@ interface KeyUsage {
reset: number
}
-const apiKeyUsage: { [ key: string ]: KeyUsage } = {}
+const apiKeyUsage: { [key: string]: KeyUsage } = {}
// the usage amount the api key was on right before it reset
-const apiKeyMaxUsage: { [ key: string ]: number } = {}
-
-const baseHypixelAPI = 'https://api.hypixel.net'
+const apiKeyMaxUsage: { [key: string]: number } = {}
/** Choose the best current API key */
@@ -77,14 +69,14 @@ export function getKeyUsage() {
export interface HypixelResponse {
[key: string]: any | {
- success: boolean
- throttled?: boolean
- }
+ success: boolean
+ throttled?: boolean
+ }
}
export interface HypixelPlayerStatsSkyBlockProfiles {
- [ uuid: string ]: {
+ [uuid: string]: {
profile_id: string
cute_name: string
}
@@ -103,112 +95,66 @@ export interface HypixelPlayerSocialMedia {
}
}
-export interface HypixelPlayer {
- _id: string
- achievementsOneTime: string[]
- displayname: string
-
- firstLogin: number,
- lastLogin: number,
- lastLogout: number
-
- knownAliases: string[],
- knownAliasesLower: string[]
-
- networkExp: number
- playername: string
- stats: {
- SkyBlock: HypixelPlayerStatsSkyBlock
- [ name: string ]: any
- },
- timePlaying: number,
- uuid: string,
- achievements: { [ name: string ]: number },
- petConsumables: { [ name: string ]: number },
- vanityMeta: {
- packages: string[]
- },
-
- language: string,
- userLanguage?: string
-
- packageRank?: string
- newPackageRank?: string
- rankPlusColor?: string
- monthlyPackageRank?: string
- rank?: string
- prefix?: string
-
- claimed_potato_talisman?: number
- skyblock_free_cookie?: number
-
- socialMedia?: HypixelPlayerSocialMedia
-}
+
/** Send an HTTP request to the Hypixel API */
-export let sendApiRequest = async function sendApiRequest({ path, key, args }): Promise<HypixelResponse> {
+export let sendApiRequest = async<P extends keyof typedHypixelApi.Requests>(path: P, options: typedHypixelApi.Requests[P]['options']): Promise<typedHypixelApi.Requests[P]['response']> => {
// Send a raw http request to api.hypixel.net, and return the parsed json
- if (key)
- // If there's an api key, add it to the arguments
- args.key = key
-
- // Construct a url from the base api url, path, and arguments
- const fetchUrl = baseHypixelAPI + '/' + path + '?' + jsonToQuery(args)
-
- let fetchResponse: nodeFetch.Response
- let fetchJsonParsed: any
-
+ let response: typedHypixelApi.Requests[P]['response']
try {
- fetchResponse = await fetch(
- fetchUrl,
- { agent: () => httpsAgent }
+ response = await typedHypixelApi.request(
+ path,
+ options
)
- fetchJsonParsed = await fetchResponse.json()
} catch {
- // if there's an error, wait a second and try again
await sleep(1000)
- return await sendApiRequest({ path, key, args })
+ return await sendApiRequest(path, options)
}
- // bruh
- if (fetchJsonParsed.cause === 'This endpoint is currently disabled') {
- await sleep(30000)
- return await sendApiRequest({ path, key, args })
- }
+ if (!response.data.success) {
+ // bruh
+ if (response.data.cause === 'This endpoint is currently disabled') {
+ await sleep(30000)
+ return await sendApiRequest(path, options)
+ }
- // if the cause is "Invalid API key", remove the key from the list of keys and try again
- if (fetchJsonParsed.cause === 'Invalid API key') {
- if (apiKeys.includes(key)) {
- apiKeys.splice(apiKeys.indexOf(key), 1)
- console.log(`${key} is invalid, removing it from the list of keys`)
+ // if the cause is "Invalid API key", remove the key from the list of keys and try again
+ if ('key' in options && response.data.cause === 'Invalid API key') {
+ if (apiKeys.includes(options.key)) {
+ apiKeys.splice(apiKeys.indexOf(options.key), 1)
+ console.log(`${options.key} is invalid, removing it from the list of keys`)
+ }
+ return await sendApiRequest(path, {
+ ...options,
+ key: chooseApiKey()
+ })
}
- return await sendApiRequest({ path, key: chooseApiKey(), args })
}
- if (fetchResponse.headers.get('ratelimit-limit')) {
+ if ('key' in options && response.headers['RateLimit-Limit']) {
// remember how many uses it has
- apiKeyUsage[key] = {
- remaining: parseInt(fetchResponse.headers.get('ratelimit-remaining') ?? '0'),
- limit: parseInt(fetchResponse.headers.get('ratelimit-limit') ?? '0'),
- reset: Date.now() + parseInt(fetchResponse.headers.get('ratelimit-reset') ?? '0') * 1000 + 1000,
+ apiKeyUsage[options.key] = {
+ remaining: response.headers['RateLimit-Remaining'] ?? 0,
+ limit: response.headers['Ratelimit-Limit'] ?? 0,
+ reset: Date.now() + response.headers['Ratelimit-Reset'] ?? 0 * 1000 + 1000,
}
- let usage = apiKeyUsage[key].limit - apiKeyUsage[key].remaining
+ let usage = apiKeyUsage[options.key].limit - apiKeyUsage[options.key].remaining
// if it's not in apiKeyMaxUsage or this usage is higher, update it
- if (!apiKeyMaxUsage[key] || (usage > apiKeyMaxUsage[key]))
- apiKeyMaxUsage[key] = usage
+ if (!apiKeyMaxUsage[options.key] || (usage > apiKeyMaxUsage[options.key]))
+ apiKeyMaxUsage[options.key] = usage
}
-
- if (fetchJsonParsed.throttle) {
- if (apiKeyUsage[key])
- apiKeyUsage[key].remaining = 0
+
+ if ('key' in options && !response.data.success && 'throttle' in response.data && response.data.throttle) {
+ if (apiKeyUsage[options.key])
+ apiKeyUsage[options.key].remaining = 0
// if it's throttled, wait 10 seconds and try again
await sleep(10000)
- return await sendApiRequest({ path, key, args })
+ return await sendApiRequest(path, options)
}
- return fetchJsonParsed
+ return response
}
// this is necessary for mocking in the tests because es6
-export function mockSendApiRequest($value) { sendApiRequest = $value } \ No newline at end of file
+export function mockSendApiRequest($value) { sendApiRequest = $value }
diff --git a/src/hypixelCached.ts b/src/hypixelCached.ts
index bc34ef0..5a17c3c 100644
--- a/src/hypixelCached.ts
+++ b/src/hypixelCached.ts
@@ -174,10 +174,9 @@ export async function fetchPlayer(user: string): Promise<CleanPlayer | null> {
fetchingPlayers.add(playerUuid)
- const cleanPlayer: CleanPlayer = await hypixel.sendCleanApiRequest({
- path: 'player',
- args: { uuid: playerUuid }
- })
+ const cleanPlayer = await hypixel.sendCleanApiRequest('player',
+ { uuid: playerUuid }
+ )
fetchingPlayers.delete(playerUuid)
@@ -185,7 +184,7 @@ export async function fetchPlayer(user: string): Promise<CleanPlayer | null> {
playerCache.set(playerUuid, cleanPlayer)
usernameCache.set(playerUuid, cleanPlayer.username)
-
+
// clone in case it gets modified somehow later
const cleanBasicPlayer = Object.assign({}, cleanPlayer)
if (cleanBasicPlayer.profiles) {
@@ -335,7 +334,8 @@ export async function fetchProfile(user: string, profile: string): Promise<Clean
if (!profileName) return null // uhh this should never happen but if it does just return null
- const cleanProfile: CleanFullProfile = await hypixel.fetchMemberProfileUncached(playerUuid, profileUuid)
+ const cleanProfile = await hypixel.fetchMemberProfileUncached(playerUuid, profileUuid)
+ if (!cleanProfile) return null
// we know the name from fetchProfileName, so set it here
cleanProfile.name = profileName
@@ -349,12 +349,12 @@ export async function fetchProfile(user: string, profile: string): Promise<Clean
* Fetch a CleanProfile from the uuid
* @param profileUuid A profile name or profile uuid
*/
-export async function fetchBasicProfileFromUuid(profileUuid: string): Promise<CleanProfile | undefined> {
+export async function fetchBasicProfileFromUuid(profileUuid: string): Promise<CleanProfile | null> {
if (profileCache.has(profileUuid)) {
// we have the profile cached, return it :)
if (debug) console.debug('Cache hit! fetchBasicProfileFromUuid', profileUuid)
const profile: CleanFullProfile | undefined = profileCache.get(profileUuid)
- if (!profile) return undefined
+ if (!profile) return null
return {
uuid: profile.uuid,
members: profile.members.map(m => ({