diff options
author | mat <github@matdoes.dev> | 2022-03-25 21:13:13 -0500 |
---|---|---|
committer | mat <github@matdoes.dev> | 2022-03-25 21:13:13 -0500 |
commit | 6b94d0110a52a355758432ff6cd14c80553471ea (patch) | |
tree | 8113e5ee126b56f2d7e31309b5fa1fd65d008a77 /src | |
parent | 7f4e9c598d4697e63602bc36f9034610cac84604 (diff) | |
download | skyblock-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.ts | 4 | ||||
-rw-r--r-- | src/cleaners/rank.ts | 10 | ||||
-rw-r--r-- | src/cleaners/skyblock/election.ts | 2 | ||||
-rw-r--r-- | src/cleaners/skyblock/member.ts | 5 | ||||
-rw-r--r-- | src/cleaners/skyblock/profile.ts | 15 | ||||
-rw-r--r-- | src/cleaners/skyblock/profiles.ts | 13 | ||||
-rw-r--r-- | src/hypixel.ts | 88 | ||||
-rw-r--r-- | src/hypixelApi.ts | 148 | ||||
-rw-r--r-- | src/hypixelCached.ts | 16 |
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 => ({ |