Skip to main content

steam_client/services/
account.rs

1//! Account management.
2//!
3//! This module provides functionality to manage Steam account settings
4//! including email validation, Steam Guard details, and privacy settings.
5
6use crate::{error::SteamError, SteamClient};
7
8/// Steam Guard authentication details.
9#[derive(Debug, Clone)]
10pub struct SteamGuardDetails {
11    /// Whether Steam Guard is enabled.
12    pub is_steamguard_enabled: bool,
13    /// Whether email authentication is enabled.
14    pub is_email_auth_enabled: bool,
15    /// Whether mobile authenticator is enabled.
16    pub is_mobile_auth_enabled: bool,
17    /// The timestamp when Steam Guard was enabled.
18    pub timestamp_steamguard_enabled: Option<u64>,
19    /// The timestamp when two-factor was enabled.
20    pub timestamp_two_factor_enabled: Option<u64>,
21    /// Session data (machine tokens, etc).
22    pub machine_sessions: Vec<MachineSession>,
23}
24
25/// A machine session for Steam Guard.
26#[derive(Debug, Clone)]
27pub struct MachineSession {
28    /// Machine name.
29    pub machine_name: String,
30    /// Timestamp of first login.
31    pub first_login: u64,
32    /// Timestamp of last login.
33    pub last_login: u64,
34    /// IP address of last login.
35    pub last_ip: Option<String>,
36}
37
38/// Account privacy settings.
39#[derive(Debug, Clone)]
40pub struct PrivacySettings {
41    /// Profile privacy state (1=private, 2=friends only, 3=public).
42    pub privacy_state: u32,
43    /// Inventory privacy state.
44    pub privacy_state_inventory: u32,
45    /// Gifts privacy state.
46    pub privacy_state_gifts: u32,
47    /// Owned games privacy state.
48    pub privacy_state_owned_games: u32,
49    /// Playtime privacy state.
50    pub privacy_state_playtime: u32,
51    /// Friends list privacy state.
52    pub privacy_state_friends_list: u32,
53}
54
55/// Account limitations information.
56#[derive(Debug, Clone)]
57pub struct AccountLimitations {
58    /// Whether this is a limited account.
59    pub is_limited: bool,
60    /// Whether the account is community banned.
61    pub is_community_banned: bool,
62    /// Whether the account is locked.
63    pub is_locked: bool,
64    /// Whether limited accounts can invite friends.
65    pub can_invite_friends: bool,
66}
67
68/// Email information for the account.
69#[derive(Debug, Clone)]
70pub struct EmailInfo {
71    /// Email address.
72    pub address: String,
73    /// Whether the email is validated.
74    pub validated: bool,
75}
76
77/// VAC ban status.
78#[derive(Debug, Clone)]
79pub struct VacBans {
80    /// Number of VAC bans.
81    pub num_bans: u32,
82    /// App IDs with bans.
83    pub app_ids: Vec<u32>,
84}
85
86/// Wallet information.
87#[derive(Debug, Clone)]
88pub struct WalletInfo {
89    /// Whether the account has a wallet.
90    pub has_wallet: bool,
91    /// Wallet balance in cents.
92    pub balance: i32,
93    /// Currency code.
94    pub currency: i32,
95}
96
97impl SteamClient {
98    /// Request a validation email be sent to the account's email address.
99    ///
100    /// # Example
101    ///
102    /// ```rust,ignore
103    /// client.request_validation_email().await?;
104    /// tracing::info!("Validation email sent!");
105    /// ```
106    pub async fn request_validation_email(&mut self) -> Result<(), SteamError> {
107        if !self.is_logged_in() {
108            return Err(SteamError::NotLoggedOn);
109        }
110
111        let request = steam_protos::CMsgClientRequestValidationEmail {};
112        self.send_message(steam_enums::EMsg::ClientRequestValidationMail, &request).await
113    }
114
115    /// Get Steam Guard status and details.
116    ///
117    /// # Returns
118    ///
119    /// Returns Steam Guard status including whether mobile authenticator is
120    /// enabled.
121    ///
122    /// # Note
123    ///
124    /// This requires the unified message infrastructure.
125    pub async fn get_steam_guard_details(&mut self) -> Result<SteamGuardDetails, SteamError> {
126        if !self.is_logged_in() {
127            return Err(SteamError::NotLoggedOn);
128        }
129
130        let request = steam_protos::CCredentialsGetSteamGuardDetailsRequest::default();
131        self.send_service_method("Credentials.GetSteamGuardDetails#1", &request).await?;
132
133        // TODO: Response handling requires async event correlation.
134        // For now, return default values as placeholder.
135        Ok(SteamGuardDetails {
136            is_steamguard_enabled: false,
137            is_email_auth_enabled: false,
138            is_mobile_auth_enabled: false,
139            timestamp_steamguard_enabled: None,
140            timestamp_two_factor_enabled: None,
141            machine_sessions: Vec::new(),
142        })
143    }
144
145    /// Get the account's profile privacy settings.
146    ///
147    /// # Returns
148    ///
149    /// Returns the privacy settings for various profile sections.
150    ///
151    /// # Note
152    ///
153    ///
154    /// The request is sent via unified messages and awaited asynchronously.
155    pub async fn get_privacy_settings(&mut self) -> Result<PrivacySettings, SteamError> {
156        if !self.is_logged_in() {
157            return Err(SteamError::NotLoggedOn);
158        }
159
160        // Send request and wait for response
161        let request = steam_protos::CPlayerGetPrivacySettingsRequest {};
162        let response: steam_protos::CPlayerGetPrivacySettingsResponse = self.send_unified_request_and_wait("Player.GetPrivacySettings#1", &request).await?;
163
164        if let Some(settings) = response.privacy_settings {
165            Ok(PrivacySettings {
166                privacy_state: settings.privacy_state.unwrap_or(0) as u32,
167                privacy_state_inventory: settings.privacy_state_inventory.unwrap_or(0) as u32,
168                privacy_state_gifts: settings.privacy_state_gifts.unwrap_or(0) as u32,
169                privacy_state_owned_games: settings.privacy_state_ownedgames.unwrap_or(0) as u32,
170                privacy_state_playtime: settings.privacy_state_playtime.unwrap_or(0) as u32,
171                privacy_state_friends_list: settings.privacy_state_friendslist.unwrap_or(0) as u32,
172            })
173        } else {
174            Err(SteamError::Other("No privacy settings returned".into()))
175        }
176    }
177
178    /// Get account limitations.
179    ///
180    /// # Returns
181    ///
182    /// Returns account limitations like community ban status, etc.
183    pub fn get_account_limitations(&self) -> Result<AccountLimitations, SteamError> {
184        if let Some(limitations) = self.account.read().limitations.clone() {
185            Ok(AccountLimitations {
186                is_limited: limitations.limited,
187                is_community_banned: limitations.community_banned,
188                is_locked: limitations.locked,
189                can_invite_friends: limitations.can_invite_friends,
190            })
191        } else {
192            Err(SteamError::Other("Account limitations not available yet".into()))
193        }
194    }
195
196    /// Get VAC ban status.
197    ///
198    /// # Returns
199    ///
200    /// Returns VAC ban status.
201    pub fn get_vac_bans(&self) -> Result<VacBans, SteamError> {
202        if let Some(vac) = self.account.read().vac.clone() {
203            Ok(VacBans { num_bans: vac.num_bans, app_ids: vac.appids.clone() })
204        } else {
205            Err(SteamError::Other("VAC status not available yet".into()))
206        }
207    }
208
209    /// Get credential change times (password, email changes).
210    ///
211    /// # Returns
212    ///
213    /// Returns timestamps for when credentials were last changed.
214    ///
215    /// # Note
216    ///
217    /// This requires the unified message infrastructure.
218    pub async fn get_credential_change_times(&mut self) -> Result<CredentialChangeTimes, SteamError> {
219        if !self.is_logged_in() {
220            return Err(SteamError::NotLoggedOn);
221        }
222
223        let request = steam_protos::CCredentialsLastCredentialChangeTimeRequest { user_changes_only: Some(true) };
224        let response: steam_protos::CCredentialsLastCredentialChangeTimeResponse = self.send_unified_request_and_wait("Credentials.GetCredentialChangeTimeDetails#1", &request).await?;
225
226        Ok(CredentialChangeTimes {
227            password_last_changed: response.timestamp_last_password_change.map(|t| t as u64),
228            email_last_changed: response.timestamp_last_email_change.map(|t| t as u64),
229        })
230    }
231
232    /// Get account authentication secret.
233    ///
234    /// # Returns
235    ///
236    /// Returns the secret ID and secret key.
237    pub async fn get_auth_secret(&mut self) -> Result<(i32, Vec<u8>), SteamError> {
238        if !self.is_logged_in() {
239            return Err(SteamError::NotLoggedOn);
240        }
241
242        let request = steam_protos::CCredentialsGetAccountAuthSecretRequest::default();
243        let response: steam_protos::CCredentialsGetAccountAuthSecretResponse = self.send_unified_request_and_wait("Credentials.GetAccountAuthSecret#1", &request).await?;
244
245        Ok((response.secret_id.unwrap_or(0), response.secret.unwrap_or_default()))
246    }
247}
248
249/// Credential change timestamps.
250#[derive(Debug, Clone)]
251pub struct CredentialChangeTimes {
252    /// Timestamp of last password change.
253    pub password_last_changed: Option<u64>,
254    /// Timestamp of last email change.
255    pub email_last_changed: Option<u64>,
256}
257
258/// Account info received from Steam.
259#[derive(Debug, Clone)]
260pub struct AccountInfo {
261    /// Persona name.
262    pub name: String,
263    /// IP country code.
264    pub country: String,
265    /// Number of authorized computers.
266    pub authed_machines: i32,
267    /// Account flags.
268    pub flags: u32,
269    /// Facebook ID if linked.
270    pub facebook_id: Option<u64>,
271    /// Facebook name if linked.
272    pub facebook_name: Option<String>,
273    /// Whether phone is verified.
274    pub phone_verified: bool,
275    /// Two-factor state.
276    pub two_factor_state: u32,
277}
278
279impl From<&steam_protos::CMsgClientAccountInfo> for AccountInfo {
280    fn from(msg: &steam_protos::CMsgClientAccountInfo) -> Self {
281        Self {
282            name: msg.persona_name.clone().unwrap_or_default(),
283            country: msg.ip_country.clone().unwrap_or_default(),
284            authed_machines: msg.count_authed_computers.unwrap_or(0),
285            flags: msg.account_flags.unwrap_or(0),
286            facebook_id: msg.facebook_id,
287            facebook_name: msg.facebook_name.clone(),
288            phone_verified: msg.is_phone_verified.unwrap_or(false),
289            two_factor_state: msg.two_factor_state.unwrap_or(0),
290        }
291    }
292}