aboutsummaryrefslogtreecommitdiff
path: root/src/commands/osu.rs
diff options
context:
space:
mode:
authorCara Salter <cara@devcara.com>2022-01-07 13:33:31 -0500
committerCara Salter <cara@devcara.com>2022-01-07 13:33:31 -0500
commit3f9376a46efeb53b494a8b5272fc15be6ca5869a (patch)
treec3dc6e9a1b8362479718e8c8ea7d90ea6c3b6a75 /src/commands/osu.rs
downloadglitch-ng-3f9376a46efeb53b494a8b5272fc15be6ca5869a.tar.gz
glitch-ng-3f9376a46efeb53b494a8b5272fc15be6ca5869a.zip
Initial Commit
Diffstat (limited to 'src/commands/osu.rs')
-rw-r--r--src/commands/osu.rs166
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(())
+}