# 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
- [x] **Account Details** - Retrieve wallet balance, email, country, and security status
- [x] **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
- [x] **Steam App Management** - Get owned apps, fetch app details, and CS:GO account stats
- [x] **File Uploads** - Upload images for chat and specific use cases
- [x] **Match History** - Retrieve CS:GO/CS2 match history with player stats, scores, and GOTV replays
- [x] **Help Requests** - List active/past support tickets and view conversation details
- [x] **Player Reports** - Retrieve history of reported players
## Installation
Add to your `Cargo.toml`:
```toml
[dependencies]
steam-user = { path = "../steam-user" }
```
### Feature Flags
| `remote` | ❌ | Enables `RemoteSteamUser`, an HTTP client that delegates to a [`steam-user-api`](../steam-user-api) REST server |
```toml
# Enable the remote client
steam-user = { path = "../steam-user", features = ["remote"] }
```
## Usage
### Basic Setup
```rust
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
```rust
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
```rust
// 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
```rust
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
```rust
// 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
```rust
// 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
```rust
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
println!("Lowest price: {:?}", price.lowest_price);
}
```
### Trade Management
```rust
// 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
```rust
// 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
```rust
// 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)
```rust
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)
```rust
// 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
```rust
// 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
```rust
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?;
}
```
## Remote Client (feature `remote`)
`RemoteSteamUser` mirrors the `SteamUser` API but delegates all operations to a remote [`steam-user-api`](../steam-user-api) REST server via HTTP. Supports **round-robin load balancing** and **automatic retry**.
### Quick Start
```rust
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
```rust
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
```rust
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
```rust
// 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:
```rust
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
| `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
| `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.