aboutsummaryrefslogtreecommitdiff
path: root/src/cleaners/skyblock/profile.ts
blob: 0297081a4984ef2761985955051c7b419f7ee944 (plain)
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.replace(/-/g, ''),
        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.replace(/-/g, '')

    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.replace(/-/g, ''),
        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
}