diff options
-rw-r--r-- | Cargo.lock | 15 | ||||
-rw-r--r-- | Cargo.toml | 5 | ||||
-rw-r--r-- | src/main.rs | 71 |
3 files changed, 74 insertions, 17 deletions
@@ -411,6 +411,7 @@ dependencies = [ "anyhow", "hyper", "hyper-tls", + "serde", "serde_json", "tokio", ] @@ -519,6 +520,20 @@ name = "serde" version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "serde_json" @@ -23,3 +23,8 @@ version = "*" [dependencies.serde_json] version = "*" + +[dependencies.serde] +version = "*" +features = ["derive"] + diff --git a/src/main.rs b/src/main.rs index ce2150d..29aeb61 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use std::env; +use std::fmt::format; use std::net::SocketAddr; use std::sync::Arc; @@ -6,8 +7,31 @@ use hyper::{Body, Method, Request, Response, Server}; use hyper::client::{Client, HttpConnector}; use hyper::service::{make_service_fn, service_fn}; use hyper_tls::HttpsConnector; +use serde::{Deserialize, Serialize}; +use serde_json::Value; -async fn get_user_data(client: &Client<HttpsConnector<HttpConnector>>, token: &str, user_id: u64) -> anyhow::Result<serde_json::Value> { +macro_rules! unwrap_resp { + ($x:expr) => { + match $x { + Ok(x) => x, + Err(x) => return Ok(x) + } + }; +} + +#[derive(Deserialize, Debug)] +struct DiscordUserFormat { + accent_color: Option<i64>, + username: String, + discriminator: String, + id: String, + public_flags: i64, + #[serde(default)] bot: bool, + banner: Option<String>, + avatar: Option<String>, +} + +async fn get_user_data(client: &Client<HttpsConnector<HttpConnector>>, token: &str, user_id: u64) -> anyhow::Result<DiscordUserFormat> { let request = Request::builder() .method(Method::GET) .uri(format!("https://discord.com/api/v10/users/{}", user_id)) @@ -17,18 +41,15 @@ async fn get_user_data(client: &Client<HttpsConnector<HttpConnector>>, token: &s let mut x = client.request(request).await?; let body = hyper::body::to_bytes(x.body_mut()).await?; let json_data = String::from_utf8(Vec::from(body))?; - let json: serde_json::Value = serde_json::from_str(&json_data)?; + let json: DiscordUserFormat = serde_json::from_str(&json_data)?; Ok(json) } -async fn get_avatar_url(client: &Client<HttpsConnector<HttpConnector>>, token: &str, user_id: u64) -> anyhow::Result<String> { - let json = get_user_data(client, token, user_id).await?; - let id = json["id"].as_str().ok_or(anyhow::anyhow!("Missing avatar"))?; - let discrim = json["discriminator"].as_str().ok_or(anyhow::anyhow!("Missing discriminator"))?; - println!("Served request for {}: {}#{}", id, json["username"], discrim); - let avatar_url = match json["avatar"].as_str() { - None => default_avatar_url(discrim)?, - Some(avatar_hash) => format!("https://cdn.discordapp.com/avatars/{}/{}.png", id, avatar_hash) +fn get_avatar_url(json: &DiscordUserFormat) -> anyhow::Result<String> { + println!("Served request for {}: {}#{}", json.id, json.username, json.discriminator); + let avatar_url = match &json.avatar { + None => default_avatar_url(&json.discriminator)?, + Some(avatar_hash) => format!("https://cdn.discordapp.com/avatars/{}/{}.png", json.id, avatar_hash) }; Ok(avatar_url) } @@ -67,6 +88,7 @@ fn default_avatar_url(discrim: &str) -> anyhow::Result<String> { Ok(format!("https://cdn.discordapp.com/embed/avatars/{}.png", bare)) } +#[derive(Serialize, Debug)] struct ResponseUserFormat { username: String, discriminator: String, @@ -75,22 +97,37 @@ struct ResponseUserFormat { } async fn respond_with_json(arc: Arc<Ctx>, userid: &str) -> anyhow::Result<Response<Body>> { - let num_id = match userid.parse::<u64>() { - Err(_) => return make_err(404, "Not found"), - Ok(num) => num, + let json = unwrap_resp!(get_discord_data_for(&arc, userid).await?); + let avatar_url = get_avatar_url(&json)?; + let response = ResponseUserFormat { + username: json.username, + discriminator: json.discriminator, + avatar: avatar_url, + banner: json.banner.map(|hash| format!("https://cdn.discordapp.com/banners/{}/{}.png", json.id, hash)), }; Ok(Response::builder() .status(200) .header("content-type", "application/json") - .body(serde_json::to_string("{\"todo\": true}")?.into())?) + .body(serde_json::to_string(&response)?.into())?) } -async fn respond_with_image(arc: Arc<Ctx>, userid: &str) -> anyhow::Result<Response<Body>> { +async fn get_discord_data_for(arc: &Arc<Ctx>, userid: &str) -> anyhow::Result<anyhow::Result<DiscordUserFormat, Response<Body>>> { let num_id = match userid.parse::<u64>() { - Err(_) => return make_err(404, "Not found"), + Err(_) => return make_err(404, "Not found").map(Err), Ok(num) => num, }; - let avatar_url = match get_avatar_url(&arc.client, &arc.token, num_id).await { + Ok(Ok(match get_user_data(&arc.client, &arc.token, num_id).await { + Err(e) => { + eprintln!("Got error from discord: {:?}", e); + return make_err(502, "Discord failed to respond").map(Err) + }, + Ok(user_data) => user_data, + })) +} + +async fn respond_with_image(arc: Arc<Ctx>, userid: &str) -> anyhow::Result<Response<Body>> { + let json = unwrap_resp!(get_discord_data_for(&arc, userid).await?); + let avatar_url = match get_avatar_url(&json) { Err(_) => return make_err(502, "Discord failed to respond"), Ok(avatar_url) => avatar_url, }; |