steam-user 0.1.0

Steam User web client for Rust - HTTP-based Steam Community interactions
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
# 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


| Feature  | Default | Description |
|----------|---------|-------------|
| `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
let price = community.get_price_overview(730, "AK-47 | Redline (Field-Tested)").await?;
if price.success {
    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?;

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`](../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


| 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.