mirror of
https://github.com/natsukagami/youmubot.git
synced 2025-04-18 16:28:55 +00:00
Implement score request
This commit is contained in:
parent
3e951554d7
commit
f1742664c4
5 changed files with 117 additions and 12 deletions
|
@ -24,6 +24,16 @@ impl Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_request(
|
||||||
|
&self,
|
||||||
|
c: &HTTPClient,
|
||||||
|
r: reqwest::RequestBuilder,
|
||||||
|
) -> Result<reqwest::Response, Error> {
|
||||||
|
let v = r.query(&[("k", &self.key)]).build()?;
|
||||||
|
// println!("{}", v.url());
|
||||||
|
Ok(c.execute(v)?)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn beatmaps(
|
pub fn beatmaps(
|
||||||
&self,
|
&self,
|
||||||
client: &HTTPClient,
|
client: &HTTPClient,
|
||||||
|
@ -32,7 +42,7 @@ impl Client {
|
||||||
) -> Result<Vec<Beatmap>, Error> {
|
) -> Result<Vec<Beatmap>, Error> {
|
||||||
let mut r = BeatmapRequestBuilder::new(kind);
|
let mut r = BeatmapRequestBuilder::new(kind);
|
||||||
f(&mut r);
|
f(&mut r);
|
||||||
let res = r.build(client).query(&[("k", &self.key)]).send()?.json()?;
|
let res = self.build_request(client, r.build(client))?.json()?;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +54,7 @@ impl Client {
|
||||||
) -> Result<Option<User>, Error> {
|
) -> Result<Option<User>, Error> {
|
||||||
let mut r = UserRequestBuilder::new(user);
|
let mut r = UserRequestBuilder::new(user);
|
||||||
f(&mut r);
|
f(&mut r);
|
||||||
let res: Vec<_> = r.build(client).query(&[("k", &self.key)]).send()?.json()?;
|
let res: Vec<_> = self.build_request(client, r.build(client))?.json()?;
|
||||||
Ok(res.into_iter().next())
|
Ok(res.into_iter().next())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +66,43 @@ impl Client {
|
||||||
) -> Result<Vec<Score>, Error> {
|
) -> Result<Vec<Score>, Error> {
|
||||||
let mut r = ScoreRequestBuilder::new(beatmap_id);
|
let mut r = ScoreRequestBuilder::new(beatmap_id);
|
||||||
f(&mut r);
|
f(&mut r);
|
||||||
let res = r.build(client).query(&[("k", &self.key)]).send()?.json()?;
|
let mut res: Vec<Score> = self.build_request(client, r.build(client))?.json()?;
|
||||||
|
|
||||||
|
// with a scores request you need to fill the beatmap ids yourself
|
||||||
|
res.iter_mut().for_each(|v| {
|
||||||
|
v.beatmap_id = beatmap_id;
|
||||||
|
});
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn user_best(
|
||||||
|
&self,
|
||||||
|
client: &HTTPClient,
|
||||||
|
user: UserID,
|
||||||
|
f: impl FnOnce(&mut UserScoreRequestBuilder) -> &mut UserScoreRequestBuilder,
|
||||||
|
) -> Result<Vec<Score>, Error> {
|
||||||
|
self.user_scores(UserScoreType::Best, client, user, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn user_recent(
|
||||||
|
&self,
|
||||||
|
client: &HTTPClient,
|
||||||
|
user: UserID,
|
||||||
|
f: impl FnOnce(&mut UserScoreRequestBuilder) -> &mut UserScoreRequestBuilder,
|
||||||
|
) -> Result<Vec<Score>, Error> {
|
||||||
|
self.user_scores(UserScoreType::Recent, client, user, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn user_scores(
|
||||||
|
&self,
|
||||||
|
u: UserScoreType,
|
||||||
|
client: &HTTPClient,
|
||||||
|
user: UserID,
|
||||||
|
f: impl FnOnce(&mut UserScoreRequestBuilder) -> &mut UserScoreRequestBuilder,
|
||||||
|
) -> Result<Vec<Score>, Error> {
|
||||||
|
let mut r = UserScoreRequestBuilder::new(u, user);
|
||||||
|
f(&mut r);
|
||||||
|
let res = self.build_request(client, r.build(client))?.json()?;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,15 +13,18 @@ impl<'de> Deserialize<'de> for Score {
|
||||||
{
|
{
|
||||||
let raw: raw::Score = raw::Score::deserialize(deserializer)?;
|
let raw: raw::Score = raw::Score::deserialize(deserializer)?;
|
||||||
Ok(Score {
|
Ok(Score {
|
||||||
id: parse_from_str(&raw.score_id)?,
|
id: raw.score_id.map(parse_from_str).transpose()?,
|
||||||
username: raw.username,
|
|
||||||
user_id: parse_from_str(&raw.user_id)?,
|
user_id: parse_from_str(&raw.user_id)?,
|
||||||
date: parse_date(&raw.date)?,
|
date: parse_date(&raw.date)?,
|
||||||
|
beatmap_id: raw.beatmap_id.map(parse_from_str).transpose()?.unwrap_or(0),
|
||||||
replay_available: parse_bool(&raw.replay_available)?,
|
replay_available: parse_bool(&raw.replay_available)?,
|
||||||
score: parse_from_str(&raw.score)?,
|
score: parse_from_str(&raw.score)?,
|
||||||
pp: parse_from_str(&raw.pp)?,
|
pp: parse_from_str(&raw.pp)?,
|
||||||
rank: parse_from_str(&raw.rank)?,
|
rank: parse_from_str(&raw.rank)?,
|
||||||
mods: parse_from_str(&raw.enabled_mods)?,
|
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_300: parse_from_str(&raw.count300)?,
|
||||||
count_100: parse_from_str(&raw.count100)?,
|
count_100: parse_from_str(&raw.count100)?,
|
||||||
count_50: parse_from_str(&raw.count50)?,
|
count_50: parse_from_str(&raw.count50)?,
|
||||||
|
|
|
@ -149,6 +149,18 @@ pub struct Beatmap {
|
||||||
pub pass_count: u64,
|
pub pass_count: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const NEW_MODE_NAMES: [&'static str; 4] = ["osu", "taiko", "fruits", "mania"];
|
||||||
|
|
||||||
|
impl Beatmap {
|
||||||
|
/// Gets a link pointing to the beatmap, in the new format.
|
||||||
|
pub fn link(&self) -> String {
|
||||||
|
format!(
|
||||||
|
"https://osu.ppy.sh/beatmapsets/{}#{}/{}",
|
||||||
|
self.beatmapset_id, NEW_MODE_NAMES[self.mode as usize], self.beatmap_id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UserEvent {
|
pub struct UserEvent {
|
||||||
pub display_html: String,
|
pub display_html: String,
|
||||||
|
@ -203,8 +215,8 @@ impl std::str::FromStr for Rank {
|
||||||
type Err = String;
|
type Err = String;
|
||||||
fn from_str(a: &str) -> Result<Self, Self::Err> {
|
fn from_str(a: &str) -> Result<Self, Self::Err> {
|
||||||
Ok(match &a.to_uppercase()[..] {
|
Ok(match &a.to_uppercase()[..] {
|
||||||
"SS" => Rank::SS,
|
"SS" | "X" => Rank::SS,
|
||||||
"SSH" => Rank::SSH,
|
"SSH" | "XH" => Rank::SSH,
|
||||||
"S" => Rank::S,
|
"S" => Rank::S,
|
||||||
"SH" => Rank::SH,
|
"SH" => Rank::SH,
|
||||||
"A" => Rank::A,
|
"A" => Rank::A,
|
||||||
|
@ -225,11 +237,11 @@ impl fmt::Display for Rank {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Score {
|
pub struct Score {
|
||||||
pub id: u64,
|
pub id: Option<u64>, // No id if you fail
|
||||||
pub username: String,
|
|
||||||
pub user_id: u64,
|
pub user_id: u64,
|
||||||
pub date: DateTime<Utc>,
|
pub date: DateTime<Utc>,
|
||||||
pub replay_available: bool,
|
pub replay_available: bool,
|
||||||
|
pub beatmap_id: u64,
|
||||||
|
|
||||||
pub score: u64,
|
pub score: u64,
|
||||||
pub pp: f64,
|
pub pp: f64,
|
||||||
|
|
|
@ -78,9 +78,9 @@ pub(crate) struct UserEvent {
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub(crate) struct Score {
|
pub(crate) struct Score {
|
||||||
pub score_id: String,
|
pub score_id: Option<String>,
|
||||||
|
pub beatmap_id: Option<String>,
|
||||||
pub score: String,
|
pub score: String,
|
||||||
pub username: String,
|
|
||||||
pub count300: String,
|
pub count300: String,
|
||||||
pub count100: String,
|
pub count100: String,
|
||||||
pub count50: String,
|
pub count50: String,
|
||||||
|
|
|
@ -207,6 +207,50 @@ pub mod builders {
|
||||||
.query(&self.limit.map(|v| ("limit", v.to_string())).to_query())
|
.query(&self.limit.map(|v| ("limit", v.to_string())).to_query())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) enum UserScoreType {
|
||||||
|
Recent,
|
||||||
|
Best,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UserScoreRequestBuilder {
|
||||||
|
score_type: UserScoreType,
|
||||||
|
user: UserID,
|
||||||
|
mode: Option<Mode>,
|
||||||
|
limit: Option<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserScoreRequestBuilder {
|
||||||
|
pub(crate) fn new(score_type: UserScoreType, user: UserID) -> Self {
|
||||||
|
UserScoreRequestBuilder {
|
||||||
|
score_type,
|
||||||
|
user,
|
||||||
|
mode: None,
|
||||||
|
limit: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mode(&mut self, m: Mode) -> &mut Self {
|
||||||
|
self.mode = Some(m);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn limit(&mut self, limit: u8) -> &mut Self {
|
||||||
|
self.limit = Some(limit).filter(|&v| v <= 100).or(self.limit);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn build(&self, client: &Client) -> RequestBuilder {
|
||||||
|
client
|
||||||
|
.get(match self.score_type {
|
||||||
|
UserScoreType::Best => "https://osu.ppy.sh/api/get_user_best",
|
||||||
|
UserScoreType::Recent => "https://osu.ppy.sh/api/get_user_recent"
|
||||||
|
})
|
||||||
|
.query(&self.user.to_query())
|
||||||
|
.query(&self.mode.to_query())
|
||||||
|
.query(&self.limit.map(|v| ("limit", v.to_string())).to_query())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UserBestRequest {
|
pub struct UserBestRequest {
|
||||||
|
|
Loading…
Add table
Reference in a new issue