diff options
Diffstat (limited to 'build/hypixelCached.js')
-rw-r--r-- | build/hypixelCached.js | 339 |
1 files changed, 0 insertions, 339 deletions
diff --git a/build/hypixelCached.js b/build/hypixelCached.js deleted file mode 100644 index 69bcc62..0000000 --- a/build/hypixelCached.js +++ /dev/null @@ -1,339 +0,0 @@ -/** - * Fetch the clean and cached Hypixel API - */ -import { isUuid, undashUuid } from './util.js'; -import * as hypixel from './hypixel.js'; -import * as mojang from './mojang.js'; -import NodeCache from 'node-cache'; -import { debug } from './index.js'; -import LRUCache from 'lru-cache'; -// cache usernames for 30 minutes -/** uuid: username */ -export const usernameCache = new NodeCache({ - // stdTTL: 60 * 60 * 4, - stdTTL: 60 * 30, - checkperiod: 60, - useClones: false, -}); -usernameCache.setMaxListeners(50); -export const basicProfilesCache = new NodeCache({ - stdTTL: 60 * 10, - checkperiod: 60, - useClones: true, -}); -export const playerCache = new NodeCache({ - stdTTL: 60, - checkperiod: 10, - useClones: true, -}); -// cache "basic players" (players without profiles) for 20 minutes -export const basicPlayerCache = new LRUCache({ - max: 10000, - maxAge: 60 * 20 * 1000, -}); -export const profileCache = new NodeCache({ - stdTTL: 30, - checkperiod: 10, - useClones: true, -}); -export const profilesCache = new NodeCache({ - stdTTL: 60 * 3, - checkperiod: 10, - useClones: false, -}); -export const profileNameCache = new NodeCache({ - stdTTL: 60 * 60, - checkperiod: 60, - useClones: false, -}); -function waitForCacheSet(cache, key, value) { - return new Promise((resolve, reject) => { - const listener = (setKey, setValue) => { - if (((setKey === key) || (value && setValue === value)) && typeof setValue === 'string') { - cache.removeListener('set', listener); - return resolve({ key: setKey, value: setValue }); - } - }; - cache.on('set', listener); - }); -} -/** - * Fetch the uuid from a user - * @param user A user can be either a uuid or a username - */ -export async function uuidFromUser(user) { - // if the user is 32 characters long, it has to be a uuid - if (isUuid(user)) - return undashUuid(user); - if (usernameCache.has(undashUuid(user))) { - // check if the uuid is a key - const username = usernameCache.get(undashUuid(user)); - // sometimes the username will be null, return that - if (username === null) - return undefined; - // if it has .then, then that means its a waitForCacheSet promise. This is done to prevent requests made while it is already requesting - if (username.then) { - const { key: uuid, value: _username } = await username; - usernameCache.set(uuid, _username); - return uuid; - } - else - return undashUuid(user); - } - // check if the username is a value - const uuidToUsername = usernameCache.mget(usernameCache.keys()); - for (const [uuid, username] of Object.entries(uuidToUsername)) { - if (username && username.toLowerCase && user.toLowerCase() === username.toLowerCase()) - return uuid; - } - if (debug) - console.debug('Cache miss: uuidFromUser', user); - const undashedUser = undashUuid(user); - // set it as waitForCacheSet (a promise) in case uuidFromUser gets called while its fetching mojang - usernameCache.set(undashedUser, waitForCacheSet(usernameCache, user, user)); - // not cached, actually fetch mojang api now - let { uuid, username } = await mojang.profileFromUser(user); - if (!uuid) { - usernameCache.set(user, null); - return; - } - // remove dashes from the uuid so its more normal - uuid = undashUuid(uuid); - usernameCache.del(undashedUser); - 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) { - if (usernameCache.has(undashUuid(user))) { - if (debug) - console.debug('Cache hit! usernameFromUser', user); - return usernameCache.get(undashUuid(user)) ?? null; - } - if (debug) - console.debug('Cache miss: usernameFromUser', user); - let { uuid, username } = await mojang.profileFromUser(user); - if (!uuid) - return null; - uuid = undashUuid(uuid); - usernameCache.set(uuid, username); - return username; -} -let fetchingPlayers = new Set(); -export async function fetchPlayer(user) { - const playerUuid = await uuidFromUser(user); - if (!playerUuid) - return null; - if (playerCache.has(playerUuid)) - return playerCache.get(playerUuid); - // if it's already in the process of fetching, check every 100ms until it's not fetching the player anymore and fetch it again, since it'll be cached now - if (fetchingPlayers.has(playerUuid)) { - while (fetchingPlayers.has(playerUuid)) { - await new Promise(resolve => setTimeout(resolve, 100)); - } - return await fetchPlayer(user); - } - fetchingPlayers.add(playerUuid); - const cleanPlayer = await hypixel.sendCleanApiRequest({ - path: 'player', - args: { uuid: playerUuid } - }); - fetchingPlayers.delete(playerUuid); - if (!cleanPlayer) - return null; - // clone in case it gets modified somehow later - playerCache.set(playerUuid, cleanPlayer); - usernameCache.set(playerUuid, cleanPlayer.username); - const cleanBasicPlayer = Object.assign({}, cleanPlayer); - delete cleanBasicPlayer.profiles; - basicPlayerCache.set(playerUuid, cleanBasicPlayer); - return cleanPlayer; -} -/** Fetch a player without their profiles. This is heavily cached. */ -export async function fetchBasicPlayer(user) { - const playerUuid = await uuidFromUser(user); - if (!playerUuid) - return null; - if (basicPlayerCache.has(playerUuid)) - return basicPlayerCache.get(playerUuid); - const player = await fetchPlayer(playerUuid); - if (!player) { - console.debug('no player? this should never happen, perhaps the uuid is invalid or the player hasn\'t played hypixel', playerUuid); - return null; - } - delete player.profiles; - return player; -} -export async function fetchSkyblockProfiles(playerUuid) { - if (profilesCache.has(playerUuid)) { - if (debug) - console.debug('Cache hit! fetchSkyblockProfiles', playerUuid); - return profilesCache.get(playerUuid); - } - if (debug) - console.debug('Cache miss: fetchSkyblockProfiles', playerUuid); - const profiles = await hypixel.fetchMemberProfilesUncached(playerUuid); - const basicProfiles = []; - // create the basicProfiles array - for (const profile of profiles) { - const basicProfile = { - 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, - rank: m.rank - }; - }) - }; - basicProfiles.push(basicProfile); - } - // cache the profiles - profilesCache.set(playerUuid, basicProfiles); - return basicProfiles; -} -/** Fetch an array of `BasicProfile`s */ -async function fetchBasicProfiles(user) { - const playerUuid = await uuidFromUser(user); - if (!playerUuid) - return null; // invalid player, just return - if (basicProfilesCache.has(playerUuid)) { - if (debug) - console.debug('Cache hit! fetchBasicProfiles', playerUuid); - return basicProfilesCache.get(playerUuid); - } - if (debug) - console.debug('Cache miss: fetchBasicProfiles', user); - const player = await fetchPlayer(playerUuid); - if (!player) { - console.log('bruh playerUuid', user, playerUuid); - return []; - } - const profiles = player.profiles; - basicProfilesCache.set(playerUuid, profiles); - if (!profiles) - return null; - // 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, profile) { - // if a profile wasn't provided, return - if (!profile) { - if (debug) - console.debug('no profile provided?', user, profile); - return null; - } - if (debug) - console.debug('Cache miss: fetchProfileUuid', user, profile); - const profiles = await fetchBasicProfiles(user); - if (!profiles) - return null; // user probably doesnt exist - 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); - } - return null; -} -/** - * 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, profile) { - const playerUuid = await uuidFromUser(user); - if (!playerUuid) - return null; - const profileUuid = await fetchProfileUuid(playerUuid, profile); - if (!profileUuid) - return null; - if (profileCache.has(profileUuid)) { - // we have the profile cached, return it :) - if (debug) - console.debug('Cache hit! fetchProfile', profileUuid); - return profileCache.get(profileUuid); - } - if (debug) - console.debug('Cache miss: fetchProfile', user, profile); - const profileName = await fetchProfileName(user, profile); - if (!profileName) - return null; // uhh this should never happen but if it does just return null - const cleanProfile = await hypixel.fetchMemberProfileUncached(playerUuid, profileUuid); - // we know the name from fetchProfileName, so set it here - cleanProfile.name = profileName; - profileCache.set(profileUuid, cleanProfile); - return cleanProfile; -} -/** - * Fetch a CleanProfile from the uuid - * @param profileUuid A profile name or profile uuid -*/ -export async function fetchBasicProfileFromUuid(profileUuid) { - if (profileCache.has(profileUuid)) { - // we have the profile cached, return it :) - if (debug) - console.debug('Cache hit! fetchBasicProfileFromUuid', profileUuid); - const profile = profileCache.get(profileUuid); - if (!profile) - return undefined; - return { - uuid: profile.uuid, - members: profile.members.map(m => ({ - uuid: m.uuid, - username: m.username, - last_save: m.last_save, - first_join: m.first_join, - rank: m.rank, - })), - name: profile.name - }; - } - // TODO: cache this - return await hypixel.fetchBasicProfileFromUuidUncached(profileUuid); -} -/** - * 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, profile) { - // 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); - if (!profileUuid) - return null; - const playerUuid = await uuidFromUser(user); - if (!playerUuid) - return null; - if (profileNameCache.has(`${playerUuid}.${profileUuid}`)) { - // Return the profile name if it's cached - if (debug) - console.debug('Cache hit! fetchProfileName', profileUuid); - return profileNameCache.get(`${playerUuid}.${profileUuid}`) ?? null; - } - if (debug) - console.debug('Cache miss: fetchProfileName', user, profile); - const basicProfiles = await fetchBasicProfiles(playerUuid); - if (!basicProfiles) - return null; - let profileName = null; - for (const basicProfile of basicProfiles) - if (basicProfile.uuid === playerUuid) - profileName = basicProfile.name ?? null; - profileNameCache.set(`${playerUuid}.${profileUuid}`, profileName); - return profileName; -} |