steam-user 0.1.0

Steam User web client for Rust - HTTP-based Steam Community interactions
Documentation

steam-user

A Rust library for HTTP-based interactions with Steam Community web endpoints, providing functionality for mobile confirmations, profile management, market operations, and more.

Features

  • Mobile Confirmations - Accept/cancel trade and market confirmations
  • Two-Factor Authentication - Enable/disable Steam Guard, generate backup codes, deauthorize devices
  • Profile Management - Edit profile settings, privacy options, avatars, and nicknames
  • Market Operations - Create listings, search items, calculate gem values
  • Inventory Access - Fetch user inventories and item details
  • Group Management - Join groups, accept/decline invites
  • User Interactions - Post comments, invite to groups, search users
  • Comment Management - Post, delete, and list profile comments with detailed author info
  • Account Details - Retrieve wallet balance, email, country, and security status
  • Wallet & Spending - Get precise wallet balance, currency, and total amount spent on Steam
  • Phone Number Management - Add, confirm, and remove phone numbers
  • Token Management - Enumerate active refresh tokens, verify existence, and revoke tokens
  • Trade Management - Send/accept/decline trade offers, parse and retrieve trade URLs
  • License Management - Add free game licenses and sub packages
  • Loyalty Rewards - Redeem Steam Points for community items and profile rewards
  • Steam App Management - Get owned apps, fetch app details, and CS:GO account stats
  • File Uploads - Upload images for chat and specific use cases
  • Match History - Retrieve CS:GO/CS2 match history with player stats, scores, and GOTV replays
  • Help Requests - List active/past support tickets and view conversation details
  • Player Reports - Retrieve history of reported players

Installation

Add to your Cargo.toml:

[dependencies]

steam-user = { path = "../steam-user" }

Feature Flags

Feature Default Description
remote Enables RemoteSteamUser, an HTTP client that delegates to a steam-user-api REST server
# Enable the remote client

steam-user = { path = "../steam-user", features = ["remote"] }

Usage

Basic Setup

use steam_user::SteamUser;

#[tokio::main]
async fn main() -> Result<(), steam_user::SteamUserError> {
    // Create a new SteamUser client with cookies from login
    // Note: sessionid and l=english query parameters are automatically added to requests
    let mut community = SteamUser::new(&[
        "steamLoginSecure=...",
        "sessionid=...",
    ])?;
    
    // Check if logged in (Returns: is_logged_in, is_family_view_restricted)
    let (logged_in, family_view) = community.logged_in().await?;
    println!("Logged in: {}, Family View: {}", logged_in, family_view);
    
    Ok(())
}

Mobile Confirmations

use steam_user::SteamUser;

// Fetch pending confirmations (tag defaults to "conf" if None)
let confirmations = community.get_confirmations("identity_secret_base64", None).await?;

// Get the object ID (e.g., Trade Offer ID) for a confirmation
// This scrapes the details page
if let Some(offer_id) = community.get_confirmation_offer_id(&confirmation, "identity_secret_base64").await? {
    println!("Confirmation for offer: {}", offer_id);
}

// Accept a confirmation
community.respond_to_confirmation(&confirmation, "identity_secret_base64", true).await?;

// Convenience: Accept/Deny for a specific object (trade/market ID)
community.accept_confirmation_for_object("identity_secret_base64", 123456789).await?;
community.deny_confirmation_for_object("identity_secret_base64", 123456789).await?;

Two-Factor Authentication

// Enable 2FA (initiates setup, sends SMS)
let resp = community.add_authenticator().await?;
// Or using the high-level method name from FEATURES_LIST
let resp = community.enable_two_factor().await?;

// Output detailed setup instructions and secrets
println!("{}", resp.setup_instructions());

// Finalize with SMS code (automatic shared_secret handling)
community.finalize_authenticator("12345").await?;

// Or manually with shared_secret (backwards compatible)
community.finalize_two_factor("SHARED_SECRET", "12345").await?;

// Remove/Disable 2FA
community.remove_authenticator("R12345").await?;

// Deauthorize other devices
community.deauthorize_devices().await?;

// Check Steam Guard status
let status = community.get_steam_guard_status().await?;
println!("Status: {:?}", status);

// Enable Email Steam Guard
community.enable_steam_guard_email().await?;

// Disable Steam Guard (to None)
community.disable_steam_guard_email().await?;

Profile Management

use steam_user::{PrivacySettings, ProfileSettings};

// Upload avatar from file
community.upload_avatar_from_file("path/to/avatar.png").await?;

// Upload avatar from URL
community.upload_avatar_from_url("https://example.com/avatar.jpg").await?;

// Edit profile (Name, Real Name, Summary, etc.)
// Note: This preserves existing settings for fields not provided
community.edit_profile(ProfileSettings {
    name: Some("New Persona Name".to_string()),
    real_name: Some("Real Name".to_string()),
    summary: Some("I'm a bot!".to_string()),
    country: Some("US".to_string()),
    ..Default::default()
}).await?;

// Get current privacy settings
let settings = community.get_privacy_settings().await?;

// Update privacy settings
// This preserves existing settings for fields not provided
community.set_privacy_settings(PrivacySettings {
    profile: Some(PrivacyState::Public),
    inventory: Some(PrivacyState::FriendsOnly),
    ..Default::default()
}).await?;

// Set all privacy settings to Public
community.set_all_privacy(PrivacyState::Public).await?;

// Get public profile information
// Use None to fetch the currently logged-in user's profile
let profile = community.get_profile(None).await?;
println!("Name: {}", profile.name);
println!("Level: {:?}", profile.level);

// Set a nickname for another user
community.set_nickname(target_steam_id, "My Friend").await?;

// Remove a nickname
community.remove_nickname(target_steam_id).await?;

// Remove a friend
community.remove_friend(target_steam_id).await?;

// Accept a pending friend request
community.accept_friend_request(requester_steam_id).await?;

// Ignore a pending friend request
community.ignore_friend_request(requester_steam_id).await?;

// Get simple friend list (SteamID map with relationship status)
let friend_map = community.get_friends_list().await?;

// Get detailed friend list (scraped)
let friends = community.get_friends_details().await?;
for friend in friends {
    println!("Friend: {} ({})", friend.username, friend.steam_id);
}

// Block a user
community.block_user(target_steam_id).await?;

// Unblock a user
community.unblock_user(target_steam_id).await?;

// Follow a user
community.follow_user(target_steam_id).await?;

// Unfollow a user
community.unfollow_user(target_steam_id).await?;

// Invite a user to a group
community.invite_user_to_group(target_steam_id, group_id).await?;

// Invite multiple users to a group
community.invite_users_to_group(&[id1, id2], group_id).await?;

// Accept a group invitation
community.accept_group_invite(group_id).await?;

// Ignore/decline a group invitation
community.ignore_group_invite(group_id).await?;

// Search for users by name
let results = community.search_users("SteamUser", 1).await?;
for player in results.players {
    println!("Found: {} ({})", player.name, player.steam_id);
}

// Create an instant invite link
let invite_link = community.create_instant_invite().await?;
println!("Invite link: {}", invite_link);

// Get current active invite tokens
let tokens = community.get_current_quick_invite_tokens().await?;
for token in tokens.tokens {
    println!("Token: {}", token.invite_token);
}

// --- Profile Comments ---

// Post a comment
if let Some(comment) = community.post_comment(target_steam_id, "Hello from Rust!").await? {
    println!("Posted comment: {}", comment.id);
}

// Get comments from a profile
let comments = community.get_user_comments(target_steam_id).await?;
for comment in comments {
    println!("{}: {}", comment.author.name, comment.content);
}

// Delete a comment
community.delete_comment(target_steam_id, "123456789").await?;

Phone Number Management

// Get current phone status
let status = community.get_phone_number_status().await?;
println!("Phone: {}", status); // "none" or "Ends in 17"

// Add a phone number
let resp = community.add_phone_number("+84123456789").await?;

// Confirm with SMS code
community.confirm_phone_number_code("12345").await?;

// Check removal options
let removal = community.get_remove_phone_number_type().await?;

// --- Parental Control ---

// Unlock Family View
community.parental_unlock("1234").await?;

// --- Account Details & Wallet ---

// Get detailed account information
let details = community.get_account_details().await?;
println!("Account Name: {:?}", details.account_name);
println!("Wallet String: {:?}", details.wallet);
println!("Email: {:?}", details.email);

// Get precise wallet balance and currency
let wallet = community.get_steam_wallet_balance().await?;
println!("Balance: {:?}", wallet.main_balance);
println!("Currency: {:?}", wallet.currency);

// Get total amount spent on Steam
let amount_spent = community.get_amount_spent_on_steam().await?;
println!("Total Spent: {}", amount_spent);

Market Operations

// Get gem value for an item
let gem_value = community.get_gem_value(
    753,    // app_id (Steam)
    item_id
).await?;



// Get current active listings
let (listings, assets) = community.get_my_listings().await?;

// Get market history
let history = community.get_market_history(0, 50).await?;

// Sell an item
let result = community.sell_item(730, 2, asset_id, 1, 1000).await?;

// Remove a listing
community.remove_listing("123456789").await?;

// Check for market restrictions
let restrictions = community.get_market_restrictions().await?;
if !restrictions.success {
    println!("Market restricted: {:?}", restrictions.warning);
}

Inventory Management

use steamid::SteamID;

// Get the logged-in user's inventory
let inventory = community.get_inventory(753, 6).await?;

// Get another user's inventory
let steam_id = SteamID::from(76561198012345678u64);
let inventory = community.get_user_inventory_contents(steam_id, 753, 6).await?;

// Get trading inventory (JSON format)
let trading_inv = community.get_inventory_trading(730, 2).await?;

// Get a trading partner's inventory
let partner_inv = community.get_inventory_trading_partner(730, steam_id, 2).await?;

// Get inventory history
let history = community.get_inventory_history(None).await?;
for row in history.trade_history {
    println!("{}: {}", row.timestamp_str, row.description);
}

// Get full inventory history (all pages)
let full_history = community.get_full_inventory_history().await?;

// Get market price overview for an item
let price = community.get_price_overview(730, "AK-47 | Redline (Field-Tested)").await?;
if price.success {
    println!("Lowest price: {:?}", price.lowest_price);
}

Trade Management

// Get your own trade URL
if let Some(trade_url) = community.get_trade_url().await? {
    println!("Trade URL: {}", trade_url);
}

// Parse a partner's trade URL
if let Some(parsed) = community.parse_trade_url("https://steamcommunity.com/tradeoffer/new/?partner=123&token=abc") {
    println!("Parsed Trade URL: {:?}", parsed);
}

// List trade offers
let offers = community.get_trade_offer().await?;
for offer in offers.trade_offers {
    println!("Offer ID: {}", offer.tradeofferid);
}

// Send a trade offer
community.send_trade_offer(
    partner_trade_url,
    vec![TradeOfferAsset { appid: 730, contextid: "2".into(), amount: 1, assetid: "123".into() }],
    vec![],
    "Looking for skins!"
).await?;

// Accept trade offer
community.accept_trade_offer(trade_offer_id, None).await?;

// Decline trade offer
community.decline_trade_offer(trade_offer_id).await?;

Token Management

// Get all refresh tokens
let tokens = community.get_tokens().await?;

// Check if a specific token ID exists
let exists = community.check_token_exists("123456789").await?;
println!("Token exists: {}", exists);

// Revoke a specific refresh token
community.revoke_token("123456789", "IDENTITY_SECRET").await?;

// Renew access token using refresh token
community.renew_access_token().await?;

// Get auth session info from QR URL
let info = community.get_auth_session_info("https://s.team/q/123/456").await?;
println!("Session Info: {:?}", info);

// --- License & Loyalty Rewards ---

// Add a free license for a package ID
let success = community.add_free_license(12345).await?;

// Add a free sub license
let success = community.add_sub_free_license(678).await?;

// Redeem Steam Points for a reward
let result = community.redeem_points(345).await?;
if let Some(item_id) = result.communityitemid {
    println!("Redeemed item: {}", item_id);
}

Steam App Management

// Get a list of owned apps
let owned_apps = community.get_owned_apps().await?;
for app in owned_apps {
    println!("Owned: {} ({})", app.name, app.app_id);
}

// Get detailed information for specific app IDs
let details = community.get_app_detail(&[730, 440]).await?;
if let Some(csgo) = details.get(&730) {
    println!("App Name: {}", csgo.name);
    println!("Is Free: {}", csgo.is_free);
}

// Fetch CS:GO specific account stats (Rank, XP, etc.)
let stats = community.fetch_csgo_account_stats().await?;
println!("CSGO Rank: {:?}", stats.profile_rank);
println!("XP to next rank: {:?}", stats.xp_to_next_rank);

// Fetch batched loyalty reward items (Steam Points Shop)
let rewards = community.fetch_batched_loyalty_reward_items(&[730]).await?;
for batch in rewards {
    if let Some(resp) = batch.response {
        for def in resp.definitions {
            println!("Reward: {:?}", def.internal_description);
        }
    }
}

Match History (CS:GO/CS2)

use steam_user::types::match_history::FullMatchHistoryOptions;

// Get a single page of competitive match history
let history = community.get_match_history("matchhistorycompetitive", None).await?;
for m in history.matches {
    println!("Match on {} - Score: {:?}", 
        m.match_info.map.unwrap_or_default(), 
        m.scoreboard);
    for player in m.players {
        println!("  {} ({}) - K/D/A: {:?}/{:?}/{:?}", 
            player.name, player.team,
            player.kills, player.deaths, player.assists);
    }
}

// Fetch next page using continue_token
if !history.continue_token.is_empty() {
    let next_page = community.get_match_history(
        "matchhistorycompetitive", 
        Some(&history.continue_token)
    ).await?;
}

// Get full match history (all pages) with pagination limit
let options = FullMatchHistoryOptions {
    match_history_types: vec!["matchhistorycompetitive".to_string(), "matchhistorywingman".to_string()],
    max_page: Some(5),
};
let all_history = community.get_full_history_matches(options).await?;
for (match_type, matches) in all_history {
    println!("{}: {} matches found", match_type, matches.len());
}

Help Requests (Steam Support)

// List all active and past help requests
let requests = community.get_help_requests().await?;
for request in requests {
    println!("Ticket {}: {} ({})", request.id, request.title, request.status);
    
    // Get details (conversation) for a specific ticket
    let html_content = community.get_help_request_detail(&request.id).await?;
    println!("Content length: {}", html_content.len());
}

Player Reports

// Get history of players you have reported
let reports = community.get_player_reports().await?;
for report in reports {
    println!("Reported {} on {}: {}", 
        report.reported_player_name, 
        report.date, 
        report.reason
    );
}

File Uploads

use steam_user::services::file_upload::CommitFileUploadParams;

// Begin file upload
let result = community.begin_file_upload("path/to/image.png").await?;
println!("Upload URL: {}", result.url_host);

// Upload the file content to the allocated server
community.do_file_upload("path/to/image.png", &result).await?;

// Commit the upload to finalize it
let commit_resp = community.commit_file_upload(CommitFileUploadParams {
    file_name: "image.png".to_string(),
    file_sha: "random_sha_or_actual_sha".to_string(),
    file_image_width: 800,
    file_image_height: 600,
    file_type: "image/png".to_string(),
    ugcid: result.ugcid,
    timestamp: result.timestamp,
    hmac: result.hmac,
    friend_steamid: "76561198012345678".to_string(),
}).await?;

if let Some(details) = commit_resp.result.and_then(|r| r.details) {
    println!("File URL: {}", details.url);
}

Remote Client (feature remote)

RemoteSteamUser mirrors the SteamUser API but delegates all operations to a remote steam-user-api REST server via HTTP. Supports round-robin load balancing and automatic retry.

Quick Start

use steam_user::remote::RemoteSteamUser;

#[tokio::main]
async fn main() -> Result<(), steam_user::remote::RemoteSteamUserError> {
    // Single server
    let user = RemoteSteamUser::new("https://my-steam-api.example.com", &[
        "steamLoginSecure=76561198012345678||YOUR_ACCESS_TOKEN",
        "sessionid=YOUR_SESSION_ID",
    ]);

    let balance = user.get_steam_wallet_balance().await?;
    println!("Balance: {}", balance);

    Ok(())
}

Round-Robin Load Balancing

use steam_user::remote::RemoteSteamUser;

let user = RemoteSteamUser::with_urls(&[
    "https://api-1.example.com",
    "https://api-2.example.com",
    "https://api-3.example.com",
], &[
    "steamLoginSecure=76561198012345678||YOUR_ACCESS_TOKEN",
    "sessionid=YOUR_SESSION_ID",
]);

// Requests rotate across the 3 servers automatically
// If a server fails, the next one is tried (up to 5 retries by default)

Configuration

use steam_user::remote::RemoteSteamUser;

let mut user = RemoteSteamUser::new("https://api.example.com", &["..."]);

// Set max retry attempts (default: 5)
user.set_max_retries(10);

// Set optional tokens
user.set_access_token("...".to_string());
user.set_refresh_token("...".to_string());
user.set_mobile_access_token("...".to_string());
user.set_identity_secret("...".to_string());
user.set_shared_secret("...".to_string());

Local vs Remote

// Local — direct Steam web requests
let user = steam_user::SteamUser::new(&[
    "steamLoginSecure=76561198012345678||YOUR_ACCESS_TOKEN",
    "sessionid=YOUR_SESSION_ID",
])?;
let balance = user.get_steam_wallet_balance().await?;

// Remote — delegates to steam-user-api server
use steam_user::remote::RemoteSteamUser;
let user = RemoteSteamUser::new("https://my-api.example.com", &[
    "steamLoginSecure=76561198012345678||YOUR_ACCESS_TOKEN",
    "sessionid=YOUR_SESSION_ID",
]);
let balance = user.get_steam_wallet_balance().await?;

Note: SteamUser returns typed Rust structs, while RemoteSteamUser returns serde_json::Value. Both implement the SteamUserApi trait.

SteamUserApi Trait

Both SteamUser and RemoteSteamUser implement the SteamUserApi trait, providing a unified interface:

use steam_user::SteamUserApi;

async fn get_balance(client: &impl SteamUserApi) -> Result<serde_json::Value, impl std::error::Error> {
    client.get_steam_wallet_balance().await
}

Services

Module Description
services::confirmations Mobile trade/market confirmations
services::twofactor Steam Guard 2FA management
services::profile Profile and privacy settings
services::market Steam Market operations
services::inventory Inventory access
services::groups Steam group interactions
services::friends User friend requests and invites
services::comments Profile comment management
services::trade Steam trade offer operations
services::account Phone number and account management
services::tokens Refresh token management
services::license License and loyalty rewards management
services::apps Steam application and CS:GO account services
services::match_history CS:GO/CS2 match history retrieval
services::file_upload File upload workflow (begin, do, commit)
services::help Steam Support help request management
services::reports Player report history retrieval
remote::* Remote HTTP client (feature remote)

Types

Type Description
SteamUser Main client for Steam Community interactions
Session Authentication session with cookies
Confirmation Mobile confirmation data
ConfirmationType Trade, market listing, etc.
PrivacySettings Profile privacy configuration
MarketItem Market listing information
EconItem Economy/inventory item
AddPhoneNumberResponse Result of adding a phone number
RemovePhoneResult Available phone removal methods
TradeOffer Detailed trade offer data
TradeOffersResponse List of active trade offers
SteamEnumerateTokensResponse List of active refresh tokens
OwnedApp Basic owned application data
AppDetail Comprehensive Steam Store app information
CsgoAccountStats Personal CS:GO rank and activity data
MatchHistoryResponse Paginated match history with continue token
Match Single CS:GO/CS2 match with player data and scoreboard
MatchInfo Match metadata (map, time, duration, GOTV replay)
MatchPlayer Player stats in a match (K/D/A, MVP, HSP)
HelpRequest Steam Support ticket summary (ID, title, status)
PlayerReport Details of a reported player (ID, reason, timestamp)

License

This project is licensed under the MIT License.