diff options
-rw-r--r-- | src/cleaners/skyblock/farmingContents.ts | 11 | ||||
-rw-r--r-- | src/cleaners/skyblock/member.ts | 3 | ||||
-rw-r--r-- | src/cleaners/skyblock/slayers.ts | 2 | ||||
-rw-r--r-- | src/cleaners/skyblock/stats.ts | 10 | ||||
-rw-r--r-- | src/constants.ts | 14 | ||||
-rw-r--r-- | src/database.ts | 67 |
6 files changed, 96 insertions, 11 deletions
diff --git a/src/cleaners/skyblock/farmingContents.ts b/src/cleaners/skyblock/farmingContents.ts index 218837d..75427ef 100644 --- a/src/cleaners/skyblock/farmingContents.ts +++ b/src/cleaners/skyblock/farmingContents.ts @@ -1,4 +1,5 @@ import typedHypixelApi from 'typed-hypixel-api' +import { addCrops } from '../../constants.js' import { cleanItemId } from './itemId.js' export interface PlayerFarmingContestStats { @@ -24,31 +25,37 @@ export interface FarmingContests { list: PlayerFarmingContestStats[] } -export function cleanFarmingContests(data: typedHypixelApi.SkyBlockProfileMember): FarmingContests { +export async function cleanFarmingContests(data: typedHypixelApi.SkyBlockProfileMember): Promise<FarmingContests> { if (!data.jacob2) return { talkedToJacob: false, list: [] } + let cropNames: Set<string> = new Set() + const contestsByDate: Record<string, PlayerFarmingContestStats['crops']> = {} for (const [contestName, contestData] of Object.entries(data.jacob2?.contests ?? {})) { const [year, monthDay, item] = contestName.split(':') const [month, day] = monthDay.split('_') const contestByDateKey = `${year}:${month}:${day}` + const cropId = cleanItemId(item) const cropData: PlayerFarmingContestStats['crops'][number] = { - item: cleanItemId(item), + item: cropId, amount: contestData.collected, // the api returns the position 0-indexed, so we add 1 position: contestData.claimed_position !== undefined ? contestData.claimed_position + 1 : null, claimed: contestData.claimed_rewards ?? null, participants: contestData.claimed_participants ?? null } + cropNames.add(cropId) if (!(contestByDateKey in contestsByDate)) contestsByDate[contestByDateKey] = [cropData] else contestsByDate[contestByDateKey].push(cropData) } + await addCrops(Array.from(cropNames)) + const contestsByDateEntries = Object.entries(contestsByDate) // this is to sort by newest first contestsByDateEntries.reverse() diff --git a/src/cleaners/skyblock/member.ts b/src/cleaners/skyblock/member.ts index 6642976..57462ed 100644 --- a/src/cleaners/skyblock/member.ts +++ b/src/cleaners/skyblock/member.ts @@ -79,6 +79,7 @@ export async function cleanSkyBlockProfileMemberResponse(member: typedHypixelApi const petsPromise = cleanPets(member) const harpPromise = cleanHarp(member) const inventoriesPromise = inventoriesIncluded ? cleanInventories(member) : Promise.resolve(undefined) + const farmingContestsPromise = cleanFarmingContests(member) return { uuid: member.uuid, @@ -106,7 +107,7 @@ export async function cleanSkyBlockProfileMemberResponse(member: typedHypixelApi pets: await petsPromise, harp: await harpPromise, coopInvitation: await coopInvitationPromise, - farmingContests: cleanFarmingContests(member), + farmingContests: await farmingContestsPromise, left: (player.profiles?.find(profile => profile.uuid === profileId) === undefined) ?? false } diff --git a/src/cleaners/skyblock/slayers.ts b/src/cleaners/skyblock/slayers.ts index b5dda0b..6516bbf 100644 --- a/src/cleaners/skyblock/slayers.ts +++ b/src/cleaners/skyblock/slayers.ts @@ -66,7 +66,7 @@ export function cleanSlayers(data: typedHypixelApi.SkyBlockProfileMember): Slaye for (const slayerDataKey in slayerDataRaw) { // if a key starts with boss_kills_tier_ (boss_kills_tier_1), get the last number if (slayerDataKey.startsWith('boss_kills_tier_')) { - const slayerTierRaw = parseInt(slayerDataKey.substr('boss_kills_tier_'.length)) + const slayerTierRaw = parseInt(slayerDataKey.slice('boss_kills_tier_'.length)) const slayerTierKills = slayerDataRaw[slayerDataKey] ?? 0 // add 1 since hypixel is using 0 indexed tiers const slayerTier = slayerTierRaw + 1 diff --git a/src/cleaners/skyblock/stats.ts b/src/cleaners/skyblock/stats.ts index 67099af..e5baa8f 100644 --- a/src/cleaners/skyblock/stats.ts +++ b/src/cleaners/skyblock/stats.ts @@ -9,6 +9,8 @@ const statCategories: { [key: string]: string[] | null } = { // sorted in order 'races': ['_best_time', '_best_time_2'], 'mythos': ['mythos_burrows_', 'mythos_kills'], + 'farming_contests': ['farming_contests_'], + 'collection': ['collection_'], 'skills': ['skill_'], 'slayer': ['slayer_'], @@ -37,16 +39,16 @@ export function categorizeStat(statNameRaw: string): StatCategory { for (const categoryMatch of statCategoryMatchers) { // ['deaths_'] let trailingEnd = categoryMatch[0] === '_' - let trailingStart = categoryMatch.substr(-1) === '_' + let trailingStart = categoryMatch.slice(-1) === '_' if (trailingStart && statNameRaw.startsWith(categoryMatch)) { return { category: statCategory, - name: statNameRaw.substr(categoryMatch.length) + name: statNameRaw.slice(categoryMatch.length) } } else if (trailingEnd && statNameRaw.endsWith(categoryMatch)) { return { category: statCategory, - name: statNameRaw.substr(0, statNameRaw.length - categoryMatch.length) + name: statNameRaw.slice(0, statNameRaw.length - categoryMatch.length) } } else if (statNameRaw == categoryMatch) { // if it matches exactly, we don't know the name. will be defaulted to category later on @@ -83,7 +85,7 @@ export function getStatUnit(name: string): string | null { for (const [unitName, statMatchers] of Object.entries(statUnits)) { for (const statMatch of statMatchers) { let trailingEnd = statMatch[0] === '_' - let trailingStart = statMatch.substr(-1) === '_' + let trailingStart = statMatch.slice(-1) === '_' if ( (trailingStart && name.startsWith(statMatch)) || (trailingEnd && name.endsWith(statMatch)) diff --git a/src/constants.ts b/src/constants.ts index 1371250..14254ed 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -120,6 +120,9 @@ async function editFile(file: GithubFile, message: string, newContent: string): } ) const data = await r.json() as any + if (!data.content?.path) + // failed to set the data, probably ratelimited or something + return fileCache.set(file.path, { path: data.content.path, content: newContent, @@ -254,6 +257,17 @@ export async function addHarpSongs(addingSongs: string[]): Promise<void> { } +/** Fetch all the known crops (used in farming contests) as an array of strings */ +export async function fetchCrops(): Promise<string[]> { + return await constants.fetchJSONConstant('crops.json') +} + +/** Add crop names (used in farming contests) to skyblock-constants. This has caching so it's fine to call many times */ +export async function addCrops(addingCrops: string[]): Promise<void> { + await constants.addJSONConstants('crops.json', addingCrops, 'crop') +} + + interface constantValues { max_minions?: number max_fairy_souls?: number diff --git a/src/database.ts b/src/database.ts index a95878f..fa4cbd2 100644 --- a/src/database.ts +++ b/src/database.ts @@ -184,6 +184,47 @@ function getMemberHarpAttributes(member: CleanMember): StringNumber { return harpAttributes } +function getFarmingContestAttributes(member: CleanMember): StringNumber { + const farmingContestAttributes: StringNumber = {} + + let participated = 0 + let top1 = 0 + + let participatedRecord: StringNumber = {} + let top1Record: StringNumber = {} + let highestScoreRecord: StringNumber = {} + + for (const contest of member.farmingContests.list) { + participated++ + for (const cropContest of contest.crops) { + if (participatedRecord[cropContest.item] === undefined) + participatedRecord[cropContest.item] = 0 + participatedRecord[cropContest.item]++ + + if (highestScoreRecord[cropContest.item] === undefined || highestScoreRecord[cropContest.item] < cropContest.amount) + highestScoreRecord[cropContest.item] = cropContest.amount + + if (cropContest.position === 1) { + top1++ + if (top1Record[cropContest.item] === undefined) + top1Record[cropContest.item] = 0 + top1Record[cropContest.item]++ + } + } + } + farmingContestAttributes['farming_contests_participated'] = participated + farmingContestAttributes['farming_contests_top_1'] = top1 + + for (const [cropName, value] of Object.entries(participatedRecord)) + farmingContestAttributes[`farming_contests_participated_${cropName}`] = value + for (const [cropName, value] of Object.entries(top1Record)) + farmingContestAttributes[`farming_contests_top_1_${cropName}`] = value + for (const [cropName, value] of Object.entries(highestScoreRecord)) + farmingContestAttributes[`farming_contests_highest_score_${cropName}`] = value + + return farmingContestAttributes +} + function getMemberLeaderboardAttributes(member: CleanMember): StringNumber { // if you want to add a new leaderboard for member attributes, add it here (and getAllLeaderboardAttributes) const data: StringNumber = { @@ -202,6 +243,9 @@ function getMemberLeaderboardAttributes(member: CleanMember): StringNumber { // harp leaderboards ...getMemberHarpAttributes(member), + // farming contest leaderboards + ...getFarmingContestAttributes(member), + fairy_souls: member.fairySouls.total, purse: member.purse, visited_zones: member.zones.filter(z => z.visited).length, @@ -270,7 +314,7 @@ export async function fetchSlayerLeaderboards(): Promise<string[]> { return leaderboardNames } -export async function fetchHarpLeaderboards(): Promise<string[]> { +async function fetchHarpLeaderboards(): Promise<string[]> { const harpSongs = await constants.fetchHarpSongs() const leaderboardNames: string[] = [] @@ -282,6 +326,20 @@ export async function fetchHarpLeaderboards(): Promise<string[]> { return leaderboardNames } +async function fetchFarmingContestLeaderboards(): Promise<string[]> { + const leaderboardNames: string[] = [] + + leaderboardNames.push(`farming_contests_participated`) + leaderboardNames.push(`farming_contests_top_1`) + for (const crop of await constants.fetchCrops()) { + leaderboardNames.push(`farming_contests_participated_${crop}`) + leaderboardNames.push(`farming_contests_top_1_${crop}`) + leaderboardNames.push(`farming_contests_highest_score_${crop}`) + } + + return leaderboardNames +} + /** Fetch the names of all the leaderboards that rank members */ export async function fetchAllMemberLeaderboardAttributes(): Promise<string[]> { return [ @@ -300,6 +358,9 @@ export async function fetchAllMemberLeaderboardAttributes(): Promise<string[]> { // harp leaderboards ...await fetchHarpLeaderboards(), + // farming contest leaderboards + ...await fetchFarmingContestLeaderboards(), + 'fairy_souls', 'first_join', 'last_save', @@ -308,7 +369,7 @@ export async function fetchAllMemberLeaderboardAttributes(): Promise<string[]> { 'leaderboards_count', 'top_1_leaderboards_count', 'fastest_coop_join', - 'slowest_coop_join' + 'slowest_coop_join', ] } @@ -322,7 +383,7 @@ async function fetchAllProfileLeaderboardAttributes(): Promise<string[]> { function isLeaderboardReversed(name: string): boolean { for (const leaderboardMatch of reversedLeaderboards) { let trailingEnd = leaderboardMatch[0] === '_' - let trailingStart = leaderboardMatch.substr(-1) === '_' + let trailingStart = leaderboardMatch.slice(-1) === '_' if ( (trailingStart && name.startsWith(leaderboardMatch)) || (trailingEnd && name.endsWith(leaderboardMatch)) |