steam_client/services/
gifts.rs1use serde::Deserialize;
9
10use crate::{error::SteamError, SteamClient};
11
12#[derive(Debug, Clone)]
14pub struct GiftDetails {
15 pub gift_name: String,
17 pub package_id: u32,
19 pub owned: bool,
21}
22
23#[derive(Debug, Deserialize)]
25struct GiftValidateResponse {
26 success: Option<i32>,
27 gift_name: Option<String>,
28 #[serde(deserialize_with = "deserialize_id")]
29 packageid: Option<String>,
30 owned: Option<bool>,
31 message: Option<String>,
32}
33
34fn deserialize_id<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
36where
37 D: serde::Deserializer<'de>,
38{
39 use serde::Deserialize;
40 let v: serde_json::Value = Deserialize::deserialize(deserializer)?;
41 match v {
42 serde_json::Value::String(s) => Ok(Some(s)),
43 serde_json::Value::Number(n) => Ok(Some(n.to_string())),
44 serde_json::Value::Null => Ok(None),
45 _ => Err(serde::de::Error::custom("expected string or number")),
46 }
47}
48
49#[derive(Debug, Deserialize)]
51struct GiftRedeemResponse {
52 success: Option<i32>,
53 message: Option<String>,
54}
55
56fn extract_session_id(cookies: &str) -> Option<String> {
61 for part in cookies.split(';') {
62 let part = part.trim();
63 if let Some(value) = part.strip_prefix("sessionid=") {
64 return Some(value.to_string());
65 }
66 }
67 None
68}
69
70impl SteamClient {
71 pub async fn get_gift_details(&self, gift_id: &str, cookies: &str) -> Result<GiftDetails, SteamError> {
92 let session_id = extract_session_id(cookies).ok_or_else(|| SteamError::Other("No sessionid found in cookies".to_string()))?;
93
94 let url = format!("https://steamcommunity.com/gifts/{}/validateunpack", gift_id);
95
96 let client = reqwest::Client::builder()
98 .user_agent("Valve/Steam HTTP Client 1.0")
99 .default_headers({
100 let mut headers = reqwest::header::HeaderMap::new();
101 headers.insert(reqwest::header::COOKIE, cookies.parse().map_err(|_| SteamError::Other("Invalid cookie header".to_string()))?);
102 headers
103 })
104 .build()
105 .map_err(|e| SteamError::Other(format!("Failed to build HTTP client: {}", e)))?;
106
107 let response = client.post(&url).form(&[("sessionid", &session_id)]).send().await.map_err(|e| SteamError::NetworkError(std::io::Error::other(e)))?;
108
109 if !response.status().is_success() {
110 return Err(SteamError::Other(format!("HTTP error: {}", response.status())));
111 }
112
113 let body: GiftValidateResponse = response.json().await.map_err(|e| SteamError::ProtocolError(format!("Failed to parse response: {}", e)))?;
114
115 if body.success != Some(1) {
117 return Err(SteamError::Other(body.message.unwrap_or_else(|| "Unknown error validating gift".to_string())));
118 }
119
120 let gift_name = body.gift_name.ok_or_else(|| SteamError::ProtocolError("Missing gift_name in response".to_string()))?;
121 let package_id: u32 = body.packageid.ok_or_else(|| SteamError::ProtocolError("Missing packageid in response".to_string()))?.parse().map_err(|_| SteamError::ProtocolError("Invalid packageid".to_string()))?;
122
123 Ok(GiftDetails { gift_name, package_id, owned: body.owned.unwrap_or(false) })
124 }
125
126 pub async fn redeem_gift(&self, gift_id: &str, cookies: &str) -> Result<(), SteamError> {
147 let session_id = extract_session_id(cookies).ok_or_else(|| SteamError::Other("No sessionid found in cookies".to_string()))?;
148
149 let url = format!("https://steamcommunity.com/gifts/{}/unpack", gift_id);
150
151 let client = reqwest::Client::builder()
153 .user_agent("Valve/Steam HTTP Client 1.0")
154 .default_headers({
155 let mut headers = reqwest::header::HeaderMap::new();
156 headers.insert(reqwest::header::COOKIE, cookies.parse().map_err(|_| SteamError::Other("Invalid cookie header".to_string()))?);
157 headers
158 })
159 .build()
160 .map_err(|e| SteamError::Other(format!("Failed to build HTTP client: {}", e)))?;
161
162 let response = client.post(&url).form(&[("sessionid", &session_id)]).send().await.map_err(|e| SteamError::NetworkError(std::io::Error::other(e)))?;
163
164 if !response.status().is_success() {
165 return Err(SteamError::Other(format!("HTTP error: {}", response.status())));
166 }
167
168 let body: GiftRedeemResponse = response.json().await.map_err(|e| SteamError::ProtocolError(format!("Failed to parse response: {}", e)))?;
169
170 if body.success != Some(1) {
172 return Err(SteamError::Other(body.message.unwrap_or_else(|| "Unknown error redeeming gift".to_string())));
173 }
174
175 Ok(())
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182
183 #[test]
184 fn test_extract_session_id() {
185 let cookies = "sessionid=abc123; steamLoginSecure=xyz789";
186 assert_eq!(extract_session_id(cookies), Some("abc123".to_string()));
187
188 let cookies_no_session = "steamLoginSecure=xyz789";
189 assert_eq!(extract_session_id(cookies_no_session), None);
190
191 let cookies_with_spaces = " sessionid=def456 ; other=value";
192 assert_eq!(extract_session_id(cookies_with_spaces), Some("def456".to_string()));
193 }
194
195 #[test]
196 fn test_gift_details_struct() {
197 let details = GiftDetails { gift_name: "Test Game".to_string(), package_id: 12345, owned: false };
198 assert_eq!(details.gift_name, "Test Game");
199 assert_eq!(details.package_id, 12345);
200 assert!(!details.owned);
201 }
202}