1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
import { CleanBasicMember, CleanMember, cleanSkyBlockProfileMemberResponse, cleanSkyBlockProfileMemberResponseBasic } from './member.js'
import { CleanMinion, combineMinionArrays, countUniqueMinions } from './minions.js'
import typedHypixelApi from 'typed-hypixel-api'
import * as constants from '../../constants.js'
import { ApiOptions } from '../../hypixel.js'
import { Bank, cleanBank } from './bank.js'
import { cleanGameMode, GameMode } from './gameMode.js'
export interface CleanProfile extends CleanBasicProfile {
members: CleanBasicMember[]
mode: GameMode
}
export interface CleanFullProfile extends CleanProfile {
members: CleanMember[]
bank: Bank
minions: CleanMinion[]
minionCount: number
maxUniqueMinions: number
}
export interface CleanFullProfileBasicMembers extends CleanProfile {
members: CleanBasicMember[]
bank: Bank
minions: CleanMinion[]
minionCount: number
maxUniqueMinions: number
}
/** Return a `CleanProfile` instead of a `CleanFullProfile`, useful when we need to get members but don't want to waste much ram */
export async function cleanSkyblockProfileResponseLighter(data: typedHypixelApi.SkyBlockProfile | typedHypixelApi.SkyBlockProfilesResponse['profiles'][number]): Promise<CleanProfile> {
// We use Promise.all so it can fetch all the usernames at once instead of waiting for the previous promise to complete
const promises: Promise<CleanBasicMember | null>[] = []
for (const memberUUID in data.members) {
const memberRaw = data.members[memberUUID]
const memberRawWithUuid = { ...memberRaw, uuid: memberUUID }
// we pass an empty array to make it not check stats
promises.push(cleanSkyBlockProfileMemberResponseBasic(memberRawWithUuid))
}
const cleanedMembers: CleanBasicMember[] = (await Promise.all(promises)).filter(m => m) as CleanBasicMember[]
return {
uuid: data.profile_id,
name: 'cute_name' in data ? data.cute_name : undefined,
members: cleanedMembers,
mode: cleanGameMode(data)
}
}
/**
* 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<O extends ApiOptions>(
data: typedHypixelApi.SkyBlockProfile | typedHypixelApi.SkyBlockProfilesResponse['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
const profileId = data.profile_id
for (const memberUUID in data.members) {
const memberRaw = data.members[memberUUID]
const memberRawWithUuid = { ...memberRaw, uuid: memberUUID }
promises.push(cleanSkyBlockProfileMemberResponse(
memberRawWithUuid,
profileId,
[
!options?.basic ? 'stats' : undefined,
options?.mainMemberUuid === memberUUID ? 'inventories' : undefined
]
))
}
const cleanedMembers: CleanMember[] = (await Promise.all(promises)).filter(m => m) as CleanMember[]
// sometimes it's ms since epoch and sometimes it's a string, so we
// just throw it into new Date() and js will figure it out for us
const lastSave = data.last_save ? (new Date(data.last_save).getTime()) : undefined
// also set the lastSave in the member if options.mainMemberUuid matches
if (options?.mainMemberUuid) {
const mainMember = cleanedMembers.find(m => m.uuid === options.mainMemberUuid)
if (mainMember) mainMember.lastSave = lastSave ?? null
}
if (options?.basic) {
const cleanProfile: CleanProfile = {
uuid: profileId,
name: 'cute_name' in data ? data.cute_name : undefined,
members: cleanedMembers,
mode: cleanGameMode(data),
lastSave
}
// we have to do this because of the basic checking typing
return cleanProfile as any
}
const memberMinions: CleanMinion[][] = []
for (const member of cleanedMembers) {
memberMinions.push(member.minions)
}
const minions: CleanMinion[] = combineMinionArrays(memberMinions)
const { max_minions: maxUniqueMinions } = await constants.fetchConstantValues()
const uniqueMinions = countUniqueMinions(minions)
if (uniqueMinions > (maxUniqueMinions ?? 0))
await constants.setConstantValues({ max_minions: uniqueMinions })
// return more detailed info
const cleanFullProfile: CleanFullProfile = {
uuid: data.profile_id,
name: 'cute_name' in data ? data.cute_name : undefined,
members: cleanedMembers,
bank: cleanBank(data),
minions: minions,
minionCount: uniqueMinions,
maxUniqueMinions: maxUniqueMinions ?? 0,
mode: cleanGameMode(data),
lastSave
}
return cleanFullProfile
}
/** A basic profile that only includes the profile uuid and name */
export interface CleanBasicProfile {
uuid: string
// the name depends on the user, so its sometimes not included
name?: string
/** Timestamp for when the profile was last saved for the user. */
lastSave?: number
}
|