diff options
Diffstat (limited to 'src/commands/osu.rs')
-rw-r--r-- | src/commands/osu.rs | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/src/commands/osu.rs b/src/commands/osu.rs new file mode 100644 index 0000000..2b3961f --- /dev/null +++ b/src/commands/osu.rs @@ -0,0 +1,166 @@ +use std::time::Duration; + +use crate::{Context, Error}; +use poise::serenity_prelude as serenity; +use reqwest::{header, ClientBuilder}; +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize)] +struct OsuTokenResponse { + pub access_token: String, +} + +#[derive(Serialize)] +struct OsuTokenRequest { + pub client_id: u32, + pub client_secret: String, + pub grant_type: String, + pub scope: String, +} + +async fn setup_reqwest() -> Result<reqwest::Client, Error> { + let client_id = std::env::var("OSU_CLIENT_ID").unwrap(); + let client_secret = std::env::var("OSU_CLIENT_SECRET").unwrap(); + + let token_req = OsuTokenRequest { + client_id: client_id.parse::<u32>().unwrap(), + client_secret, + grant_type: "client_credentials".into(), + scope: "public".into(), + }; + + let req = reqwest::Client::new().post("https://osu.ppy.sh/oauth/token") + .json(&token_req).send().await? + .json::<OsuTokenResponse>().await?; + + + let mut headers = header::HeaderMap::new(); + headers.insert("Authorization", header::HeaderValue::from_str(format!("Bearer {}", req.access_token).as_str()).unwrap()); + + Ok(ClientBuilder::new() + .default_headers(headers) + .build().unwrap()) +} + +#[derive(Deserialize, Serialize, Clone, Debug)] +struct OsuUser { + pub username: String, + pub avatar_url: String, + pub country_code: String, + pub is_supporter: bool, + pub join_date: chrono::DateTime<chrono::Utc>, + pub statistics: OsuUserStats, +} + +#[derive(Deserialize, Serialize, Clone, Debug)] +struct OsuUserStats { + pub global_rank: Option<u32>, + pub pp: f32, + pub hit_accuracy: Option<f32>, + pub grade_counts: OsuUserStatsGrades, + pub country_rank: Option<u32>, +} + +#[derive(Deserialize, Serialize, Clone, Debug)] +struct OsuUserStatsGrades { + pub ss: u32, + pub s: u32, + pub a: u32, +} + +/// Gets an osu profile by username +/// +/// Usage: +/// ~osup <username> +/// Examples: +/// ~osup muirrum +#[poise::command(slash_command, prefix_command)] +pub async fn osup(ctx: Context<'_>, + #[description = "The osu! username or ID to look up"] + lookup: String, + ) -> Result<(), Error> { + let client = setup_reqwest().await?; + + let mut res = client.get(format!("https://osu.ppy.sh/api/v2/users/{}?key=username", lookup)) + .send().await?.json::<OsuUser>().await?; + + res.country_code = res.country_code.to_lowercase(); + + ctx.send(|m| { + m.embed(|e| { + e.title(format!("osu! Profile: {}", res.clone().username)); + e.thumbnail(res.clone().avatar_url); + e.field("Ranks", format!(":map: #{}\n:flag_{}: #{}", res.clone().statistics.global_rank.unwrap_or(0), res.clone().country_code, res.clone().statistics.country_rank.unwrap_or(0u32)), true); + + e.field("Stats", format!("**PP:** {}\n**Acc:** {}%", res.clone().statistics.pp, res.clone().statistics.hit_accuracy.unwrap_or(0.0)), false); + e + }); + m + }).await?; + + Ok(()) +} + +#[derive(Deserialize, Debug, Clone)] +struct OsuBeatMap { + pub id: u32, + pub mode: String, + pub status: String, + pub version: String, + pub total_length: u32, + pub difficulty_rating: f32, + pub bpm: u32, + pub last_updated: chrono::DateTime<chrono::Utc>, + pub passcount: u32, + pub playcount: u32, + pub beatmapset: OsuBeatMapSet, +} + +#[derive(Deserialize, Debug, Clone)] +struct OsuBeatMapSet { + pub id: u32, + pub nsfw: bool, + pub title: String, + pub artist: String, + pub covers: OsuBeatMapSetCovers, + pub creator: String, + pub tags: String, + pub submitted_date: chrono::DateTime<chrono::Utc>, +} + +#[derive(Deserialize, Debug, Clone)] +struct OsuBeatMapSetCovers { + #[serde(rename = "list@2x")] + pub list2: String, +} + +/// Looks up an osu! beatmap by its ID +/// +/// Usage: +/// ~osubm <id> +#[poise::command(slash_command, prefix_command)] +pub async fn osubm(ctx: Context<'_>, + #[description = "The beatmap ID"] + bm_id: u32, + ) -> Result<(), Error> { + let client = setup_reqwest().await?; + + let mut res = client.get(format!("https://osu.ppy.sh/api/v2/beatmaps/{}", bm_id)) + .send().await?.json::<OsuBeatMap>().await?; + + ctx.send(|m| { + m.embed(|e| { + e.title(format!("osu! Beatmap: {} by {}", res.beatmapset.title, res.beatmapset.creator)); + e.image(res.beatmapset.covers.list2); + e.description(format!("**Length:** {} **BPM:** {}\n**Difficulty:** {}:star:", res.total_length, res.bpm, res.difficulty_rating)); + e.footer(|f| { + f.text(format!("BM ID {} | BM Set ID {}\nCreated {}", res.id, res.beatmapset.id, res.beatmapset.submitted_date)); + f + }); + e + }); + m + }).await?; + + Ok(()) +} |