From 628874f4e94ec6946abba232b0bc257a4c105fb0 Mon Sep 17 00:00:00 2001 From: Kyler <59854022+KylerOlsen@users.noreply.github.com> Date: Thu, 25 Jan 2024 22:51:40 -0700 Subject: [PATCH] Implemented first two endpoints --- Cargo.toml | 3 + README.md | 31 ++++++++++ src/lib.rs | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 199 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c249f71..60128d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,6 @@ edition = "2021" [dependencies] reqwest = { version = "0.11", features = ["json"] } tokio = { version = "1", features = ["full"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +base64 = "0.21.5" diff --git a/README.md b/README.md index 9f1ed62..b52d11e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,33 @@ # Purple Cello Mojang API Interface Purple Cello interface for the Mojang API in rust + +## Implemented endpoints + +[ ] ~~API Status (Removed)~~ +[x] Username to UUID +[ ] Usernames to UUIDs +[ ] ~~UUID to Name History (Removed)~~ +[x] UUID to Profile and Skin/Cape +[ ] Blocked Servers +[ ] Statistics +[ ] Profile Information +[ ] Player Attributes +[ ] Player Blocklist +[ ] Player Certificates +[ ] Profile Name Change Information +[ ] Check Product Voucher +[ ] Name Availability +[ ] Change Name +[ ] Change Skin +[ ] Upload Skin +[ ] Reset Skin +[ ] Hide Cape +[ ] Show Cape +[ ] Verify Security Location +[ ] Get Security Questions +[ ] Send Security Answers +[ ] Get Account Migration Information +[ ] Account Migration OTP +[ ] Verify Account Migration OTP +[ ] Submit Migration Token +[ ] Connect Xbox Live diff --git a/src/lib.rs b/src/lib.rs index 7d12d9a..2a0374f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,170 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right +// Yeahbut January 2024 + +use reqwest; +use serde::{Serialize, Deserialize}; +use base64::{Engine as _, engine::general_purpose}; + +#[derive(Serialize, Deserialize)] +pub struct UsernameToUuid { + #[serde(skip_serializing_if = "Option::is_none")] + pub id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub legacy: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub demo: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub path: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub errorMessage: Option, } -#[cfg(test)] -mod tests { - use super::*; +pub async fn username_to_uuid(username: &str) + -> Result> +{ + let url = format!( + "https://api.mojang.com/users/profiles/minecraft/{}", + username + ); - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); + let resp = reqwest::get(url) + .await? + .json::() + .await?; + + Ok(resp) +} + +#[derive(Serialize, Deserialize)] +pub struct ProfileTextureMetadata { + pub model: String, +} + +#[derive(Serialize, Deserialize)] +pub struct ProfileTexture { + pub url: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option, +} + +#[derive(Serialize, Deserialize)] +pub struct ProfileTextures { + #[serde(skip_serializing_if = "Option::is_none")] + pub SKIN: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub CAPE: Option, +} + +#[derive(Serialize, Deserialize)] +pub struct ProfileValue { + pub timestamp: i64, + pub profileId: String, + pub profileName: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub signatureRequired: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub textures: Option, +} + +#[derive(Serialize, Deserialize)] +pub struct ProfileProperty { + pub name: String, + pub value: ProfileValue, + #[serde(skip_serializing_if = "Option::is_none")] + pub signature: Option, +} + +#[derive(Serialize, Deserialize)] +struct ProfilePropertyPrivate { + pub name: String, + pub value: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub signature: Option, +} + +#[derive(Serialize, Deserialize)] +pub struct UUIDToProfile { + pub id: String, + pub name: String, + pub properties: Vec, + pub profileActions: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub legacy: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub demo: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub path: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub errorMessage: Option, +} + + +#[derive(Serialize, Deserialize)] +struct UUIDToProfilePrivate { + pub id: String, + pub name: String, + pub properties: Vec, + pub profileActions: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub legacy: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub demo: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub path: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub error: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub errorMessage: Option, +} + +fn get_profile_value(properties: Vec) + -> Result, Box> +{ + let mut output: Vec = Vec::new(); + + for property in properties { + output.push(ProfileProperty { + name: property.name, + value: serde_json::from_slice( + &general_purpose::STANDARD_NO_PAD.decode(property.value)? + )?, + signature: property.signature, + }) } + + Ok(output) } + +pub async fn uuid_to_profile(uuid: &str) + -> Result> +{ + let url = format!( + "https://sessionserver.mojang.com/session/minecraft/profile/{}", + uuid + ); + + let resp = reqwest::get(url) + .await? + .json::() + .await?; + + let output = UUIDToProfile { + id: resp.id, + name: resp.name, + properties: get_profile_value(resp.properties)?, + profileActions: resp.profileActions, + legacy: resp.legacy, + demo: resp.demo, + path: resp.path, + error: resp.error, + errorMessage: resp.errorMessage, + }; + + Ok(output) +} +