mirror of
https://github.com/natsukagami/youmubot.git
synced 2025-04-19 16:58:55 +00:00
Remove old reqwest osu api v1
This commit is contained in:
parent
07928671e2
commit
fef6516ae1
7 changed files with 3 additions and 475 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3087,7 +3087,6 @@ dependencies = [
|
||||||
"osuparse",
|
"osuparse",
|
||||||
"rand",
|
"rand",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
|
||||||
"rosu-pp",
|
"rosu-pp",
|
||||||
"rosu-v2",
|
"rosu-v2",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
@ -74,8 +74,6 @@ pub async fn setup(
|
||||||
let mk_osu_client = || async {
|
let mk_osu_client = || async {
|
||||||
Arc::new(
|
Arc::new(
|
||||||
OsuHttpClient::new(
|
OsuHttpClient::new(
|
||||||
std::env::var("OSU_API_KEY").expect("Please set OSU_API_KEY as osu! api key."),
|
|
||||||
http_client.clone(),
|
|
||||||
std::env::var("OSU_API_CLIENT_ID")
|
std::env::var("OSU_API_CLIENT_ID")
|
||||||
.expect("Please set OSU_API_CLIENT_ID as osu! api v2 client ID.")
|
.expect("Please set OSU_API_CLIENT_ID as osu! api v2 client ID.")
|
||||||
.parse()
|
.parse()
|
||||||
|
|
|
@ -8,18 +8,11 @@ mod test;
|
||||||
use models::*;
|
use models::*;
|
||||||
use request::builders::*;
|
use request::builders::*;
|
||||||
use request::*;
|
use request::*;
|
||||||
use reqwest::Client as HTTPClient;
|
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use youmubot_prelude::{ratelimit::Ratelimit, *};
|
use youmubot_prelude::*;
|
||||||
|
|
||||||
/// The number of requests per minute to the osu! server.
|
|
||||||
const REQUESTS_PER_MINUTE: usize = 100;
|
|
||||||
|
|
||||||
/// Client is the client that will perform calls to the osu! api server.
|
/// Client is the client that will perform calls to the osu! api server.
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
client: Ratelimit<HTTPClient>,
|
|
||||||
key: String,
|
|
||||||
|
|
||||||
rosu: rosu_v2::Osu,
|
rosu: rosu_v2::Osu,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,32 +28,13 @@ pub fn vec_try_into<U, T: std::convert::TryFrom<U>>(v: Vec<U>) -> Result<Vec<T>,
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Create a new client from the given API key.
|
/// Create a new client from the given API key.
|
||||||
pub async fn new(
|
pub async fn new(client_id: u64, client_secret: impl Into<String>) -> Result<Client> {
|
||||||
key: String,
|
|
||||||
client: HTTPClient,
|
|
||||||
client_id: u64,
|
|
||||||
client_secret: impl Into<String>,
|
|
||||||
) -> Result<Client> {
|
|
||||||
let client = Ratelimit::new(
|
|
||||||
client,
|
|
||||||
REQUESTS_PER_MINUTE,
|
|
||||||
std::time::Duration::from_secs(60),
|
|
||||||
);
|
|
||||||
let rosu = rosu_v2::OsuBuilder::new()
|
let rosu = rosu_v2::OsuBuilder::new()
|
||||||
.client_id(client_id)
|
.client_id(client_id)
|
||||||
.client_secret(client_secret)
|
.client_secret(client_secret)
|
||||||
.build()
|
.build()
|
||||||
.await?;
|
.await?;
|
||||||
Ok(Client { client, key, rosu })
|
Ok(Client { rosu })
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn build_request(&self, url: &str) -> Result<reqwest::RequestBuilder> {
|
|
||||||
Ok(self
|
|
||||||
.client
|
|
||||||
.borrow()
|
|
||||||
.await?
|
|
||||||
.get(url)
|
|
||||||
.query(&[("k", &*self.key)]))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn beatmaps(
|
pub async fn beatmaps(
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use regex::Regex;
|
|
||||||
use rosu_pp::GameMode;
|
use rosu_pp::GameMode;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
pub mod mods;
|
pub mod mods;
|
||||||
pub mod parse;
|
|
||||||
pub(crate) mod raw;
|
|
||||||
pub(crate) mod rosu;
|
pub(crate) mod rosu;
|
||||||
|
|
||||||
pub use mods::Mods;
|
pub use mods::Mods;
|
||||||
|
|
|
@ -1,342 +0,0 @@
|
||||||
use super::*;
|
|
||||||
use chrono::{
|
|
||||||
format::{parse, Item, Numeric, Pad, Parsed},
|
|
||||||
DateTime, ParseError as ChronoParseError, Utc,
|
|
||||||
};
|
|
||||||
use std::convert::TryFrom;
|
|
||||||
use std::time::Duration;
|
|
||||||
use std::{error::Error, fmt, str::FromStr};
|
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
|
||||||
static ref EVENT_RANK_REGEX: Regex = Regex::new(r#"^.+achieved .*rank #(\d+).* on .+\((.+)\)$"#).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Errors that can be identified from parsing.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ParseError {
|
|
||||||
InvalidValue { field: &'static str, value: String },
|
|
||||||
FromStr(String),
|
|
||||||
NoApprovalDate,
|
|
||||||
NotUserEventRank,
|
|
||||||
DateParseError(ChronoParseError),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for ParseError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
use ParseError::*;
|
|
||||||
match self {
|
|
||||||
InvalidValue {
|
|
||||||
ref field,
|
|
||||||
ref value,
|
|
||||||
} => write!(f, "Invalid value `{}` for {}", value, field),
|
|
||||||
FromStr(ref s) => write!(f, "Invalid value `{}` parsing from string", s),
|
|
||||||
NoApprovalDate => write!(f, "Approval date expected but not found"),
|
|
||||||
NotUserEventRank => write!(f, "Trying to parse user event as UserEventRank"),
|
|
||||||
DateParseError(ref r) => write!(f, "Error parsing date: {}", r),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for ParseError {}
|
|
||||||
|
|
||||||
type ParseResult<T> = Result<T, ParseError>;
|
|
||||||
|
|
||||||
impl TryFrom<raw::Score> for Score {
|
|
||||||
type Error = ParseError;
|
|
||||||
fn try_from(raw: raw::Score) -> Result<Self, Self::Error> {
|
|
||||||
Ok(Score {
|
|
||||||
id: raw.score_id.map(parse_from_str).transpose()?,
|
|
||||||
user_id: parse_from_str(&raw.user_id)?,
|
|
||||||
date: parse_date(&raw.date)?,
|
|
||||||
beatmap_id: raw.beatmap_id.map(parse_from_str).transpose()?.unwrap_or(0),
|
|
||||||
replay_available: raw
|
|
||||||
.replay_available
|
|
||||||
.map(parse_bool)
|
|
||||||
.transpose()?
|
|
||||||
.unwrap_or(false),
|
|
||||||
score: parse_from_str(&raw.score)?,
|
|
||||||
pp: raw.pp.map(parse_from_str).transpose()?,
|
|
||||||
rank: parse_from_str(&raw.rank)?,
|
|
||||||
mods: {
|
|
||||||
let v: u64 = parse_from_str(&raw.enabled_mods)?;
|
|
||||||
Mods::from_bits(v).unwrap_or(Mods::NOMOD)
|
|
||||||
},
|
|
||||||
count_300: parse_from_str(&raw.count300)?,
|
|
||||||
count_100: parse_from_str(&raw.count100)?,
|
|
||||||
count_50: parse_from_str(&raw.count50)?,
|
|
||||||
count_miss: parse_from_str(&raw.countmiss)?,
|
|
||||||
count_katu: parse_from_str(&raw.countkatu)?,
|
|
||||||
count_geki: parse_from_str(&raw.countgeki)?,
|
|
||||||
max_combo: parse_from_str(&raw.maxcombo)?,
|
|
||||||
perfect: parse_bool(&raw.perfect)?,
|
|
||||||
|
|
||||||
lazer_build_id: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<raw::User> for User {
|
|
||||||
type Error = ParseError;
|
|
||||||
fn try_from(raw: raw::User) -> Result<Self, Self::Error> {
|
|
||||||
Ok(User {
|
|
||||||
id: parse_from_str(&raw.user_id)?,
|
|
||||||
username: raw.username,
|
|
||||||
joined: parse_date(&raw.join_date)?,
|
|
||||||
country: raw.country,
|
|
||||||
count_300: raw.count300.map(parse_from_str).unwrap_or(Ok(0))?,
|
|
||||||
count_100: raw.count100.map(parse_from_str).unwrap_or(Ok(0))?,
|
|
||||||
count_50: raw.count50.map(parse_from_str).unwrap_or(Ok(0))?,
|
|
||||||
play_count: raw.playcount.map(parse_from_str).unwrap_or(Ok(0))?,
|
|
||||||
played_time: raw
|
|
||||||
.total_seconds_played
|
|
||||||
.map(parse_duration)
|
|
||||||
.unwrap_or_else(|| Ok(Duration::from_secs(0)))?,
|
|
||||||
ranked_score: raw.ranked_score.map(parse_from_str).unwrap_or(Ok(0))?,
|
|
||||||
total_score: raw.total_score.map(parse_from_str).unwrap_or(Ok(0))?,
|
|
||||||
count_ss: raw.count_rank_ss.map(parse_from_str).unwrap_or(Ok(0))?,
|
|
||||||
count_ssh: raw.count_rank_ssh.map(parse_from_str).unwrap_or(Ok(0))?,
|
|
||||||
count_s: raw.count_rank_s.map(parse_from_str).unwrap_or(Ok(0))?,
|
|
||||||
count_sh: raw.count_rank_sh.map(parse_from_str).unwrap_or(Ok(0))?,
|
|
||||||
count_a: raw.count_rank_a.map(parse_from_str).unwrap_or(Ok(0))?,
|
|
||||||
rank: raw.pp_rank.map(parse_from_str).unwrap_or(Ok(0))?,
|
|
||||||
country_rank: raw.pp_country_rank.map(parse_from_str).unwrap_or(Ok(0))?,
|
|
||||||
level: raw.level.map(parse_from_str).unwrap_or(Ok(0.0))?,
|
|
||||||
pp: Some(raw.pp_raw.map(parse_from_str).unwrap_or(Ok(0.0))?).filter(|v| *v != 0.0),
|
|
||||||
accuracy: raw.accuracy.map(parse_from_str).unwrap_or(Ok(0.0))?,
|
|
||||||
events: {
|
|
||||||
let mut v = Vec::new();
|
|
||||||
for e in raw.events.into_iter() {
|
|
||||||
v.push(parse_user_event(e)?);
|
|
||||||
}
|
|
||||||
v
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<raw::Beatmap> for Beatmap {
|
|
||||||
type Error = ParseError;
|
|
||||||
fn try_from(raw: raw::Beatmap) -> Result<Self, Self::Error> {
|
|
||||||
Ok(Beatmap {
|
|
||||||
approval: parse_approval_status(&raw)?,
|
|
||||||
submit_date: parse_date(&raw.submit_date)?,
|
|
||||||
last_update: parse_date(&raw.last_update)?,
|
|
||||||
download_available: !(parse_bool(&raw.download_unavailable)?),
|
|
||||||
audio_available: !(parse_bool(&raw.audio_unavailable)?),
|
|
||||||
artist: raw.artist,
|
|
||||||
beatmap_id: parse_from_str(&raw.beatmap_id)?,
|
|
||||||
beatmapset_id: parse_from_str(&raw.beatmapset_id)?,
|
|
||||||
title: raw.title,
|
|
||||||
creator: raw.creator,
|
|
||||||
creator_id: parse_from_str(&raw.creator_id)?,
|
|
||||||
source: raw.source.filter(|v| !v.is_empty()),
|
|
||||||
genre: parse_genre(&raw.genre_id)?,
|
|
||||||
language: parse_language(&raw.language_id)?,
|
|
||||||
tags: raw.tags.split_whitespace().map(|v| v.to_owned()).collect(),
|
|
||||||
difficulty_name: raw.version,
|
|
||||||
difficulty: Difficulty {
|
|
||||||
stars: parse_from_str(&raw.difficultyrating)?,
|
|
||||||
aim: raw.diff_aim.map(parse_from_str).transpose()?,
|
|
||||||
speed: raw.diff_speed.map(parse_from_str).transpose()?,
|
|
||||||
cs: parse_from_str(&raw.diff_size)?,
|
|
||||||
od: parse_from_str(&raw.diff_overall)?,
|
|
||||||
ar: parse_from_str(&raw.diff_approach)?,
|
|
||||||
hp: parse_from_str(&raw.diff_drain)?,
|
|
||||||
count_normal: parse_from_str(&raw.count_normal)?,
|
|
||||||
count_slider: parse_from_str(&raw.count_slider)?,
|
|
||||||
count_spinner: parse_from_str(&raw.count_spinner)?,
|
|
||||||
max_combo: raw.max_combo.map(parse_from_str).transpose()?,
|
|
||||||
bpm: parse_from_str(&raw.bpm)?,
|
|
||||||
drain_length: parse_duration(&raw.hit_length)?,
|
|
||||||
total_length: parse_duration(&raw.total_length)?,
|
|
||||||
},
|
|
||||||
file_hash: raw.file_md5,
|
|
||||||
mode: parse_mode(&raw.mode)?,
|
|
||||||
favourite_count: parse_from_str(&raw.favourite_count)?,
|
|
||||||
rating: parse_from_str(&raw.rating)?,
|
|
||||||
play_count: parse_from_str(&raw.playcount)?,
|
|
||||||
pass_count: parse_from_str(&raw.passcount)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_user_event(s: raw::UserEvent) -> ParseResult<UserEvent> {
|
|
||||||
match parse_user_event_rank(&s) {
|
|
||||||
Ok(r) => return Ok(UserEvent::Rank(r)),
|
|
||||||
Err(_) => (),
|
|
||||||
};
|
|
||||||
Ok(UserEvent::OtherV1 {
|
|
||||||
display_html: s.display_html,
|
|
||||||
date: parse_date(&s.date)?,
|
|
||||||
epic_factor: parse_from_str(&s.epicfactor)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_user_event_rank(s: &raw::UserEvent) -> ParseResult<UserEventRank> {
|
|
||||||
let captures = EVENT_RANK_REGEX
|
|
||||||
.captures(s.display_html.as_str())
|
|
||||||
.ok_or(ParseError::NotUserEventRank)?;
|
|
||||||
let rank: u16 = captures
|
|
||||||
.get(1)
|
|
||||||
.ok_or(ParseError::NotUserEventRank)?
|
|
||||||
.as_str()
|
|
||||||
.parse()
|
|
||||||
.map_err(|_| ParseError::NotUserEventRank)?;
|
|
||||||
let mode = super::Mode::parse_from_display(
|
|
||||||
captures
|
|
||||||
.get(2)
|
|
||||||
.ok_or(ParseError::NotUserEventRank)?
|
|
||||||
.as_str(),
|
|
||||||
)
|
|
||||||
.ok_or(ParseError::NotUserEventRank)?;
|
|
||||||
let beatmap_id = s
|
|
||||||
.beatmap_id
|
|
||||||
.as_ref()
|
|
||||||
.ok_or(ParseError::NotUserEventRank)?
|
|
||||||
.parse::<u64>()
|
|
||||||
.map_err(|_| ParseError::NotUserEventRank)?;
|
|
||||||
Ok(UserEventRank {
|
|
||||||
beatmap_id,
|
|
||||||
date: parse_date(&s.date)?,
|
|
||||||
mode,
|
|
||||||
rank,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_mode(s: impl AsRef<str>) -> ParseResult<Mode> {
|
|
||||||
let t: u8 = parse_from_str(s)?;
|
|
||||||
use Mode::*;
|
|
||||||
Ok(match t {
|
|
||||||
0 => Std,
|
|
||||||
1 => Taiko,
|
|
||||||
2 => Catch,
|
|
||||||
3 => Mania,
|
|
||||||
_ => {
|
|
||||||
return Err(ParseError::InvalidValue {
|
|
||||||
field: "mode",
|
|
||||||
value: t.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_language(s: impl AsRef<str>) -> ParseResult<Language> {
|
|
||||||
let t: u8 = parse_from_str(s)?;
|
|
||||||
use Language::*;
|
|
||||||
Ok(match t {
|
|
||||||
0 => Any,
|
|
||||||
1 | 14 => Other,
|
|
||||||
2 => English,
|
|
||||||
3 => Japanese,
|
|
||||||
4 => Chinese,
|
|
||||||
5 => Instrumental,
|
|
||||||
6 => Korean,
|
|
||||||
7 => French,
|
|
||||||
8 => German,
|
|
||||||
9 => Swedish,
|
|
||||||
10 => Spanish,
|
|
||||||
11 => Italian,
|
|
||||||
12 => Russian,
|
|
||||||
13 => Polish,
|
|
||||||
_ => {
|
|
||||||
return Err(ParseError::InvalidValue {
|
|
||||||
field: "language",
|
|
||||||
value: t.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_genre(s: impl AsRef<str>) -> ParseResult<Genre> {
|
|
||||||
let t: u8 = parse_from_str(s)?;
|
|
||||||
use Genre::*;
|
|
||||||
Ok(match t {
|
|
||||||
0 => Any,
|
|
||||||
1 => Unspecified,
|
|
||||||
2 => VideoGame,
|
|
||||||
3 => Anime,
|
|
||||||
4 => Rock,
|
|
||||||
5 => Pop,
|
|
||||||
6 => Other,
|
|
||||||
7 => Novelty,
|
|
||||||
9 => HipHop,
|
|
||||||
10 => Electronic,
|
|
||||||
11 => Metal,
|
|
||||||
12 => Classical,
|
|
||||||
13 => Folk,
|
|
||||||
14 => Jazz,
|
|
||||||
_ => {
|
|
||||||
return Err(ParseError::InvalidValue {
|
|
||||||
field: "genre",
|
|
||||||
value: t.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_duration(s: impl AsRef<str>) -> ParseResult<Duration> {
|
|
||||||
Ok(Duration::from_secs(parse_from_str(s)?))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_from_str<T: FromStr>(s: impl AsRef<str>) -> ParseResult<T> {
|
|
||||||
let v = s.as_ref();
|
|
||||||
T::from_str(v).map_err(|_| ParseError::FromStr(v.to_owned()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_bool(b: impl AsRef<str>) -> ParseResult<bool> {
|
|
||||||
match b.as_ref() {
|
|
||||||
"1" => Ok(true),
|
|
||||||
"0" => Ok(false),
|
|
||||||
t => Err(ParseError::InvalidValue {
|
|
||||||
field: "bool",
|
|
||||||
value: t.to_owned(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_approval_status(b: &raw::Beatmap) -> ParseResult<ApprovalStatus> {
|
|
||||||
use ApprovalStatus::*;
|
|
||||||
Ok(match &b.approved[..] {
|
|
||||||
"4" => Loved,
|
|
||||||
"3" => Qualified,
|
|
||||||
"2" => Approved,
|
|
||||||
"1" => Ranked(parse_date(
|
|
||||||
b.approved_date.as_ref().ok_or(ParseError::NoApprovalDate)?,
|
|
||||||
)?),
|
|
||||||
"0" => Pending,
|
|
||||||
"-1" => WIP,
|
|
||||||
"-2" => Graveyarded,
|
|
||||||
t => {
|
|
||||||
return Err(ParseError::InvalidValue {
|
|
||||||
field: "approval status",
|
|
||||||
value: t.to_owned(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_date(date: impl AsRef<str>) -> ParseResult<DateTime<Utc>> {
|
|
||||||
let mut parsed = Parsed::new();
|
|
||||||
parse(
|
|
||||||
&mut parsed,
|
|
||||||
date.as_ref(),
|
|
||||||
[
|
|
||||||
Item::Numeric(Numeric::Year, Pad::Zero),
|
|
||||||
Item::Literal("-"),
|
|
||||||
Item::Numeric(Numeric::Month, Pad::Zero),
|
|
||||||
Item::Literal("-"),
|
|
||||||
Item::Numeric(Numeric::Day, Pad::Zero),
|
|
||||||
Item::Space(""),
|
|
||||||
Item::Numeric(Numeric::Hour, Pad::Zero),
|
|
||||||
Item::Literal(":"),
|
|
||||||
Item::Numeric(Numeric::Minute, Pad::Zero),
|
|
||||||
Item::Literal(":"),
|
|
||||||
Item::Numeric(Numeric::Second, Pad::Zero),
|
|
||||||
]
|
|
||||||
.iter(),
|
|
||||||
)
|
|
||||||
.map_err(ParseError::DateParseError)?;
|
|
||||||
parsed
|
|
||||||
.to_datetime_with_timezone(&Utc {})
|
|
||||||
.map_err(ParseError::DateParseError)
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Debug)]
|
|
||||||
pub(crate) struct Beatmap {
|
|
||||||
pub approved: String,
|
|
||||||
pub submit_date: String,
|
|
||||||
pub approved_date: Option<String>,
|
|
||||||
pub last_update: String,
|
|
||||||
pub artist: String,
|
|
||||||
pub beatmap_id: String,
|
|
||||||
pub beatmapset_id: String,
|
|
||||||
pub bpm: String,
|
|
||||||
pub creator: String,
|
|
||||||
pub creator_id: String,
|
|
||||||
pub difficultyrating: String,
|
|
||||||
pub diff_aim: Option<String>,
|
|
||||||
pub diff_speed: Option<String>,
|
|
||||||
pub diff_size: String,
|
|
||||||
pub diff_overall: String,
|
|
||||||
pub diff_approach: String,
|
|
||||||
pub diff_drain: String,
|
|
||||||
pub hit_length: String,
|
|
||||||
pub source: Option<String>,
|
|
||||||
pub genre_id: String,
|
|
||||||
pub language_id: String,
|
|
||||||
pub title: String,
|
|
||||||
pub total_length: String,
|
|
||||||
pub version: String,
|
|
||||||
pub file_md5: String,
|
|
||||||
pub mode: String,
|
|
||||||
pub tags: String,
|
|
||||||
pub favourite_count: String,
|
|
||||||
pub rating: String,
|
|
||||||
pub playcount: String,
|
|
||||||
pub passcount: String,
|
|
||||||
pub count_normal: String,
|
|
||||||
pub count_slider: String,
|
|
||||||
pub count_spinner: String,
|
|
||||||
pub max_combo: Option<String>,
|
|
||||||
pub download_unavailable: String,
|
|
||||||
pub audio_unavailable: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub(crate) struct User {
|
|
||||||
pub user_id: String,
|
|
||||||
pub username: String,
|
|
||||||
pub join_date: String,
|
|
||||||
pub country: String,
|
|
||||||
pub count300: Option<String>,
|
|
||||||
pub count100: Option<String>,
|
|
||||||
pub count50: Option<String>,
|
|
||||||
pub playcount: Option<String>,
|
|
||||||
pub ranked_score: Option<String>,
|
|
||||||
pub total_score: Option<String>,
|
|
||||||
pub pp_rank: Option<String>,
|
|
||||||
pub level: Option<String>,
|
|
||||||
pub pp_raw: Option<String>,
|
|
||||||
pub accuracy: Option<String>,
|
|
||||||
pub count_rank_ss: Option<String>,
|
|
||||||
pub count_rank_ssh: Option<String>,
|
|
||||||
pub count_rank_s: Option<String>,
|
|
||||||
pub count_rank_sh: Option<String>,
|
|
||||||
pub count_rank_a: Option<String>,
|
|
||||||
pub total_seconds_played: Option<String>,
|
|
||||||
pub pp_country_rank: Option<String>,
|
|
||||||
pub events: Vec<UserEvent>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub(crate) struct UserEvent {
|
|
||||||
pub display_html: String,
|
|
||||||
pub beatmap_id: Option<String>,
|
|
||||||
pub date: String,
|
|
||||||
pub epicfactor: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
pub(crate) struct Score {
|
|
||||||
pub score_id: Option<String>,
|
|
||||||
pub beatmap_id: Option<String>,
|
|
||||||
pub score: String,
|
|
||||||
pub count300: String,
|
|
||||||
pub count100: String,
|
|
||||||
pub count50: String,
|
|
||||||
pub countmiss: String,
|
|
||||||
pub maxcombo: String,
|
|
||||||
pub countkatu: String,
|
|
||||||
pub countgeki: String,
|
|
||||||
pub perfect: String,
|
|
||||||
pub enabled_mods: String,
|
|
||||||
pub user_id: String,
|
|
||||||
pub date: String,
|
|
||||||
pub rank: String,
|
|
||||||
pub pp: Option<String>,
|
|
||||||
pub replay_available: Option<String>,
|
|
||||||
}
|
|
|
@ -109,7 +109,6 @@ fn handle_not_found<T>(v: Result<T, OsuError>) -> Result<Option<T>, OsuError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod builders {
|
pub mod builders {
|
||||||
use reqwest::Response;
|
|
||||||
use rosu_v2::model::mods::GameModsIntermode;
|
use rosu_v2::model::mods::GameModsIntermode;
|
||||||
|
|
||||||
use crate::models;
|
use crate::models;
|
||||||
|
|
Loading…
Add table
Reference in a new issue