1use std::collections::HashMap;
4
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub struct TransactionId(pub String);
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct AddPhoneNumberResponse {
15 pub success: bool,
16 #[serde(rename = "showResend")]
17 pub show_resend: bool,
18 pub state: String,
19 #[serde(rename = "errorText")]
20 pub error_text: String,
21 pub token: String,
22 #[serde(rename = "phoneNumber")]
23 pub phone_number: Option<String>,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct ConfirmPhoneCodeResponse {
28 pub success: bool,
29 #[serde(rename = "showResend")]
30 pub show_resend: bool,
31 pub state: serde_json::Value, #[serde(rename = "errorText")]
33 pub error_text: String,
34 pub token: String,
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct RemovePhoneResult {
39 pub success: bool,
40 pub method: Option<i32>,
41 #[serde(rename = "type")]
42 pub confirm_type: Option<String>,
43 pub link: Option<String>,
44 pub wizard_param: Option<serde_json::Value>,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct WalletBalance {
50 #[serde(rename = "mainBalance")]
51 pub main_balance: Option<String>,
52 pub pending: Option<String>,
53 pub currency: Option<String>,
54}
55
56impl WalletBalance {
57 pub fn parse_main_balance(&self) -> Option<f64> {
62 crate::utils::parse_steam_balance(self.main_balance.as_deref()?)
63 }
64
65 pub fn parse_pending(&self) -> Option<f64> {
67 crate::utils::parse_steam_balance(self.pending.as_deref()?)
68 }
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct PurchaseHistoryItem {
74 pub date: DateTime<Utc>,
77 #[serde(rename = "type")]
79 pub transaction_type: String,
80 pub items: Vec<String>,
82 pub total: String,
84 pub base_price: Option<String>,
86 pub tax: Option<String>,
88 pub shipping: Option<String>,
90 pub wallet_change: Option<String>,
92 pub payment_method: Option<String>,
94 pub wallet_balance: Option<String>,
96 pub transaction_id: Option<TransactionId>,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct RedeemWalletCodeResponse {
103 pub success: i32,
105 pub detail: i32,
107 #[serde(rename = "formattednewwalletbalance")]
109 pub formatted_new_wallet_balance: Option<String>,
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
116pub struct DeviceIp {
117 pub v4: Option<u64>,
118 pub v6: Option<serde_json::Value>,
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct DeviceLocation {
124 pub time: Option<u64>,
125 pub ip: Option<DeviceIp>,
126 pub locale: Option<String>,
127 pub country: Option<String>,
128 pub state: Option<String>,
129 pub city: Option<String>,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
137pub struct AuthorizedDevice {
138 pub token_id: String,
140 pub token_description: String,
142 pub time_updated: u64,
144 pub platform_type: i32,
146 pub logged_in: i32,
148 pub os_platform: i32,
150 pub auth_type: i32,
152 pub gaming_device_type: Option<i32>,
154 pub first_seen: Option<DeviceLocation>,
156 pub last_seen: Option<DeviceLocation>,
158 pub os_type: i32,
160 pub authentication_type: i32,
162 pub effective_token_state: i32,
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct TwoFactorUsage {
169 pub time: u64,
170 pub usage_type: i32,
171 pub confirmation_type: i32,
172 pub confirmation_action: i32,
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize)]
177pub struct TwoFactorStatus {
178 pub state: i32,
180 pub inactivation_reason: Option<serde_json::Value>,
181 pub authenticator_type: Option<i32>,
182 pub authenticator_allowed: Option<i32>,
183 pub steamguard_scheme: Option<i32>,
184 pub token_gid: Option<String>,
185 pub email_validated: Option<i32>,
186 pub device_identifier: Option<String>,
187 pub time_created: Option<u64>,
188 pub revocation_attempts_remaining: Option<i32>,
189 pub classified_agent: Option<String>,
190 pub allow_external_authenticator: Option<bool>,
191 pub time_transferred: Option<u64>,
192 pub version: Option<i32>,
193 pub last_seen_auth_token_id: Option<String>,
194 pub usages: Option<Vec<TwoFactorUsage>>,
195 pub success: Option<i32>,
197 pub rwgrsn: Option<i32>,
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize)]
203pub struct PageUserInfo {
204 pub logged_in: bool,
205 pub country_code: Option<String>,
206 #[serde(default)]
207 pub excluded_content_descriptors: Vec<i32>,
208 pub steamid: Option<String>,
209 pub accountid: Option<u64>,
210 pub account_name: Option<String>,
211 pub is_support: Option<bool>,
212 pub is_limited: Option<bool>,
213 pub is_partner_member: Option<bool>,
214 pub is_valve_email: Option<bool>,
215}
216
217#[derive(Debug, Clone, Serialize, Deserialize)]
219pub struct PageHwInfo {
220 #[serde(rename = "bSteamOS")]
221 pub steam_os: bool,
222 #[serde(rename = "bSteamDeck")]
223 pub steam_deck: bool,
224}
225
226#[derive(Debug, Clone, Serialize, Deserialize, Default)]
230pub struct PageConfig {
231 #[serde(rename = "EUNIVERSE")]
232 pub euniverse: Option<i32>,
233 #[serde(rename = "WEB_UNIVERSE")]
234 pub web_universe: Option<String>,
235 #[serde(rename = "LANGUAGE")]
236 pub language: Option<String>,
237 #[serde(rename = "COUNTRY")]
238 pub country: Option<String>,
239 #[serde(rename = "PLATFORM")]
240 pub platform: Option<String>,
241 #[serde(rename = "WEBSITE_ID")]
242 pub website_id: Option<String>,
243 #[serde(rename = "BUILD_TIMESTAMP")]
244 pub build_timestamp: Option<u64>,
245 #[serde(rename = "PAGE_TIMESTAMP")]
246 pub page_timestamp: Option<u64>,
247 #[serde(rename = "NOW")]
248 pub now: Option<u64>,
249 #[serde(rename = "EREALM")]
250 pub erealm: Option<i32>,
251 #[serde(rename = "SNR")]
252 pub snr: Option<String>,
253 #[serde(rename = "IN_CLIENT")]
254 pub in_client: Option<bool>,
255 #[serde(rename = "IN_TENFOOT")]
256 pub in_tenfoot: Option<bool>,
257 #[serde(rename = "IN_GAMEPADUI")]
258 pub in_gamepadui: Option<bool>,
259 #[serde(rename = "IN_CHROMEOS")]
260 pub in_chromeos: Option<bool>,
261 #[serde(rename = "IN_MOBILE_WEBVIEW")]
262 pub in_mobile_webview: Option<bool>,
263 #[serde(rename = "FROM_WEB")]
264 pub from_web: Option<bool>,
265 #[serde(rename = "USE_POPUPS")]
266 pub use_popups: Option<bool>,
267 #[serde(rename = "COMMUNITY_BASE_URL")]
268 pub community_base_url: Option<String>,
269 #[serde(rename = "STORE_BASE_URL")]
270 pub store_base_url: Option<String>,
271 #[serde(rename = "STORE_CHECKOUT_BASE_URL")]
272 pub store_checkout_base_url: Option<String>,
273 #[serde(rename = "HELP_BASE_URL")]
274 pub help_base_url: Option<String>,
275 #[serde(rename = "WEBAPI_BASE_URL")]
276 pub webapi_base_url: Option<String>,
277 #[serde(rename = "AVATAR_BASE_URL")]
278 pub avatar_base_url: Option<String>,
279 #[serde(rename = "LOGIN_BASE_URL")]
280 pub login_base_url: Option<String>,
281 #[serde(rename = "MEDIA_CDN_URL")]
282 pub media_cdn_url: Option<String>,
283 #[serde(rename = "MEDIA_CDN_COMMUNITY_URL")]
284 pub media_cdn_community_url: Option<String>,
285 #[serde(rename = "COMMUNITY_CDN_URL")]
286 pub community_cdn_url: Option<String>,
287 #[serde(rename = "COMMUNITY_CDN_ASSET_URL")]
288 pub community_cdn_asset_url: Option<String>,
289 #[serde(rename = "STORE_CDN_URL")]
290 pub store_cdn_url: Option<String>,
291 #[serde(rename = "STORE_ICON_BASE_URL")]
292 pub store_icon_base_url: Option<String>,
293 #[serde(rename = "STORE_ITEM_BASE_URL")]
294 pub store_item_base_url: Option<String>,
295 #[serde(rename = "VIDEO_CDN_URL")]
296 pub video_cdn_url: Option<String>,
297 #[serde(rename = "TOKEN_URL")]
298 pub token_url: Option<String>,
299 #[serde(rename = "STEAMTV_BASE_URL")]
300 pub steamtv_base_url: Option<String>,
301 #[serde(rename = "PARTNER_BASE_URL")]
302 pub partner_base_url: Option<String>,
303 #[serde(rename = "SUPPORT_BASE_URL")]
304 pub support_base_url: Option<String>,
305 #[serde(rename = "CHAT_BASE_URL")]
306 pub chat_base_url: Option<String>,
307 #[serde(rename = "PUBLIC_SHARED_URL")]
308 pub public_shared_url: Option<String>,
309 #[serde(rename = "BASE_URL_SHARED_CDN")]
310 pub base_url_shared_cdn: Option<String>,
311 #[serde(rename = "BASE_URL_STORE_CDN_ASSETS")]
312 pub base_url_store_cdn_assets: Option<String>,
313 #[serde(rename = "COMMUNITY_ASSETS_BASE_URL")]
314 pub community_assets_base_url: Option<String>,
315 #[serde(rename = "CLAN_CDN_ASSET_URL")]
316 pub clan_cdn_asset_url: Option<String>,
317}
318
319#[derive(Debug, Clone, Serialize, Deserialize, Default)]
323pub struct StoreUserConfig {
324 pub webapi_token: Option<String>,
326 #[serde(flatten)]
328 pub extra: HashMap<String, serde_json::Value>,
329}
330
331#[derive(Debug, Clone, Serialize, Deserialize)]
333pub struct PageNotification {
334 pub notification_id: String,
335 pub notification_targets: i32,
337 pub notification_type: i32,
339 pub body_data: String,
341 pub read: i32,
343 pub timestamp: u64,
344 pub hidden: i32,
345 pub expiry: u64,
346 pub viewed: Option<u64>,
347}
348
349#[derive(Debug, Clone, Serialize, Deserialize, Default)]
351pub struct PageNotifications {
352 #[serde(default)]
353 pub notifications: Vec<PageNotification>,
354 pub pending_gift_count: Option<i32>,
355 pub pending_friend_count: Option<i32>,
356}
357
358#[derive(Debug, Clone, Serialize, Deserialize)]
360pub struct BroadcastUser {
361 pub success: i32,
362 #[serde(rename = "bHideStoreBroadcast")]
363 pub hide_store_broadcast: i32,
364}
365
366#[derive(Debug, Clone, Serialize, Deserialize, Default)]
368pub struct AccountDetails {
369 pub account_name: Option<String>,
371 pub email: Option<String>,
373 pub phone_hint: Option<String>,
375 pub latest_android_app_version: Option<String>,
378 pub requesting_token_id: Option<String>,
381 pub active_devices: Vec<AuthorizedDevice>,
383 pub revoked_devices: Vec<AuthorizedDevice>,
385 pub two_factor_status: Option<TwoFactorStatus>,
387 pub user_info: Option<PageUserInfo>,
389 pub hw_info: Option<PageHwInfo>,
391 pub page_config: Option<PageConfig>,
393 pub store_user_config: Option<StoreUserConfig>,
395 pub notifications: Option<PageNotifications>,
397 pub broadcast_user: Option<BroadcastUser>,
399 pub wallet_balance: Option<WalletBalance>,
401 pub avatar_hash: Option<String>,
403 pub country: Option<String>,
405}
406
407impl AccountDetails {
408 pub fn account_security(&self) -> Option<String> {
415 let tf = self.two_factor_status.as_ref()?;
416 match tf.state {
417 1 => Some("Steam Guard Mobile Authenticator".to_string()),
418 0 if tf.email_validated == Some(1) => Some("Steam Guard (Email)".to_string()),
419 _ => None,
420 }
421 }
422}
423
424#[cfg(test)]
425mod tests {
426 use super::*;
427
428 #[test]
429 fn test_deserialize_redeem_wallet_response() {
430 let json = r#"{"success":1,"detail":0,"formattednewwalletbalance":"132.500\u20ab"}"#;
431 let response: RedeemWalletCodeResponse = serde_json::from_str(json).unwrap();
432 assert_eq!(response.success, 1);
433 assert_eq!(response.detail, 0);
434 assert_eq!(response.formatted_new_wallet_balance.as_deref(), Some("132.500₫"));
435 }
436}