use std::collections::HashMap;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct TransactionId(pub String);
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AddPhoneNumberResponse {
pub success: bool,
#[serde(rename = "showResend")]
pub show_resend: bool,
pub state: String,
#[serde(rename = "errorText")]
pub error_text: String,
pub token: String,
#[serde(rename = "phoneNumber")]
pub phone_number: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfirmPhoneCodeResponse {
pub success: bool,
#[serde(rename = "showResend")]
pub show_resend: bool,
pub state: serde_json::Value, #[serde(rename = "errorText")]
pub error_text: String,
pub token: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RemovePhoneResult {
pub success: bool,
pub method: Option<i32>,
#[serde(rename = "type")]
pub confirm_type: Option<String>,
pub link: Option<String>,
pub wizard_param: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WalletBalance {
#[serde(rename = "mainBalance")]
pub main_balance: Option<String>,
pub pending: Option<String>,
pub currency: Option<String>,
}
impl WalletBalance {
pub fn parse_main_balance(&self) -> Option<f64> {
crate::utils::parse_steam_balance(self.main_balance.as_deref()?)
}
pub fn parse_pending(&self) -> Option<f64> {
crate::utils::parse_steam_balance(self.pending.as_deref()?)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PurchaseHistoryItem {
pub date: DateTime<Utc>,
#[serde(rename = "type")]
pub transaction_type: String,
pub items: Vec<String>,
pub total: String,
pub base_price: Option<String>,
pub tax: Option<String>,
pub shipping: Option<String>,
pub wallet_change: Option<String>,
pub payment_method: Option<String>,
pub wallet_balance: Option<String>,
pub transaction_id: Option<TransactionId>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RedeemWalletCodeResponse {
pub success: i32,
pub detail: i32,
#[serde(rename = "formattednewwalletbalance")]
pub formatted_new_wallet_balance: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeviceIp {
pub v4: Option<u64>,
pub v6: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeviceLocation {
pub time: Option<u64>,
pub ip: Option<DeviceIp>,
pub locale: Option<String>,
pub country: Option<String>,
pub state: Option<String>,
pub city: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthorizedDevice {
pub token_id: String,
pub token_description: String,
pub time_updated: u64,
pub platform_type: i32,
pub logged_in: i32,
pub os_platform: i32,
pub auth_type: i32,
pub gaming_device_type: Option<i32>,
pub first_seen: Option<DeviceLocation>,
pub last_seen: Option<DeviceLocation>,
pub os_type: i32,
pub authentication_type: i32,
pub effective_token_state: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TwoFactorUsage {
pub time: u64,
pub usage_type: i32,
pub confirmation_type: i32,
pub confirmation_action: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TwoFactorStatus {
pub state: i32,
pub inactivation_reason: Option<serde_json::Value>,
pub authenticator_type: Option<i32>,
pub authenticator_allowed: Option<i32>,
pub steamguard_scheme: Option<i32>,
pub token_gid: Option<String>,
pub email_validated: Option<i32>,
pub device_identifier: Option<String>,
pub time_created: Option<u64>,
pub revocation_attempts_remaining: Option<i32>,
pub classified_agent: Option<String>,
pub allow_external_authenticator: Option<bool>,
pub time_transferred: Option<u64>,
pub version: Option<i32>,
pub last_seen_auth_token_id: Option<String>,
pub usages: Option<Vec<TwoFactorUsage>>,
pub success: Option<i32>,
pub rwgrsn: Option<i32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PageUserInfo {
pub logged_in: bool,
pub country_code: Option<String>,
#[serde(default)]
pub excluded_content_descriptors: Vec<i32>,
pub steamid: Option<String>,
pub accountid: Option<u64>,
pub account_name: Option<String>,
pub is_support: Option<bool>,
pub is_limited: Option<bool>,
pub is_partner_member: Option<bool>,
pub is_valve_email: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PageHwInfo {
#[serde(rename = "bSteamOS")]
pub steam_os: bool,
#[serde(rename = "bSteamDeck")]
pub steam_deck: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct PageConfig {
#[serde(rename = "EUNIVERSE")]
pub euniverse: Option<i32>,
#[serde(rename = "WEB_UNIVERSE")]
pub web_universe: Option<String>,
#[serde(rename = "LANGUAGE")]
pub language: Option<String>,
#[serde(rename = "COUNTRY")]
pub country: Option<String>,
#[serde(rename = "PLATFORM")]
pub platform: Option<String>,
#[serde(rename = "WEBSITE_ID")]
pub website_id: Option<String>,
#[serde(rename = "BUILD_TIMESTAMP")]
pub build_timestamp: Option<u64>,
#[serde(rename = "PAGE_TIMESTAMP")]
pub page_timestamp: Option<u64>,
#[serde(rename = "NOW")]
pub now: Option<u64>,
#[serde(rename = "EREALM")]
pub erealm: Option<i32>,
#[serde(rename = "SNR")]
pub snr: Option<String>,
#[serde(rename = "IN_CLIENT")]
pub in_client: Option<bool>,
#[serde(rename = "IN_TENFOOT")]
pub in_tenfoot: Option<bool>,
#[serde(rename = "IN_GAMEPADUI")]
pub in_gamepadui: Option<bool>,
#[serde(rename = "IN_CHROMEOS")]
pub in_chromeos: Option<bool>,
#[serde(rename = "IN_MOBILE_WEBVIEW")]
pub in_mobile_webview: Option<bool>,
#[serde(rename = "FROM_WEB")]
pub from_web: Option<bool>,
#[serde(rename = "USE_POPUPS")]
pub use_popups: Option<bool>,
#[serde(rename = "COMMUNITY_BASE_URL")]
pub community_base_url: Option<String>,
#[serde(rename = "STORE_BASE_URL")]
pub store_base_url: Option<String>,
#[serde(rename = "STORE_CHECKOUT_BASE_URL")]
pub store_checkout_base_url: Option<String>,
#[serde(rename = "HELP_BASE_URL")]
pub help_base_url: Option<String>,
#[serde(rename = "WEBAPI_BASE_URL")]
pub webapi_base_url: Option<String>,
#[serde(rename = "AVATAR_BASE_URL")]
pub avatar_base_url: Option<String>,
#[serde(rename = "LOGIN_BASE_URL")]
pub login_base_url: Option<String>,
#[serde(rename = "MEDIA_CDN_URL")]
pub media_cdn_url: Option<String>,
#[serde(rename = "MEDIA_CDN_COMMUNITY_URL")]
pub media_cdn_community_url: Option<String>,
#[serde(rename = "COMMUNITY_CDN_URL")]
pub community_cdn_url: Option<String>,
#[serde(rename = "COMMUNITY_CDN_ASSET_URL")]
pub community_cdn_asset_url: Option<String>,
#[serde(rename = "STORE_CDN_URL")]
pub store_cdn_url: Option<String>,
#[serde(rename = "STORE_ICON_BASE_URL")]
pub store_icon_base_url: Option<String>,
#[serde(rename = "STORE_ITEM_BASE_URL")]
pub store_item_base_url: Option<String>,
#[serde(rename = "VIDEO_CDN_URL")]
pub video_cdn_url: Option<String>,
#[serde(rename = "TOKEN_URL")]
pub token_url: Option<String>,
#[serde(rename = "STEAMTV_BASE_URL")]
pub steamtv_base_url: Option<String>,
#[serde(rename = "PARTNER_BASE_URL")]
pub partner_base_url: Option<String>,
#[serde(rename = "SUPPORT_BASE_URL")]
pub support_base_url: Option<String>,
#[serde(rename = "CHAT_BASE_URL")]
pub chat_base_url: Option<String>,
#[serde(rename = "PUBLIC_SHARED_URL")]
pub public_shared_url: Option<String>,
#[serde(rename = "BASE_URL_SHARED_CDN")]
pub base_url_shared_cdn: Option<String>,
#[serde(rename = "BASE_URL_STORE_CDN_ASSETS")]
pub base_url_store_cdn_assets: Option<String>,
#[serde(rename = "COMMUNITY_ASSETS_BASE_URL")]
pub community_assets_base_url: Option<String>,
#[serde(rename = "CLAN_CDN_ASSET_URL")]
pub clan_cdn_asset_url: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct StoreUserConfig {
pub webapi_token: Option<String>,
#[serde(flatten)]
pub extra: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PageNotification {
pub notification_id: String,
pub notification_targets: i32,
pub notification_type: i32,
pub body_data: String,
pub read: i32,
pub timestamp: u64,
pub hidden: i32,
pub expiry: u64,
pub viewed: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct PageNotifications {
#[serde(default)]
pub notifications: Vec<PageNotification>,
pub pending_gift_count: Option<i32>,
pub pending_friend_count: Option<i32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BroadcastUser {
pub success: i32,
#[serde(rename = "bHideStoreBroadcast")]
pub hide_store_broadcast: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct AccountDetails {
pub account_name: Option<String>,
pub email: Option<String>,
pub phone_hint: Option<String>,
pub latest_android_app_version: Option<String>,
pub requesting_token_id: Option<String>,
pub active_devices: Vec<AuthorizedDevice>,
pub revoked_devices: Vec<AuthorizedDevice>,
pub two_factor_status: Option<TwoFactorStatus>,
pub user_info: Option<PageUserInfo>,
pub hw_info: Option<PageHwInfo>,
pub page_config: Option<PageConfig>,
pub store_user_config: Option<StoreUserConfig>,
pub notifications: Option<PageNotifications>,
pub broadcast_user: Option<BroadcastUser>,
pub wallet_balance: Option<WalletBalance>,
pub avatar_hash: Option<String>,
pub country: Option<String>,
}
impl AccountDetails {
pub fn account_security(&self) -> Option<String> {
let tf = self.two_factor_status.as_ref()?;
match tf.state {
1 => Some("Steam Guard Mobile Authenticator".to_string()),
0 if tf.email_validated == Some(1) => Some("Steam Guard (Email)".to_string()),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_deserialize_redeem_wallet_response() {
let json = r#"{"success":1,"detail":0,"formattednewwalletbalance":"132.500\u20ab"}"#;
let response: RedeemWalletCodeResponse = serde_json::from_str(json).unwrap();
assert_eq!(response.success, 1);
assert_eq!(response.detail, 0);
assert_eq!(response.formatted_new_wallet_balance.as_deref(), Some("132.500₫"));
}
}