steam_resolve_vanity/
lib.rs1use reqwest::header::LOCATION;
2use reqwest::redirect::Policy;
3use reqwest::{Client, ClientBuilder, StatusCode};
4use serde::Deserialize;
5pub use steamid_ng::SteamID;
6use thiserror::Error;
7
8#[derive(Deserialize)]
9struct SteamApiResponse {
10 response: VanityUrlResponse,
11}
12
13#[derive(Deserialize)]
14struct VanityUrlResponse {
15 #[serde(default)]
16 steamid: Option<SteamID>,
17 success: u8,
18}
19
20#[derive(Debug, Error)]
21pub enum Error {
22 #[error("Invalid api key")]
23 InvalidKey,
24 #[error("Error while making request to steam api")]
25 Request(#[from] reqwest::Error),
26 #[error("Received malformed steam id")]
27 SteamId(#[from] steamid_ng::SteamIDError),
28}
29
30pub async fn resolve_vanity_url(url: &str, api_key: &str) -> Result<Option<SteamID>, Error> {
32 let response: reqwest::Response = Client::new()
33 .get("http://api.steampowered.com/ISteamUser/ResolveVanityURL/v0001/")
34 .query(&[("key", api_key), ("vanityurl", url)])
35 .send()
36 .await?;
37
38 if response.status() == StatusCode::FORBIDDEN {
39 return Err(Error::InvalidKey);
40 }
41
42 let api_response: SteamApiResponse = response.json().await?;
43
44 Ok(api_response
45 .response
46 .steamid
47 .filter(|_| api_response.response.success == 1))
48}
49
50pub async fn get_vanity_url(steam_id: SteamID) -> Result<Option<String>, Error> {
51 let response: reqwest::Response = ClientBuilder::new()
52 .redirect(Policy::none())
53 .build()
54 .unwrap()
55 .get(&format!(
56 "https://steamcommunity.com/profiles/{}",
57 u64::from(steam_id)
58 ))
59 .send()
60 .await?;
61
62 Ok(match response.status() {
63 StatusCode::FOUND => response
64 .headers()
65 .into_iter()
66 .find_map(|(name, value)| if name == LOCATION { Some(value) } else { None })
67 .and_then(|value| value.to_str().ok())
68 .map(|value| value.split_at("https://steamcommunity.com/id/".len()).1)
69 .map(|value| value.trim_end_matches("/").to_string()),
70 _ => None,
71 })
72}
73
74#[cfg(test)]
75#[tokio::test]
76async fn test_valid() {
77 let key = dotenv::var("STEAM_API_KEY").unwrap();
78 assert_eq!(
79 Some(SteamID::from(76561198024494988)),
80 resolve_vanity_url("icewind1991", &key).await.unwrap()
81 )
82}
83
84#[cfg(test)]
85#[tokio::test]
86async fn test_invalid_key() {
87 assert!(matches!(
88 resolve_vanity_url("icewind1991", "foo").await.unwrap_err(),
89 Error::InvalidKey
90 ))
91}
92
93#[cfg(test)]
94#[tokio::test]
95async fn test_not_found() {
96 let key = dotenv::var("STEAM_API_KEY").unwrap();
97 assert_eq!(
98 None,
99 resolve_vanity_url("hopefully_non_existing_steam_id", &key)
100 .await
101 .unwrap()
102 )
103}
104
105#[cfg(test)]
106#[tokio::test]
107async fn test_get_vanity() {
108 assert_eq!(
109 Some("icewind1991".to_string()),
110 get_vanity_url(SteamID::from(76561198024494988))
111 .await
112 .unwrap()
113 )
114}
115
116#[cfg(test)]
117#[tokio::test]
118async fn test_get_vanity_not_found() {
119 assert_eq!(
120 None,
121 get_vanity_url(SteamID::from(76561198024494987))
122 .await
123 .unwrap()
124 )
125}