Skip to main content

steam_client/services/
familysharing.rs

1//! Family sharing functionality for Steam client.
2//!
3//! This module provides management of Steam Family Library Sharing,
4//! including authorized borrowers and sharing devices.
5
6use steam_enums::EMsg;
7use steamid::SteamID;
8
9use crate::{error::SteamError, SteamClient};
10
11/// An authorized borrower for family sharing.
12#[derive(Debug, Clone)]
13pub struct AuthorizedBorrower {
14    /// Borrower's SteamID.
15    pub steamid: SteamID,
16    /// Whether the authorization is pending.
17    pub is_pending: bool,
18    /// Whether the authorization was canceled.
19    pub is_canceled: bool,
20    /// Time the authorization was created (Unix timestamp).
21    pub time_created: u32,
22}
23
24/// An authorized sharing device.
25#[derive(Debug, Clone)]
26pub struct AuthorizedDevice {
27    /// Device token.
28    pub device_token: u64,
29    /// Device name.
30    pub device_name: String,
31    /// Whether the authorization is pending.
32    pub is_pending: bool,
33    /// Whether the authorization was canceled.
34    pub is_canceled: bool,
35    /// Whether the device is limited.
36    pub is_limited: bool,
37    /// Last time the device was used (Unix timestamp).
38    pub last_time_used: Option<u32>,
39    /// Last borrower who used this device.
40    pub last_borrower: Option<SteamID>,
41    /// Last app played on this device.
42    pub last_app_played: Option<u32>,
43}
44
45impl SteamClient {
46    /// Add new borrowers to your family sharing.
47    ///
48    /// # Arguments
49    /// * `borrowers` - SteamIDs of users to authorize as borrowers
50    pub async fn add_authorized_borrowers(&mut self, borrowers: Vec<SteamID>) -> Result<(), SteamError> {
51        if !self.is_logged_in() {
52            return Err(SteamError::NotLoggedOn);
53        }
54
55        let msg = steam_protos::CDeviceAuthAddAuthorizedBorrowersRequest {
56            steamid: self.steam_id.as_ref().map(|s| s.steam_id64()),
57            steamid_borrower: borrowers.iter().map(|s| s.steam_id64()).collect(),
58        };
59
60        let _: steam_protos::CDeviceAuthAddAuthorizedBorrowersResponse = self.send_unified_request_and_wait("DeviceAuth.AddAuthorizedBorrowers#1", &msg).await?;
61
62        Ok(())
63    }
64
65    /// Remove borrowers from your family sharing.
66    ///
67    /// # Arguments
68    /// * `borrowers` - SteamIDs of borrowers to remove
69    pub async fn remove_authorized_borrowers(&mut self, borrowers: Vec<SteamID>) -> Result<(), SteamError> {
70        if !self.is_logged_in() {
71            return Err(SteamError::NotLoggedOn);
72        }
73
74        let msg = steam_protos::CDeviceAuthRemoveAuthorizedBorrowersRequest {
75            steamid: self.steam_id.as_ref().map(|s| s.steam_id64()),
76            steamid_borrower: borrowers.iter().map(|s| s.steam_id64()).collect(),
77        };
78
79        let _: steam_protos::CDeviceAuthRemoveAuthorizedBorrowersResponse = self.send_unified_request_and_wait("DeviceAuth.RemoveAuthorizedBorrowers#1", &msg).await?;
80
81        Ok(())
82    }
83
84    /// Get a list of Steam accounts authorized to borrow your library.
85    ///
86    /// # Arguments
87    /// * `include_canceled` - Include canceled authorizations
88    /// * `include_pending` - Include pending authorizations
89    pub async fn get_authorized_borrowers(&mut self, include_canceled: bool, include_pending: bool) -> Result<Vec<AuthorizedBorrower>, SteamError> {
90        if !self.is_logged_in() {
91            return Err(SteamError::NotLoggedOn);
92        }
93
94        let msg = steam_protos::CDeviceAuthGetAuthorizedBorrowersRequest {
95            steamid: self.steam_id.as_ref().map(|s| s.steam_id64()),
96            include_canceled: Some(include_canceled),
97            include_pending: Some(include_pending),
98        };
99
100        let response: steam_protos::CDeviceAuthGetAuthorizedBorrowersResponse = self.send_unified_request_and_wait("DeviceAuth.GetAuthorizedBorrowers#1", &msg).await?;
101
102        let borrowers = response
103            .borrowers
104            .into_iter()
105            .map(|b| AuthorizedBorrower {
106                steamid: SteamID::from(b.steamid.unwrap_or(0)),
107                is_pending: b.is_pending.unwrap_or(false),
108                is_canceled: b.is_canceled.unwrap_or(false),
109                time_created: b.time_created.unwrap_or(0),
110            })
111            .collect();
112
113        Ok(borrowers)
114    }
115
116    /// Get a list of devices you have authorized for sharing.
117    ///
118    /// # Arguments
119    /// * `include_canceled` - Include canceled authorizations
120    pub async fn get_authorized_sharing_devices(&mut self, include_canceled: bool) -> Result<Vec<AuthorizedDevice>, SteamError> {
121        if !self.is_logged_in() {
122            return Err(SteamError::NotLoggedOn);
123        }
124
125        let msg = steam_protos::CDeviceAuthGetOwnAuthorizedDevicesRequest { steamid: self.steam_id.as_ref().map(|s| s.steam_id64()), include_canceled: Some(include_canceled) };
126
127        let response: steam_protos::CDeviceAuthGetOwnAuthorizedDevicesResponse = self.send_unified_request_and_wait("DeviceAuth.GetOwnAuthorizedDevices#1", &msg).await?;
128
129        let devices = response
130            .devices
131            .into_iter()
132            .map(|d| AuthorizedDevice {
133                device_token: d.auth_device_token.unwrap_or(0),
134                device_name: d.device_name.unwrap_or_default(),
135                is_pending: d.is_pending.unwrap_or(false),
136                is_canceled: d.is_canceled.unwrap_or(false),
137                is_limited: d.is_limited.unwrap_or(false),
138                last_time_used: d.last_time_used,
139                last_borrower: d.last_borrower_id.map(SteamID::from),
140                last_app_played: d.last_app_played,
141            })
142            .collect();
143
144        Ok(devices)
145    }
146
147    /// Authorize the local device for library sharing.
148    ///
149    /// # Arguments
150    /// * `device_name` - Name for this device
151    ///
152    /// # Returns
153    /// The device token for the newly authorized device.
154    pub async fn authorize_local_sharing_device(&mut self, device_name: &str) -> Result<u64, SteamError> {
155        if !self.is_logged_in() {
156            return Err(SteamError::NotLoggedOn);
157        }
158
159        let msg = steam_protos::CMsgClientAuthorizeLocalDeviceRequest {
160            device_description: Some(device_name.to_string()),
161            owner_account_id: Some(self.steam_id.as_ref().map(|s| s.account_id).unwrap_or(0)),
162            local_device_token: None,
163        };
164
165        let response: steam_protos::CMsgClientAuthorizeLocalDevice = self.send_request_and_wait(EMsg::ClientAuthorizeLocalDeviceRequest, &msg).await?;
166
167        if let Some(result) = response.eresult {
168            if result != 1 {
169                return Err(SteamError::SteamResult(steam_enums::EResult::from_i32(result).unwrap_or(steam_enums::EResult::Fail)));
170            }
171        }
172
173        Ok(response.authed_device_token.unwrap_or(0))
174    }
175
176    /// Deauthorize a sharing device.
177    ///
178    /// # Arguments
179    /// * `device_token` - The device token to deauthorize
180    pub async fn deauthorize_sharing_device(&mut self, device_token: u64) -> Result<(), SteamError> {
181        if !self.is_logged_in() {
182            return Err(SteamError::NotLoggedOn);
183        }
184
185        let msg = steam_protos::CMsgClientDeauthorizeDeviceRequest {
186            deauthorization_account_id: Some(self.steam_id.as_ref().map(|s| s.account_id).unwrap_or(0)),
187            deauthorization_device_token: Some(device_token),
188        };
189
190        let response: steam_protos::CMsgClientDeauthorizeDevice = self.send_request_and_wait(EMsg::ClientDeauthorizeDeviceRequest, &msg).await?;
191
192        if let Some(result) = response.eresult {
193            if result != 1 {
194                return Err(SteamError::SteamResult(steam_enums::EResult::from_i32(result).unwrap_or(steam_enums::EResult::Fail)));
195            }
196        }
197
198        Ok(())
199    }
200
201    /// Use local device authorizations to allow usage of shared licenses.
202    ///
203    /// # Arguments
204    /// * `owner_steam_id` - The SteamID of the library owner
205    /// * `device_token` - The device token authorized by the owner
206    pub async fn use_sharing_authorization(&mut self, owner_steam_id: SteamID, device_token: u64) -> Result<(), SteamError> {
207        if !self.is_logged_in() {
208            return Err(SteamError::NotLoggedOn);
209        }
210
211        let msg = steam_protos::CMsgClientUseLocalDeviceAuthorizations {
212            authorization_account_id: vec![owner_steam_id.account_id],
213            device_tokens: vec![steam_protos::c_msg_client_use_local_device_authorizations::DeviceToken { owner_account_id: Some(owner_steam_id.account_id), token_id: Some(device_token) }],
214        };
215
216        self.send_message(EMsg::ClientUseLocalDeviceAuthorizations, &msg).await
217    }
218
219    /// Deactivate family sharing authorizations.
220    pub async fn deactivate_sharing_authorization(&mut self) -> Result<(), SteamError> {
221        if !self.is_logged_in() {
222            return Err(SteamError::NotLoggedOn);
223        }
224
225        let msg = steam_protos::CMsgClientUseLocalDeviceAuthorizations { authorization_account_id: vec![], device_tokens: vec![] };
226
227        self.send_message(EMsg::ClientUseLocalDeviceAuthorizations, &msg).await
228    }
229}