diff options
Diffstat (limited to 'hypixelCached.ts')
-rw-r--r-- | hypixelCached.ts | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/hypixelCached.ts b/hypixelCached.ts new file mode 100644 index 0000000..5fe65a3 --- /dev/null +++ b/hypixelCached.ts @@ -0,0 +1,240 @@ +import NodeCache from 'node-cache' +import * as mojang from './mojang' +import * as hypixel from './hypixel' +import { CleanPlayer } from './cleaners/player' +import { CleanBasicProfile, CleanFullProfile, CleanProfile } from './hypixel' +import { undashUuid } from './util' + +/** +Hypixel... but with caching +All the caching in this project is done here! +*/ + + +// cache usernames for 4 hours +const usernameCache = new NodeCache({ + stdTTL: 60 * 60 * 4, + checkperiod: 60, + useClones: false, +}) + +const basicProfilesCache = new NodeCache({ + stdTTL: 60 * 10, + checkperiod: 60, + useClones: false, +}) + +const playerCache = new NodeCache({ + stdTTL: 60, + checkperiod: 10, + useClones: false, +}) + +const profileCache = new NodeCache({ + stdTTL: 30, + checkperiod: 10, + useClones: false, +}) + +const profilesCache = new NodeCache({ + stdTTL: 60 * 3, + checkperiod: 10, + useClones: false, +}) + +const profileNameCache = new NodeCache({ + stdTTL: 60 * 60, + checkperiod: 60, + useClones: false, +}) + +/** + * Fetch the uuid from a user + * @param user A user can be either a uuid or a username + */ +export async function uuidFromUser(user: string): Promise<string> { + if (usernameCache.has(undashUuid(user))) + // check if the uuid is a key + return undashUuid(user) + + // check if the username is a value + const uuidToUsername: {[ key: string ]: string} = usernameCache.mget(usernameCache.keys()) + for (const [ uuid, username ] of Object.entries(uuidToUsername)) { + if (user.toLowerCase() === username.toLowerCase()) + return uuid + } + + // not cached, actually fetch mojang api now + let { uuid, username } = await mojang.mojangDataFromUser(user) + + // remove dashes from the uuid so its more normal + uuid = undashUuid(uuid) + usernameCache.set(uuid, username) + return uuid +} + +/** + * Fetch the username from a user + * @param user A user can be either a uuid or a username + */ +export async function usernameFromUser(user: string): Promise<string> { + if (usernameCache.has(undashUuid(user))) { + return usernameCache.get(undashUuid(user)) + } + + let { uuid, username } = await mojang.mojangDataFromUser(user) + uuid = undashUuid(uuid) + usernameCache.set(uuid, username) + return username +} + + +export async function fetchPlayer(user: string): Promise<CleanPlayer> { + const playerUuid = await uuidFromUser(user) + + if (playerCache.has(playerUuid)) { + console.log('cache hit! fetchPlayer', playerUuid) + return playerCache.get(playerUuid) + } + + const cleanPlayer: CleanPlayer = await hypixel.sendCleanApiRequest({ + path: 'player', + args: { uuid: playerUuid } + }) + playerCache.set(playerUuid, cleanPlayer) + return cleanPlayer +} + + +export async function fetchSkyblockProfiles(playerUuid: string): Promise<CleanProfile[]> { + if (profilesCache.has(playerUuid)) { + console.log('cache hit! fetchSkyblockProfiles', playerUuid) + return profilesCache.get(playerUuid) + } + + const profiles: CleanFullProfile[] = await hypixel.sendCleanApiRequest({ + path: 'skyblock/profiles', + args: { + uuid: playerUuid + }}, + ) + + const basicProfiles: CleanProfile[] = [] + + // create the basicProfiles array + for (const profile of profiles) { + const basicProfile: CleanProfile = { + name: profile.name, + uuid: profile.uuid, + members: profile.members.map(m => { + return { + uuid: m.uuid, + username: m.username, + first_join: m.first_join, + last_save: m.last_save + } + }) + } + basicProfiles.push(basicProfile) + } + + // cache the profiles + profilesCache.set(playerUuid, basicProfiles) + + return basicProfiles +} + +/** Fetch an array of `BasicProfile`s */ +async function fetchBasicProfiles(user: string): Promise<CleanBasicProfile[]> { + const playerUuid = await uuidFromUser(user) + if (basicProfilesCache.has(playerUuid)) { + console.log('cache hit! fetchBasicProfiles') + return basicProfilesCache.get(playerUuid) + } + const player = await fetchPlayer(playerUuid) + const profiles = player.profiles + basicProfilesCache.set(playerUuid, profiles) + + console.log(player) + + // cache the profile names and uuids to profileNameCache because we can + for (const profile of profiles) + profileNameCache.set(`${playerUuid}.${profile.uuid}`, profile.name) + + return profiles +} + +/** + * Fetch a profile UUID from its name and user + * @param user A username or uuid + * @param profile A profile name or profile uuid + */ +export async function fetchProfileUuid(user: string, profile: string) { + const profiles = await fetchBasicProfiles(user) + + const profileUuid = undashUuid(profile) + + for (const p of profiles) { + if (p.name.toLowerCase() === profileUuid.toLowerCase()) + return undashUuid(p.uuid) + else if (undashUuid(p.uuid) === undashUuid(profileUuid)) + return undashUuid(p.uuid) + } +} + +/** + * Fetch an entire profile from the user and profile data + * @param user A username or uuid + * @param profile A profile name or profile uuid + */ +export async function fetchProfile(user: string, profile: string): Promise<CleanFullProfile> { + const profileUuid = await fetchProfileUuid(user, profile) + + if (profileCache.has(profileUuid)) { + console.log('cache hit! fetchProfile') + // we have the profile cached, return it :) + return profileCache.get(profileUuid) + } + + const profileName = await fetchProfileName(user, profile) + + const cleanProfile: CleanFullProfile = await hypixel.sendCleanApiRequest({ + path: 'skyblock/profile', + args: { + profile: profileUuid + } + }) + + // we know the name from fetchProfileName, so set it here + cleanProfile.name = profileName + + profileCache.set(profileUuid, cleanProfile) + + return cleanProfile +} + +/** + * Fetch the name of a profile from the user and profile uuid + * @param user A player uuid or username + * @param profile A profile uuid or name + */ +export async function fetchProfileName(user: string, profile: string): Promise<string> { + // we're fetching the profile and player uuid again in case we were given a name, but it's cached so it's not much of a problem + const profileUuid = await fetchProfileUuid(user, profile) + const playerUuid = await uuidFromUser(user) + + if (profileNameCache.has(`${playerUuid}.${profileUuid}`)) { + // Return the profile name if it's cached + console.log('cache hit! fetchProfileName') + return profileNameCache.get(`${playerUuid}.${profileUuid}`) + } + + const basicProfiles = await fetchBasicProfiles(playerUuid) + let profileName + for (const basicProfile of basicProfiles) + if (basicProfile.uuid === playerUuid) + profileName = basicProfile.name + + profileNameCache.set(`${playerUuid}.${profileUuid}`, profileName) + return profileName +}
\ No newline at end of file |