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 response = self.http_client.post_form_with_cookies(&url, &[("sessionid", session_id.as_str())], cookies).await?;
97
98 if !response.is_success() {
99 return Err(SteamError::Other(format!("HTTP error: {}", response.status)));
100 }
101
102 let body: GiftValidateResponse = response.json().map_err(|e| SteamError::ProtocolError(format!("Failed to parse response: {}", e)))?;
103
104 if body.success != Some(1) {
106 return Err(SteamError::Other(body.message.unwrap_or_else(|| "Unknown error validating gift".to_string())));
107 }
108
109 let gift_name = body.gift_name.ok_or_else(|| SteamError::ProtocolError("Missing gift_name in response".to_string()))?;
110 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()))?;
111
112 Ok(GiftDetails { gift_name, package_id, owned: body.owned.unwrap_or(false) })
113 }
114
115 pub async fn redeem_gift(&self, gift_id: &str, cookies: &str) -> Result<(), SteamError> {
136 let session_id = extract_session_id(cookies).ok_or_else(|| SteamError::Other("No sessionid found in cookies".to_string()))?;
137
138 let url = format!("https://steamcommunity.com/gifts/{}/unpack", gift_id);
139
140 let response = self.http_client.post_form_with_cookies(&url, &[("sessionid", session_id.as_str())], cookies).await?;
141
142 if !response.is_success() {
143 return Err(SteamError::Other(format!("HTTP error: {}", response.status)));
144 }
145
146 let body: GiftRedeemResponse = response.json().map_err(|e| SteamError::ProtocolError(format!("Failed to parse response: {}", e)))?;
147
148 if body.success != Some(1) {
150 return Err(SteamError::Other(body.message.unwrap_or_else(|| "Unknown error redeeming gift".to_string())));
151 }
152
153 Ok(())
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn test_extract_session_id() {
163 let cookies = "sessionid=abc123; steamLoginSecure=xyz789";
164 assert_eq!(extract_session_id(cookies), Some("abc123".to_string()));
165
166 let cookies_no_session = "steamLoginSecure=xyz789";
167 assert_eq!(extract_session_id(cookies_no_session), None);
168
169 let cookies_with_spaces = " sessionid=def456 ; other=value";
170 assert_eq!(extract_session_id(cookies_with_spaces), Some("def456".to_string()));
171 }
172
173 #[test]
174 fn test_gift_details_struct() {
175 let details = GiftDetails { gift_name: "Test Game".to_string(), package_id: 12345, owned: false };
176 assert_eq!(details.gift_name, "Test Game");
177 assert_eq!(details.package_id, 12345);
178 assert!(!details.owned);
179 }
180}