@zikeji/hypixel
Version:
With IntelliSense support & test coverage, this is an unopinionated async/await API wrapper for Hypixel's Public API. It is developed in TypeScript complete with documentation, typed interfaces for all API responses, built-in rate-limit handling, flexible
177 lines (171 loc) • 6.36 kB
text/typescript
import type { ResourcesSkyblockCollectionsResponse } from "../types/AugmentedTypes";
import type { SkyBlockProfile } from "../types/Augmented/SkyBlock/Profile";
/**
* Interface describing the accumulated information of all members on a profile.
* @hidden
*/
interface ProfileCollectionValues {
unlockedCollTiers: Set<string>;
collectionQuantities: {
[collectionName: string]: number;
};
}
/**
* Interface that describes a collection category, e.g. "Farming"
*/
export interface SkyBlockProfileCollectionGroup {
/** The ID of the group, e.g. "FARMING" */
id: string;
/** The name of the group, e.g. "Farming" */
name: string;
/**
* A number representing the percentage progress the profile is through this group, e.g. "100" or "83.33333333333334"
* @example
* ```typescript
* const progress = collections[0].progress;
* console.log(progress);
* // output:
* 83.33333333333334
*
* const percent = Math.round(progress * 100) / 100;
* console.log(percent);
* // output:
* 83.33
* ```
*/
progress: number;
/** Collection children that the profile has reached the maximum tier of. */
maxedChildCollections: number;
/** How many collections are in this collection group. */
totalCollections: number;
/** The children of this collection group. */
children: SkyBlockProfileCollection[];
}
/**
* Interface describing an individual collection.
*/
export interface SkyBlockProfileCollection {
/** The ID of this collection, e.g. "LOG:2" */
id: string;
/** The name of this collection, e.g. "Birch Wood" */
name: string;
/** The tier the profile has reached. */
tier: number;
/** The next tier the profile can reach. If the profile has reached the max tier, this value is omitted. */
nextTier?: number;
/** The maximum tier of this collection. */
maxTier: number;
/**
* The amount of resources in this collection the profile has collected toward tiers.
*
* **Note:**
* If the profile is a coop and all players do not have their collection API enabled, this will only account for the amounts collected by members with their collection API enabled.
*/
amount: number;
/** The amount required to reach the next collection tier. If the profile has reached the max tier, this value is omitted. */
nextTierAmountRequired?: number;
/** The progress the profile is toward maxing this collection. */
progress: number;
}
export type SkyBlockProfileCollections = SkyBlockProfileCollectionGroup[];
/**
* This helper takes a profile and scans all of it's member's to get the most accurate collection information possible. Returns false is none of the members of the profile had their collections API enabled.
* @param profile The SkyBlock profile object you want to check.
* @param collections The collections resource object.
* @category Helper
*/
export function getSkyBlockProfileMemberCollections(
profile: Pick<NonNullable<SkyBlockProfile>, "members">,
collections: ResourcesSkyblockCollectionsResponse["collections"]
): SkyBlockProfileCollections | false {
const profileCollectionValues = Object.values(profile.members).reduce(
(acc, member) => {
/* istanbul ignore else */
if (member.player_data?.unlocked_coll_tiers) {
member.player_data?.unlocked_coll_tiers.forEach((uTier) => {
acc.unlockedCollTiers.add(uTier);
});
}
/* istanbul ignore else */
if (member.collection) {
Object.entries(member.collection).forEach(([key, quantity]) => {
/* istanbul ignore else */
if (!acc.collectionQuantities[key]) acc.collectionQuantities[key] = 0;
acc.collectionQuantities[key] +=
quantity /* istanbul ignore next */ || 0;
});
}
return acc;
},
{
unlockedCollTiers: new Set(),
collectionQuantities: {},
} as ProfileCollectionValues
);
if (profileCollectionValues.unlockedCollTiers.size === 0) {
return false;
}
return Object.entries(collections).reduce(
(acc, [collectionGroupId, collectionGroup]) => {
const group: SkyBlockProfileCollectionGroup = {
id: collectionGroupId,
name: collectionGroup?.name ?? "Unknown",
progress: 0,
maxedChildCollections: 0,
totalCollections: 0,
children: [],
};
Object.entries(collectionGroup?.items ?? []).forEach(
([collectionId, collection]) => {
group.totalCollections += 1;
const child: SkyBlockProfileCollection = {
id: collectionId,
name: collection.name,
tier: collection.tiers.reduce((tier, tierInfo) => {
if (
profileCollectionValues.unlockedCollTiers.has(
`${collectionId}_${tierInfo.tier}`
) ||
(profileCollectionValues.collectionQuantities[collectionId] ??
0) > (tierInfo.amountRequired /* istanbul ignore next */ || 0)
) {
// eslint-disable-next-line no-param-reassign
tier = tierInfo.tier as number;
}
return tier;
}, 0),
nextTier: 0,
maxTier: collection.maxTiers,
amount:
profileCollectionValues.collectionQuantities[collectionId] ?? 0,
nextTierAmountRequired: 0,
progress: 0,
};
if (child.tier === child.maxTier) {
delete child.nextTier;
delete child.nextTierAmountRequired;
group.maxedChildCollections += 1;
child.progress = 100;
} else {
child.nextTier = child.tier + 1;
child.nextTierAmountRequired =
/* istanbul ignore next */
collection.tiers.find((tInfo) => tInfo.tier === child.nextTier)
?.amountRequired /* istanbul ignore next */ || 0;
child.progress =
((profileCollectionValues.collectionQuantities[collectionId] ||
0) /
child.nextTierAmountRequired) *
100;
}
group.children.push(child);
}
);
group.progress =
(group.maxedChildCollections / group.totalCollections) * 100;
acc.push(group);
return acc;
},
[] as SkyBlockProfileCollections
);
}