1use scraper::{Html, Selector};
4use serde_json::Value;
5
6use crate::{
7 client::SteamUser,
8 endpoint::steam_endpoint,
9 error::SteamUserError,
10 types::{AddPhoneNumberResponse, ConfirmPhoneCodeResponse, RemovePhoneResult},
11};
12
13impl SteamUser {
14 #[steam_endpoint(GET, host = Store, path = "/phone/manage", kind = Read)]
26 pub async fn get_phone_number_status(&self) -> Result<Option<String>, SteamUserError> {
27 let response = self.get_path("/phone/manage").send().await?.text().await?;
28
29 let document = Html::parse_document(&response);
30 let header_selector = Selector::parse("h2.pageheader").expect("valid CSS selector");
31
32 if let Some(header) = document.select(&header_selector).next() {
33 let header_text = header.text().collect::<String>().trim().to_string();
34 if header_text == "Add a phone number to your account" {
35 return Ok(Some("none".to_string()));
36 } else if header_text == "Manage phone number" {
37 let desc_selector = Selector::parse(".phone_header_description > span").expect("valid CSS selector");
38 if let Some(desc) = document.select(&desc_selector).next() {
39 return Ok(Some(desc.text().collect::<String>().trim().to_string()));
40 }
41 }
42 }
43
44 Ok(None)
45 }
46
47 #[steam_endpoint(POST, host = Store, path = "/phone/add_ajaxop", kind = Write)]
55 pub async fn add_phone_number(&self, phone: &str) -> Result<AddPhoneNumberResponse, SteamUserError> {
56 let response: AddPhoneNumberResponse = self.post_path("/phone/add_ajaxop").header("referer", "https://store.steampowered.com/phone/add").form(&[("op", "get_phone_number"), ("input", phone), ("confirmed", "1"), ("checkfortos", "1"), ("bisediting", "0"), ("token", "0")]).send().await?.json().await?;
57
58 Ok(response)
59 }
60
61 #[steam_endpoint(POST, host = Store, path = "/phone/add_ajaxop", kind = Write)]
68 pub async fn confirm_phone_code_for_add(&self, code: &str) -> Result<ConfirmPhoneCodeResponse, SteamUserError> {
69 let response: ConfirmPhoneCodeResponse = self.post_path("/phone/add_ajaxop").form(&[("op", "get_sms_code"), ("input", code), ("confirmed", "1"), ("checkfortos", "1"), ("bisediting", "0"), ("token", "0")]).send().await?.json().await?;
70
71 Ok(response)
72 }
73
74 #[steam_endpoint(POST, host = Store, path = "/phone/add_ajaxop", kind = Write)]
77 pub async fn resend_phone_verification_code(&self) -> Result<Value, SteamUserError> {
78 let response: Value = self.post_path("/phone/add_ajaxop").form(&[("op", "retry_email_verification"), ("input", ""), ("confirmed", "1"), ("checkfortos", "1"), ("bisediting", "0"), ("token", "0")]).send().await?.json().await?;
79
80 Ok(response)
81 }
82
83 #[steam_endpoint(GET, host = Help, path = "/en/wizard/HelpRemovePhoneNumber", kind = Recovery)]
89 pub async fn get_remove_phone_number_type(&self) -> Result<Option<RemovePhoneResult>, SteamUserError> {
90 let response = self.get_path("/en/wizard/HelpRemovePhoneNumber?redir=store/account").send().await?.text().await?;
91
92 let document = Html::parse_document(&response);
93 let button_selector = Selector::parse("a.help_wizard_button").expect("valid CSS selector");
94
95 for button in document.select(&button_selector) {
96 let text = button.text().collect::<String>().trim().to_string();
97 let link = button.value().attr("href").map(|l| if l.starts_with("http") { l.to_string() } else { format!("https://help.steampowered.com{}", l) });
98
99 let mut wizard_param = None;
100 if let Some(ref l) = link {
101 if let Ok(url) = url::Url::parse(l) {
102 let map: std::collections::HashMap<_, _> = url.query_pairs().into_owned().collect();
103 if !map.is_empty() {
104 wizard_param = serde_json::to_value(map).ok();
105 }
106 }
107 }
108
109 if text == "Send a confirmation to my Steam Mobile app" {
110 return Ok(Some(RemovePhoneResult { success: true, method: Some(8), confirm_type: Some("SteamAppConfirm".into()), link, wizard_param }));
111 }
112
113 if text.starts_with("Text an account verification code to my phone number ending in") {
114 return Ok(Some(RemovePhoneResult { success: true, method: Some(4), confirm_type: Some("PhoneTextingConfirm".into()), link, wizard_param }));
115 }
116
117 if text.starts_with("Email an account verification code to") {
118 return Ok(Some(RemovePhoneResult { success: true, method: Some(2), confirm_type: Some("EmailConfirm".into()), link, wizard_param }));
119 }
120 }
121
122 Ok(None)
123 }
124
125 #[steam_endpoint(POST, host = Help, path = "/en/wizard/AjaxSendAccountRecoveryCode", kind = Recovery)]
128 pub async fn send_account_recovery_code(&self, wizard_param: Value, method: i32) -> Result<Value, SteamUserError> {
129 let mut params = wizard_param;
130 if let Some(obj) = params.as_object_mut() {
131 obj.insert("method".to_string(), Value::Number(method.into()));
132 obj.insert("n".to_string(), Value::Number(1.into()));
133 obj.insert("wizard_ajax".to_string(), Value::String("1".into()));
134 obj.insert("gamepad".to_string(), Value::String("0".into()));
135 }
136
137 let response: Value = self.post_path("/en/wizard/AjaxSendAccountRecoveryCode").form(¶ms).send().await?.json().await?;
138
139 Ok(response)
140 }
141
142 #[steam_endpoint(GET, host = Help, path = "/en/wizard/AjaxVerifyAccountRecoveryCode", kind = Recovery)]
144 pub async fn confirm_remove_phone_number_code(&self, wizard_param: Value, code: &str) -> Result<Value, SteamUserError> {
145 let mut params = wizard_param;
146 if let Some(obj) = params.as_object_mut() {
147 obj.insert("code".to_string(), Value::String(code.to_string()));
148 obj.insert("wizard_ajax".to_string(), Value::String("1".into()));
149 obj.insert("gamepad".to_string(), Value::String("0".into()));
150 }
151
152 let response: Value = self.get_path("/en/wizard/AjaxVerifyAccountRecoveryCode").query(¶ms).send().await?.json().await?;
153
154 Ok(response)
155 }
156
157 #[steam_endpoint(POST, host = Help, path = "/en/wizard/AjaxSendConfirmation2SteamMobileApp", kind = Recovery)]
159 pub async fn send_confirmation_2_steam_mobile_app(&self, wizard_param: Value) -> Result<Value, SteamUserError> {
160 let mut params = wizard_param;
161 if let Some(obj) = params.as_object_mut() {
162 obj.insert("wizard_ajax".to_string(), Value::String("1".into()));
163 obj.insert("gamepad".to_string(), Value::String("0".into()));
164 }
165
166 let response: Value = self.post_path("/en/wizard/AjaxSendConfirmation2SteamMobileApp").form(¶ms).send().await?.json().await?;
167
168 Ok(response)
169 }
170
171 #[steam_endpoint(POST, host = Help, path = "/en/wizard/AjaxSendConfirmation2SteamMobileAppFinal", kind = Recovery)]
173 pub async fn send_confirmation_2_steam_mobile_app_final(&self, wizard_param: Value) -> Result<Value, SteamUserError> {
174 let mut params = wizard_param;
175 if let Some(obj) = params.as_object_mut() {
176 obj.insert("wizard_ajax".to_string(), Value::String("1".into()));
177 obj.insert("gamepad".to_string(), Value::String("0".into()));
178 }
179
180 let response: Value = self.post_path("/en/wizard/AjaxSendConfirmation2SteamMobileAppFinal").form(¶ms).send().await?.json().await?;
181
182 Ok(response)
183 }
184}