use steam_user::services::account::parse_account_details_html;
use tracing::{error, info};
fn unwrap_viewsource(raw: &str) -> String {
let stripped = regex::Regex::new(r"<[^>]+>").expect("regex compilation failed").replace_all(raw, "");
stripped.replace("<", "<").replace(">", ">").replace(""", "\"").replace("'", "'").replace("&", "&")
}
fn main() {
let path = std::env::args().nth(1).unwrap_or_else(|| r"C:\Users\Admin\Downloads\view-source_https___store.steampowered.com_account_authorizeddevices.html".to_string());
let raw = std::fs::read_to_string(&path).unwrap_or_else(|e| {
error!("Failed to read '{}': {}", path, e);
std::process::exit(1);
});
let html = if raw.contains(r#"class="line-content""#) {
error!("[info] Detected Firefox view-source file — unwrapping...");
unwrap_viewsource(&raw)
} else {
raw
};
let page = parse_account_details_html(&html);
info!("═══════════════════════ ACCOUNT ═══════════════════════");
info!(" account_name : {:?}", page.account_name);
info!(" email : {:?}", page.email);
info!(" phone_hint : {:?}", page.phone_hint);
info!(" avatar_hash : {:?}", page.avatar_hash);
info!(" requesting_token_id : {:?}", page.requesting_token_id);
info!(" latest_android_ver : {:?}", page.latest_android_app_version);
info!(" country : {:?}", page.country);
info!(" account_security : {:?}", page.account_security());
info!("\n═══════════════════════ WALLET ════════════════════════");
if let Some(w) = &page.wallet_balance {
info!(" main_balance : {:?}", w.main_balance);
info!(" currency : {:?}", w.currency);
info!(" pending : {:?}", w.pending);
info!(" parsed amount : {:?}", w.parse_main_balance());
} else {
info!(" (not found)");
}
info!("\n═══════════════════════ USER INFO ═════════════════════");
if let Some(u) = &page.user_info {
info!(" logged_in : {}", u.logged_in);
info!(" steamid : {:?}", u.steamid);
info!(" accountid : {:?}", u.accountid);
info!(" account_name : {:?}", u.account_name);
info!(" country_code : {:?}", u.country_code);
info!(" is_limited : {:?}", u.is_limited);
info!(" is_support : {:?}", u.is_support);
info!(" is_partner_member: {:?}", u.is_partner_member);
info!(" is_valve_email : {:?}", u.is_valve_email);
info!(" excluded_content : {:?}", u.excluded_content_descriptors);
} else {
info!(" (not found)");
}
info!("\n═══════════════════════ HW INFO ═══════════════════════");
if let Some(h) = &page.hw_info {
info!(" steam_os : {}", h.steam_os);
info!(" steam_deck : {}", h.steam_deck);
} else {
info!(" (not found)");
}
info!("\n══════════════════ TWO-FACTOR STATUS ══════════════════");
if let Some(tf) = &page.two_factor_status {
info!(" state : {}", tf.state);
info!(" authenticator_type : {:?}", tf.authenticator_type);
info!(" authenticator_allowed : {:?}", tf.authenticator_allowed);
info!(" steamguard_scheme : {:?}", tf.steamguard_scheme);
info!(" email_validated : {:?}", tf.email_validated);
info!(" token_gid : {:?}", tf.token_gid);
info!(" time_created : {:?}", tf.time_created);
info!(" revocation_attempts_remaining: {:?}", tf.revocation_attempts_remaining);
info!(" allow_external_authenticator : {:?}", tf.allow_external_authenticator);
info!(" version : {:?}", tf.version);
info!(" success : {:?}", tf.success);
info!(" rwgrsn : {:?}", tf.rwgrsn);
if let Some(usages) = &tf.usages {
info!(" usages ({}):", usages.len());
for u in usages {
info!(" time={} type={} conf_type={} conf_action={}", u.time, u.usage_type, u.confirmation_type, u.confirmation_action);
}
}
} else {
info!(" (not found)");
}
info!("\n═══════════════════════ ACTIVE DEVICES ({}) ═══════════════════════", page.active_devices.len());
for (i, d) in page.active_devices.iter().enumerate() {
print_device(i + 1, d);
}
info!("\n══════════════════════ REVOKED DEVICES ({}) ══════════════════════", page.revoked_devices.len());
for (i, d) in page.revoked_devices.iter().enumerate() {
print_device(i + 1, d);
}
info!("\n══════════════════════ NOTIFICATIONS ══════════════════");
if let Some(n) = &page.notifications {
info!(" pending_gift_count : {:?}", n.pending_gift_count);
info!(" pending_friend_count: {:?}", n.pending_friend_count);
info!(" notifications ({}):", n.notifications.len());
for notif in &n.notifications {
info!(" id={} type={} read={} ts={}", notif.notification_id, notif.notification_type, notif.read, notif.timestamp);
}
} else {
info!(" (not found)");
}
info!("\n════════════════════════ BROADCAST ════════════════════");
if let Some(b) = &page.broadcast_user {
info!(" success : {}", b.success);
info!(" hide_store_broadcast : {}", b.hide_store_broadcast);
} else {
info!(" (not found)");
}
info!("\n═══════════════════ STORE USER CONFIG ═════════════════");
if let Some(c) = &page.store_user_config {
let token_preview = c.webapi_token.as_deref().map(|t| if t.len() > 40 { format!("{}…", &t[..40]) } else { t.to_string() });
info!(" webapi_token (truncated): {:?}", token_preview);
info!(" extra keys: {:?}", c.extra.keys().collect::<Vec<_>>());
} else {
info!(" (not found)");
}
info!("\n════════════════════════ PAGE CONFIG ══════════════════");
if let Some(c) = &page.page_config {
info!(" language : {:?}", c.language);
info!(" country : {:?}", c.country);
info!(" platform : {:?}", c.platform);
info!(" website_id : {:?}", c.website_id);
info!(" euniverse : {:?}", c.euniverse);
info!(" erealm : {:?}", c.erealm);
info!(" build_timestamp : {:?}", c.build_timestamp);
info!(" page_timestamp : {:?}", c.page_timestamp);
info!(" now : {:?}", c.now);
info!(" in_client : {:?}", c.in_client);
info!(" from_web : {:?}", c.from_web);
info!(" snr : {:?}", c.snr);
info!(" store_base_url : {:?}", c.store_base_url);
info!(" webapi_base_url : {:?}", c.webapi_base_url);
info!(" avatar_base_url : {:?}", c.avatar_base_url);
} else {
info!(" (not found)");
}
}
fn print_device(n: usize, d: &steam_user::types::AuthorizedDevice) {
info!(" [{n}] token_id : {}", d.token_id);
info!(" description : {}", d.token_description);
info!(" platform : {} os_platform: {} auth_type: {} state: {}", d.platform_type, d.os_platform, d.auth_type, d.effective_token_state);
info!(" logged_in : {} os_type: {} auth_type2: {}", d.logged_in, d.os_type, d.authentication_type);
info!(" updated : {}", d.time_updated);
if let Some(ls) = &d.last_seen {
let ip = ls
.ip
.as_ref()
.and_then(|i| i.v4)
.map(|v| {
let b = (v as u32).to_be_bytes();
format!("{}.{}.{}.{}", b[0], b[1], b[2], b[3])
})
.unwrap_or_else(|| "—".into());
info!(" last_seen : time={} ip={} {}, {}", ls.time.unwrap_or(0), ip, ls.country.as_deref().unwrap_or("?"), ls.city.as_deref().unwrap_or("?"));
}
}