Skip to content

Commit

Permalink
Update the Beatmap interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
TTTaevas committed Nov 20, 2023
1 parent f159b3a commit 8df8605
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 67 deletions.
89 changes: 48 additions & 41 deletions lib/beatmap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,6 @@ export interface BeatmapsetExtendedPlus extends BeatmapsetExtended {
id: number
name: string
}
has_favourited: boolean
language: {
id: number
name: string
Expand All @@ -207,57 +206,65 @@ export interface BeatmapsetExtendedPlus extends BeatmapsetExtended {
recent_favourites: User[]
related_users: User[]
user: User
/**
* Only exists if authorized user
*/
has_favourited?: boolean
}

/**
* @privateRemarks While not expected from anywhere, it should be exported for ease of use purposes
*/
export interface BeatmapDifficultyAttributes {
star_rating: number
max_combo: number
/**
* osu
*/
aim_difficulty?: number
/**
* osu, taiko, fruits
*/
approach_rate?: number
/**
* osu
*/
flashlight_difficulty?: number
/**
* osu
*/
overall_difficulty?: number
/**
* osu
*/
slider_factor?: number
/**
* osu
*/
speed_difficulty?: number
/**
* taiko
*/
stamina_difficulty?: number
/**
* taiko
*/
}

/**
* Expected from api.getBeatmapDifficultyAttributesOsu()
*/
export interface BeatmapDifficultyAttributesOsu extends BeatmapDifficultyAttributes {
aim_difficulty: number
speed_difficulty: number
speed_note_count: number
flashlight_difficulty: number
slider_factor: number
approach_rate: number
overall_difficulty: number
}

/**
* Expected from api.getBeatmapDifficultyAttributesTaiko()
*/
export interface BeatmapDifficultyAttributesTaiko extends BeatmapDifficultyAttributes {
stamina_difficulty: number
rhythm_difficulty: number
/**
* taiko
*/
colour_difficulty: number
/**
* taiko, mania
*/
peak_difficulty: number
great_hit_window: number
}

/**
* Expected from api.getBeatmapDifficultyAttributesFruits()
*/
export interface BeatmapDifficultyAttributesFruits extends BeatmapDifficultyAttributes {
approach_rate: number
}

/**
* Expected from api.getBeatmapDifficultyAttributesMania()
*/
export interface BeatmapDifficultyAttributesMania extends BeatmapDifficultyAttributes {
great_hit_window: number
/**
* mania
* @remarks (2023-11-20) Doesn't exist anymore?
*/
score_multiplier: number
score_multiplier?: number
}

/**
* Expected from api.getBeatmapPack(), api.getBeatmapPacks()
*/
export interface BeatmapPack {
author: string
date: Date
Expand All @@ -266,7 +273,7 @@ export interface BeatmapPack {
* Are difficulty reduction mods unable to be used to clear this pack? (is `false` if you can use such mods)
*/
no_diff_reduction: boolean
ruleset_id: number,
ruleset_id: number | null,
tag: string,
url: string,
beatmapsets?: BeatmapsetExtended[],
Expand Down
46 changes: 36 additions & 10 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fetch, { FetchError } from "node-fetch"
import querystring from "querystring"
import { User, UserExtended, KudosuHistory, UserWithKudosu, UserWithCountryCoverGroupsStatisticsSupport, UserExtendedWithStatisticsrulesets, UserWithCountryCoverGroupsStatisticsrulesets } from "./user.js"
import { Beatmap, BeatmapExtended, BeatmapDifficultyAttributes, BeatmapPack, Beatmapset, BeatmapsetExtended, BeatmapExtendedWithFailtimesBeatmapsetextended, BeatmapsetExtendedPlus } from "./beatmap.js"
import { Beatmap, BeatmapExtended, BeatmapDifficultyAttributes, BeatmapPack, Beatmapset, BeatmapsetExtended, BeatmapExtendedWithFailtimesBeatmapsetextended, BeatmapsetExtendedPlus, BeatmapDifficultyAttributesOsu, BeatmapDifficultyAttributesFruits, BeatmapDifficultyAttributesMania, BeatmapDifficultyAttributesTaiko } from "./beatmap.js"
import { Leader, Match, MatchInfo, MultiplayerScore, MultiplayerScores, PlaylistItem, Room } from "./multiplayer.js"
import { Rulesets, Mod } from "./misc.js"
import { BeatmapUserScore, Score, ScoreWithUser, ScoreWithUserBeatmapBeatmapset } from "./score.js"
Expand Down Expand Up @@ -252,6 +252,8 @@ export class API {
this.log(true, "Server responded with status code 401, maybe you need to do this action as an user?")
} else if (response.status === 403) {
this.log(true, "Server responded with status code 403, you may lack the necessary scope for this action!")
} else if (response.status === 422) {
this.log(true, "Server responded with status code 422, you may be unable to use those parameters together!")
} else if (response.status === 429) {
this.log(true, "Server responded with status code 429, you're sending too many requests at once and are getting rate-limited!")
to_retry = true
Expand Down Expand Up @@ -372,21 +374,45 @@ export class API {
return response.beatmaps.map((b: BeatmapExtended) => correctType(b)) as BeatmapExtended[]
}

private async getBeatmapDifficultyAttributes(id: number, mods?: Mod[] | string[] | number, ruleset?: Rulesets): Promise<BeatmapDifficultyAttributes> {
const response = await this.request("post", `beatmaps/${id}/attributes`, {ruleset_id: ruleset, mods})
return correctType(response.attributes) as BeatmapDifficultyAttributes
}
/**
* Get various data about the difficulty of a beatmap!
* @remarks Will ignore the customization of your mods
* @param beatmap The Beatmap in question
* @param mods Defaults to No Mod, can be a bitset of mods, an array of mod acronyms ("DT" for DoubleTime), or an array of Mods
* @param ruleset Defaults to the Ruleset the Beatmap was made for, useful to specify if you want a convert
* @returns The (beatmap) difficulty attributes of the Beatmap
* @example ```ts
* await api.getBeatmapDifficultyAttributes({id: 811925}, ["HR", "HD"])
* ```
*/
async getBeatmapDifficultyAttributes(beatmap: {id: number} | Beatmap, mods?: Mod[] | string[] | number,
ruleset?: Rulesets): Promise<BeatmapDifficultyAttributes> {
const response = await this.request("post", `beatmaps/${beatmap.id}/attributes`, {ruleset_id: ruleset, mods})
return correctType(response.attributes) as BeatmapDifficultyAttributes
async getBeatmapDifficultyAttributesOsu(beatmap: {id: number} | Beatmap, mods?: Mod[] | string[] | number): Promise<BeatmapDifficultyAttributesOsu> {
return await this.getBeatmapDifficultyAttributes(beatmap.id, mods, Rulesets.osu) as BeatmapDifficultyAttributesOsu
}
/**
* Get various data about the difficulty of a beatmap!
* @remarks Will ignore the customization of your mods
* @param beatmap The Beatmap in question
* @param mods Defaults to No Mod, can be a bitset of mods, an array of mod acronyms ("DT" for DoubleTime), or an array of Mods
*/
async getBeatmapDifficultyAttributesTaiko(beatmap: {id: number} | Beatmap, mods?: Mod[] | string[] | number): Promise<BeatmapDifficultyAttributesTaiko> {
return await this.getBeatmapDifficultyAttributes(beatmap.id, mods, Rulesets.taiko) as BeatmapDifficultyAttributesTaiko
}
/**
* Get various data about the difficulty of a beatmap!
* @remarks Will ignore the customization of your mods
* @param beatmap The Beatmap in question
* @param mods Defaults to No Mod, can be a bitset of mods, an array of mod acronyms ("DT" for DoubleTime), or an array of Mods
*/
async getBeatmapDifficultyAttributesFruits(beatmap: {id: number} | Beatmap, mods?: Mod[] | string[] | number): Promise<BeatmapDifficultyAttributesFruits> {
return await this.getBeatmapDifficultyAttributes(beatmap.id, mods, Rulesets.fruits) as BeatmapDifficultyAttributesFruits
}
/**
* Get various data about the difficulty of a beatmap!
* @remarks Will ignore the customization of your mods
* @param beatmap The Beatmap in question
* @param mods Defaults to No Mod, can be a bitset of mods, an array of mod acronyms ("DT" for DoubleTime), or an array of Mods
*/
async getBeatmapDifficultyAttributesMania(beatmap: {id: number} | Beatmap, mods?: Mod[] | string[] | number): Promise<BeatmapDifficultyAttributesMania> {
return await this.getBeatmapDifficultyAttributes(beatmap.id, mods, Rulesets.mania) as BeatmapDifficultyAttributesMania
}

/**
Expand Down
43 changes: 27 additions & 16 deletions lib/tests/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function isOk(response: any, condition?: boolean, depth: number = Infinity) {
if (condition === undefined) condition = true
if (!response || !condition) {
if (Array.isArray(response) && response[0]) {
console.log("(only printing the first element of the response array for the following error:")
console.log("(only printing the first element of the response array for the error below)")
response = response[0]
}
console.error("❌ Bad response:", util.inspect(response, {colors: true, compact: true, breakLength: 400, depth: depth}))
Expand Down Expand Up @@ -113,23 +113,34 @@ const testBeatmapStuff = async (): Promise<boolean> => {
if (!isOk(b1, !b1 || (b1.id === beatmap_id && validate(b1, "BeatmapExtendedWithFailtimesBeatmapsetextended", generator)))) okay = false
let b2 = await <Promise<ReturnType<typeof api.getBeatmaps> | false>>attempt("getBeatmaps: ", api.getBeatmaps([beatmap_id, 4089655]))
if (!isOk(b2, !b2 || (b2.length === 2 && validate(b2[0], "BeatmapExtended", generator)))) okay = false
let b3 = await <Promise<ReturnType<typeof api.getBeatmapDifficultyAttributes> | false>>attempt(
"getBeatmapAttributes: ", api.getBeatmapDifficultyAttributes({id: beatmap_id}, ["DT"]))
if (!isOk(b3, !b3 || (b3.great_hit_window < 35 && validate(b3, "BeatmapDifficultyAttributes", generator)))) okay = false
let b4 = await <Promise<ReturnType<typeof api.getBeatmapUserScore> | false>>attempt(

let b3 = await <Promise<ReturnType<typeof api.getBeatmapDifficultyAttributesOsu> | false>>attempt(
"getBeatmapAttributesOsu: ", api.getBeatmapDifficultyAttributesOsu({id: 125660}, ["DT"]))
if (!isOk(b3, !b3 || (b3.approach_rate.toFixed(2) === "9.67" && validate(b3, "BeatmapDifficultyAttributesOsu", generator)))) okay = false
let b4 = await <Promise<ReturnType<typeof api.getBeatmapDifficultyAttributesTaiko> | false>>attempt(
"getBeatmapAttributesTaiko: ", api.getBeatmapDifficultyAttributesTaiko({id: beatmap_id}, ["DT"]))
if (!isOk(b4, !b4 || (b4.great_hit_window < 35 && validate(b4, "BeatmapDifficultyAttributesTaiko", generator)))) okay = false
let b5 = await <Promise<ReturnType<typeof api.getBeatmapDifficultyAttributesFruits> | false>>attempt(
"getBeatmapAttributesFruits: ", api.getBeatmapDifficultyAttributesFruits({id: 705339}, ["DT"]))
if (!isOk(b5, !b5 || (b5.approach_rate.toFixed(2) === "10.33" && validate(b5, "BeatmapDifficultyAttributesFruits", generator)))) okay = false
let b6 = await <Promise<ReturnType<typeof api.getBeatmapDifficultyAttributesMania> | false>>attempt(
"getBeatmapAttributesMania: ", api.getBeatmapDifficultyAttributesMania({id: 3980252}, ["DT"]))
if (!isOk(b6, !b6 || (b6.great_hit_window === 40 && validate(b6, "BeatmapDifficultyAttributesMania", generator)))) okay = false

let b7 = await <Promise<ReturnType<typeof api.getBeatmapUserScore> | false>>attempt(
"getBeatmapUserScore: ", api.getBeatmapUserScore({id: 176960}, {id: 7276846}, ["NM"]))
if (!isOk(b4, !b4 || (b4.score.accuracy < 0.99 && validate(b4, "BeatmapUserScore", score_gen)))) okay = false
let b5 = await <Promise<ReturnType<typeof api.getBeatmapUserScores> | false>>attempt(
if (!isOk(b7, !b7 || (b7.score.accuracy < 0.99 && validate(b7, "BeatmapUserScore", score_gen)))) okay = false
let b8 = await <Promise<ReturnType<typeof api.getBeatmapUserScores> | false>>attempt(
"getBeatmapUserScores: ", api.getBeatmapUserScores({id: 203993}, {id: 7276846}, osu.Rulesets.fruits))
if (!isOk(b5, !b5 || (b5.length === 1 && validate(b5[0], "Score", score_gen)))) okay = false
let b6 = await <Promise<ReturnType<typeof api.getBeatmapset> | false>>attempt("getBeatmapset: ", api.getBeatmapset({id: 1971037}))
if (!isOk(b6, !b6 || (b6.submitted_date?.toISOString().substring(0, 10) === "2023-04-07", validate(b6, "BeatmapsetExtendedPlus", generator)))) okay = false
let b7 = await <Promise<ReturnType<typeof api.getBeatmapPack> | false>>attempt("getBeatmapPack: ", api.getBeatmapPack({tag: "P217"}))
if (!isOk(b7, !b7 || (b7.tag === "P217" && validate(b7, "BeatmapPack", generator)))) okay = false
let b8 = await <Promise<ReturnType<typeof api.getBeatmapPacks> | false>>attempt("getBeatmapPacks: ", api.getBeatmapPacks("tournament"))
if (!isOk(b8, !b8 || (b8.length >= 100 && validate(b8[0], "BeatmapPack", generator)))) okay = false
let b9 = await <Promise<ReturnType<typeof api.getBeatmapScores> | false>>attempt("getBeatmapScores: ", api.getBeatmapScores({id: 129891}))
if (!isOk(b9, !b9 || (b9[0].score >= 132408001 && validate(b9[0], "ScoreWithUser", score_gen)))) okay = false
if (!isOk(b8, !b8 || (b8.length === 1 && validate(b8[0], "Score", score_gen)))) okay = false
let b9 = await <Promise<ReturnType<typeof api.getBeatmapset> | false>>attempt("getBeatmapset: ", api.getBeatmapset({id: 1971037}))
if (!isOk(b9, !b9 || (b9.submitted_date?.toISOString().substring(0, 10) === "2023-04-07", validate(b9, "BeatmapsetExtendedPlus", generator)))) okay = false
let b10 = await <Promise<ReturnType<typeof api.getBeatmapPack> | false>>attempt("getBeatmapPack: ", api.getBeatmapPack({tag: "P217"}))
if (!isOk(b10, !b10 || (b10.tag === "P217" && validate(b10, "BeatmapPack", generator)))) okay = false
let b11 = await <Promise<ReturnType<typeof api.getBeatmapPacks> | false>>attempt("getBeatmapPacks: ", api.getBeatmapPacks("tournament"))
if (!isOk(b11, !b11 || (b11.length >= 100 && validate(b11[0], "BeatmapPack", generator)))) okay = false
let b12 = await <Promise<ReturnType<typeof api.getBeatmapScores> | false>>attempt("getBeatmapScores: ", api.getBeatmapScores({id: 129891}))
if (!isOk(b12, !b12 || (b12[0].score >= 132408001 && validate(b12[0], "ScoreWithUser", score_gen)))) okay = false

return okay
}
Expand Down

0 comments on commit 8df8605

Please sign in to comment.