├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── ENDPOINTS.md ├── LICENSE ├── README.md ├── branding ├── docs.png ├── logo.png └── logo.svg ├── examples └── player_summaries.rs ├── src ├── econ_service │ ├── get_trade_history.rs │ ├── get_trade_offer.rs │ ├── get_trade_offers.rs │ ├── get_trade_offers_summary.rs │ └── mod.rs ├── errors.rs ├── game_servers_service │ ├── get_account_list.rs │ ├── get_account_public_info.rs │ ├── get_servers_steam_ids_by_ip.rs │ ├── mod.rs │ └── query_login_token.rs ├── lib.rs ├── macros.rs ├── player_service │ ├── get_badges.rs │ ├── get_community_badge_progress.rs │ ├── get_owned_games.rs │ ├── get_recently_played_games.rs │ ├── get_steam_level.rs │ └── mod.rs ├── published_file_service │ ├── mod.rs │ └── query_files.rs ├── site_license_service │ ├── get_current_client_connections.rs │ ├── get_total_playtime.rs │ └── mod.rs ├── steam_apps │ ├── get_app_list.rs │ ├── get_servers_at_address.rs │ ├── mod.rs │ └── up_to_date_check.rs ├── steam_economy │ ├── get_asset_class_info.rs │ ├── get_asset_prices.rs │ └── mod.rs ├── steam_id.rs ├── steam_news │ ├── get_news_for_app.rs │ └── mod.rs ├── steam_remote_storage │ ├── get_collection_details.rs │ ├── get_published_file.rs │ └── mod.rs ├── steam_user │ ├── get_friend_list.rs │ ├── get_player_bans.rs │ ├── get_player_summaries.rs │ ├── get_user_group_list.rs │ ├── mod.rs │ └── resolve_vanity_url.rs ├── steam_user_auth │ ├── authenticate_user_ticket.rs │ └── mod.rs ├── steam_user_stats │ ├── get_global_achievement_percentages_for_app.rs │ ├── get_number_of_current_players.rs │ ├── get_player_achievements.rs │ ├── get_schema_for_game.rs │ ├── get_user_stats_for_game.rs │ └── mod.rs └── steam_webapi_util │ ├── get_server_info.rs │ ├── get_supported_api_list.rs │ └── mod.rs └── tests ├── common └── mod.rs ├── econ_service.rs ├── player_service.rs ├── published_file_service.rs ├── steam_apps.rs ├── steam_economy.rs ├── steam_id.rs ├── steam_news.rs ├── steam_remote_storage.rs ├── steam_user.rs ├── steam_user_auth.rs ├── steam_user_stats.rs └── steam_webapi_util.rs /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform normalization 2 | * text=auto 3 | 4 | *.rs text diff=rust 5 | *.toml text diff=toml 6 | Cargo.lock text -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | STEAM_API_KEY: ${{ secrets.STEAM_API_KEY }} 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | - name: Compile the crate 19 | run: cargo build --verbose 20 | - name: Run tests 21 | run: cargo test --verbose 22 | - name: Build the documentation 23 | run: cargo doc --verbose 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | # Visual Studio Code preferences 17 | .vscode/ 18 | 19 | # main.rs used for testing 20 | src/main.rs -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.24.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "async-stream" 22 | version = "0.3.6" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" 25 | dependencies = [ 26 | "async-stream-impl", 27 | "futures-core", 28 | "pin-project-lite", 29 | ] 30 | 31 | [[package]] 32 | name = "async-stream-impl" 33 | version = "0.3.6" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" 36 | dependencies = [ 37 | "proc-macro2", 38 | "quote", 39 | "syn", 40 | ] 41 | 42 | [[package]] 43 | name = "autocfg" 44 | version = "1.4.0" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 47 | 48 | [[package]] 49 | name = "backtrace" 50 | version = "0.3.74" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 53 | dependencies = [ 54 | "addr2line", 55 | "cfg-if", 56 | "libc", 57 | "miniz_oxide", 58 | "object", 59 | "rustc-demangle", 60 | "windows-targets", 61 | ] 62 | 63 | [[package]] 64 | name = "bitflags" 65 | version = "2.8.0" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" 68 | 69 | [[package]] 70 | name = "bytes" 71 | version = "1.9.0" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" 74 | 75 | [[package]] 76 | name = "cc" 77 | version = "1.2.10" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" 80 | dependencies = [ 81 | "shlex", 82 | ] 83 | 84 | [[package]] 85 | name = "cfg-if" 86 | version = "1.0.0" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 89 | 90 | [[package]] 91 | name = "crossbeam-deque" 92 | version = "0.8.6" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" 95 | dependencies = [ 96 | "crossbeam-epoch", 97 | "crossbeam-utils", 98 | ] 99 | 100 | [[package]] 101 | name = "crossbeam-epoch" 102 | version = "0.9.18" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 105 | dependencies = [ 106 | "crossbeam-utils", 107 | ] 108 | 109 | [[package]] 110 | name = "crossbeam-utils" 111 | version = "0.8.21" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 114 | 115 | [[package]] 116 | name = "either" 117 | version = "1.13.0" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 120 | 121 | [[package]] 122 | name = "equivalent" 123 | version = "1.0.1" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 126 | 127 | [[package]] 128 | name = "futures-core" 129 | version = "0.3.31" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 132 | 133 | [[package]] 134 | name = "getrandom" 135 | version = "0.2.15" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 138 | dependencies = [ 139 | "cfg-if", 140 | "libc", 141 | "wasi", 142 | ] 143 | 144 | [[package]] 145 | name = "gimli" 146 | version = "0.31.1" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 149 | 150 | [[package]] 151 | name = "hashbrown" 152 | version = "0.15.2" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 155 | 156 | [[package]] 157 | name = "indexmap" 158 | version = "2.7.1" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" 161 | dependencies = [ 162 | "equivalent", 163 | "hashbrown", 164 | ] 165 | 166 | [[package]] 167 | name = "itoa" 168 | version = "1.0.14" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" 171 | 172 | [[package]] 173 | name = "libc" 174 | version = "0.2.169" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" 177 | 178 | [[package]] 179 | name = "lock_api" 180 | version = "0.4.12" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 183 | dependencies = [ 184 | "autocfg", 185 | "scopeguard", 186 | ] 187 | 188 | [[package]] 189 | name = "log" 190 | version = "0.4.25" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" 193 | 194 | [[package]] 195 | name = "memchr" 196 | version = "2.7.4" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 199 | 200 | [[package]] 201 | name = "miniz_oxide" 202 | version = "0.8.3" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" 205 | dependencies = [ 206 | "adler2", 207 | ] 208 | 209 | [[package]] 210 | name = "minreq" 211 | version = "2.13.0" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "36a8e50e917e18a37d500d27d40b7bc7d127e71c0c94fb2d83f43b4afd308390" 214 | dependencies = [ 215 | "log", 216 | "once_cell", 217 | "rustls", 218 | "rustls-webpki", 219 | "serde", 220 | "serde_json", 221 | "webpki-roots", 222 | ] 223 | 224 | [[package]] 225 | name = "mio" 226 | version = "1.0.3" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 229 | dependencies = [ 230 | "libc", 231 | "wasi", 232 | "windows-sys", 233 | ] 234 | 235 | [[package]] 236 | name = "num_enum" 237 | version = "0.7.3" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" 240 | dependencies = [ 241 | "num_enum_derive", 242 | ] 243 | 244 | [[package]] 245 | name = "num_enum_derive" 246 | version = "0.7.3" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" 249 | dependencies = [ 250 | "proc-macro-crate", 251 | "proc-macro2", 252 | "quote", 253 | "syn", 254 | ] 255 | 256 | [[package]] 257 | name = "object" 258 | version = "0.36.7" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 261 | dependencies = [ 262 | "memchr", 263 | ] 264 | 265 | [[package]] 266 | name = "once_cell" 267 | version = "1.20.2" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 270 | 271 | [[package]] 272 | name = "parking_lot" 273 | version = "0.12.3" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 276 | dependencies = [ 277 | "lock_api", 278 | "parking_lot_core", 279 | ] 280 | 281 | [[package]] 282 | name = "parking_lot_core" 283 | version = "0.9.10" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 286 | dependencies = [ 287 | "cfg-if", 288 | "libc", 289 | "redox_syscall", 290 | "smallvec", 291 | "windows-targets", 292 | ] 293 | 294 | [[package]] 295 | name = "pin-project-lite" 296 | version = "0.2.16" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 299 | 300 | [[package]] 301 | name = "proc-macro-crate" 302 | version = "3.2.0" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" 305 | dependencies = [ 306 | "toml_edit", 307 | ] 308 | 309 | [[package]] 310 | name = "proc-macro2" 311 | version = "1.0.93" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" 314 | dependencies = [ 315 | "unicode-ident", 316 | ] 317 | 318 | [[package]] 319 | name = "quote" 320 | version = "1.0.38" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" 323 | dependencies = [ 324 | "proc-macro2", 325 | ] 326 | 327 | [[package]] 328 | name = "rayon" 329 | version = "1.10.0" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" 332 | dependencies = [ 333 | "either", 334 | "rayon-core", 335 | ] 336 | 337 | [[package]] 338 | name = "rayon-core" 339 | version = "1.12.1" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" 342 | dependencies = [ 343 | "crossbeam-deque", 344 | "crossbeam-utils", 345 | ] 346 | 347 | [[package]] 348 | name = "redox_syscall" 349 | version = "0.5.8" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" 352 | dependencies = [ 353 | "bitflags", 354 | ] 355 | 356 | [[package]] 357 | name = "ring" 358 | version = "0.17.13" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "70ac5d832aa16abd7d1def883a8545280c20a60f523a370aa3a9617c2b8550ee" 361 | dependencies = [ 362 | "cc", 363 | "cfg-if", 364 | "getrandom", 365 | "libc", 366 | "untrusted", 367 | "windows-sys", 368 | ] 369 | 370 | [[package]] 371 | name = "rustc-demangle" 372 | version = "0.1.24" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 375 | 376 | [[package]] 377 | name = "rustls" 378 | version = "0.21.12" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" 381 | dependencies = [ 382 | "log", 383 | "ring", 384 | "rustls-webpki", 385 | "sct", 386 | ] 387 | 388 | [[package]] 389 | name = "rustls-webpki" 390 | version = "0.101.7" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" 393 | dependencies = [ 394 | "ring", 395 | "untrusted", 396 | ] 397 | 398 | [[package]] 399 | name = "ryu" 400 | version = "1.0.19" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" 403 | 404 | [[package]] 405 | name = "scopeguard" 406 | version = "1.2.0" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 409 | 410 | [[package]] 411 | name = "sct" 412 | version = "0.7.1" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" 415 | dependencies = [ 416 | "ring", 417 | "untrusted", 418 | ] 419 | 420 | [[package]] 421 | name = "serde" 422 | version = "1.0.217" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" 425 | dependencies = [ 426 | "serde_derive", 427 | ] 428 | 429 | [[package]] 430 | name = "serde-this-or-that" 431 | version = "0.5.0" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "f90e7ee61f57b2b1a195326bf7ee88651d3d5e688f34bd5971110851764200bf" 434 | dependencies = [ 435 | "serde", 436 | ] 437 | 438 | [[package]] 439 | name = "serde_derive" 440 | version = "1.0.217" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" 443 | dependencies = [ 444 | "proc-macro2", 445 | "quote", 446 | "syn", 447 | ] 448 | 449 | [[package]] 450 | name = "serde_json" 451 | version = "1.0.137" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" 454 | dependencies = [ 455 | "itoa", 456 | "memchr", 457 | "ryu", 458 | "serde", 459 | ] 460 | 461 | [[package]] 462 | name = "shlex" 463 | version = "1.3.0" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 466 | 467 | [[package]] 468 | name = "signal-hook-registry" 469 | version = "1.4.2" 470 | source = "registry+https://github.com/rust-lang/crates.io-index" 471 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 472 | dependencies = [ 473 | "libc", 474 | ] 475 | 476 | [[package]] 477 | name = "smallvec" 478 | version = "1.13.2" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 481 | 482 | [[package]] 483 | name = "socket2" 484 | version = "0.5.8" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" 487 | dependencies = [ 488 | "libc", 489 | "windows-sys", 490 | ] 491 | 492 | [[package]] 493 | name = "steam-rs" 494 | version = "0.5.1" 495 | dependencies = [ 496 | "minreq", 497 | "num_enum", 498 | "rayon", 499 | "serde", 500 | "serde-this-or-that", 501 | "serde_json", 502 | "tokio", 503 | "tokio-test", 504 | ] 505 | 506 | [[package]] 507 | name = "syn" 508 | version = "2.0.96" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" 511 | dependencies = [ 512 | "proc-macro2", 513 | "quote", 514 | "unicode-ident", 515 | ] 516 | 517 | [[package]] 518 | name = "tokio" 519 | version = "1.43.1" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "492a604e2fd7f814268a378409e6c92b5525d747d10db9a229723f55a417958c" 522 | dependencies = [ 523 | "backtrace", 524 | "bytes", 525 | "libc", 526 | "mio", 527 | "parking_lot", 528 | "pin-project-lite", 529 | "signal-hook-registry", 530 | "socket2", 531 | "tokio-macros", 532 | "windows-sys", 533 | ] 534 | 535 | [[package]] 536 | name = "tokio-macros" 537 | version = "2.5.0" 538 | source = "registry+https://github.com/rust-lang/crates.io-index" 539 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 540 | dependencies = [ 541 | "proc-macro2", 542 | "quote", 543 | "syn", 544 | ] 545 | 546 | [[package]] 547 | name = "tokio-stream" 548 | version = "0.1.17" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" 551 | dependencies = [ 552 | "futures-core", 553 | "pin-project-lite", 554 | "tokio", 555 | ] 556 | 557 | [[package]] 558 | name = "tokio-test" 559 | version = "0.4.4" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" 562 | dependencies = [ 563 | "async-stream", 564 | "bytes", 565 | "futures-core", 566 | "tokio", 567 | "tokio-stream", 568 | ] 569 | 570 | [[package]] 571 | name = "toml_datetime" 572 | version = "0.6.8" 573 | source = "registry+https://github.com/rust-lang/crates.io-index" 574 | checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" 575 | 576 | [[package]] 577 | name = "toml_edit" 578 | version = "0.22.22" 579 | source = "registry+https://github.com/rust-lang/crates.io-index" 580 | checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" 581 | dependencies = [ 582 | "indexmap", 583 | "toml_datetime", 584 | "winnow", 585 | ] 586 | 587 | [[package]] 588 | name = "unicode-ident" 589 | version = "1.0.16" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" 592 | 593 | [[package]] 594 | name = "untrusted" 595 | version = "0.9.0" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 598 | 599 | [[package]] 600 | name = "wasi" 601 | version = "0.11.0+wasi-snapshot-preview1" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 604 | 605 | [[package]] 606 | name = "webpki-roots" 607 | version = "0.25.4" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" 610 | 611 | [[package]] 612 | name = "windows-sys" 613 | version = "0.52.0" 614 | source = "registry+https://github.com/rust-lang/crates.io-index" 615 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 616 | dependencies = [ 617 | "windows-targets", 618 | ] 619 | 620 | [[package]] 621 | name = "windows-targets" 622 | version = "0.52.6" 623 | source = "registry+https://github.com/rust-lang/crates.io-index" 624 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 625 | dependencies = [ 626 | "windows_aarch64_gnullvm", 627 | "windows_aarch64_msvc", 628 | "windows_i686_gnu", 629 | "windows_i686_gnullvm", 630 | "windows_i686_msvc", 631 | "windows_x86_64_gnu", 632 | "windows_x86_64_gnullvm", 633 | "windows_x86_64_msvc", 634 | ] 635 | 636 | [[package]] 637 | name = "windows_aarch64_gnullvm" 638 | version = "0.52.6" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 641 | 642 | [[package]] 643 | name = "windows_aarch64_msvc" 644 | version = "0.52.6" 645 | source = "registry+https://github.com/rust-lang/crates.io-index" 646 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 647 | 648 | [[package]] 649 | name = "windows_i686_gnu" 650 | version = "0.52.6" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 653 | 654 | [[package]] 655 | name = "windows_i686_gnullvm" 656 | version = "0.52.6" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 659 | 660 | [[package]] 661 | name = "windows_i686_msvc" 662 | version = "0.52.6" 663 | source = "registry+https://github.com/rust-lang/crates.io-index" 664 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 665 | 666 | [[package]] 667 | name = "windows_x86_64_gnu" 668 | version = "0.52.6" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 671 | 672 | [[package]] 673 | name = "windows_x86_64_gnullvm" 674 | version = "0.52.6" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 677 | 678 | [[package]] 679 | name = "windows_x86_64_msvc" 680 | version = "0.52.6" 681 | source = "registry+https://github.com/rust-lang/crates.io-index" 682 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 683 | 684 | [[package]] 685 | name = "winnow" 686 | version = "0.6.25" 687 | source = "registry+https://github.com/rust-lang/crates.io-index" 688 | checksum = "ad699df48212c6cc6eb4435f35500ac6fd3b9913324f938aea302022ce19d310" 689 | dependencies = [ 690 | "memchr", 691 | ] 692 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "steam-rs" 3 | version = "0.5.1" 4 | authors = ["Garrett Howard "] 5 | edition = "2021" 6 | description = "Safe Rust bindings for the Steam Web API" 7 | readme = "README.md" 8 | repository = "https://github.com/garhow/steam-rs" 9 | documentation = "https://docs.rs/steam-rs" 10 | license = "MIT" 11 | keywords = ["steam", "api"] 12 | categories = ["api-bindings"] 13 | 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | 16 | [dependencies] 17 | minreq = { version = "2.11.0", features = ["https", "json-using-serde"] } 18 | num_enum = "0.7.2" 19 | rayon = "1.8.1" 20 | serde = { version = "1.0.196", features = ["derive"] } 21 | serde-this-or-that = "0.5.0" 22 | serde_json = "1.0.113" 23 | tokio = { version = "1.43.1", features = ["full"] } 24 | 25 | [dev-dependencies] 26 | tokio-test = "0.4.3" 27 | -------------------------------------------------------------------------------- /ENDPOINTS.md: -------------------------------------------------------------------------------- 1 | # Supported API endpoints 2 | 3 | The following is a list of interfaces and endpoints that are currently supported by this crate. 4 | 5 | Interfaces that are prefixed with ✓ are fully implemented! 6 | 7 | ## ✓ IEconService 8 | - [ ] FlushAssetAppearanceCache **(requires publisher key)** 9 | - [ ] FlushContextCache **(requires publisher key)** 10 | - [ ] FlushInventoryCache **(requires publisher key)** 11 | - [x] GetTradeHistory 12 | - [x] GetTradeOffers 13 | - [x] GetTradeOffer 14 | - [x] GetTradeOffersSummary 15 | ## IGameServersService 16 | - [x] GetAccountList 17 | - [ ] CreateAccount 18 | - [ ] SetMemo 19 | - [ ] ResetLoginToken 20 | - [ ] DeleteAccount 21 | - [x] GetAccountPublicInfo 22 | - [x] QueryLoginToken 23 | - [x] GetServerSteamIDsByIP 24 | - [ ] GetServerIPsBySteamID 25 | ## ✓ IPlayerService 26 | - [x] GetOwnedGames 27 | - [x] GetRecentlyPlayedGames 28 | - [x] GetSteamLevel 29 | - [x] GetBadges 30 | - [x] GetCommunityBadgeProgress 31 | ## ✓ IPublishedFileService 32 | - [x] QueryFiles 33 | ## ✓ ISiteLicenseService 34 | - [x] GetCurrentClientConnections 35 | - [x] GetTotalPlaytime 36 | ## ✓ ISteamApps 37 | - [x] GetAppList 38 | - [x] GetServersAtAddress 39 | - [x] UpToDateCheck 40 | ## ✓ ISteamEconomy 41 | - [x] GetAssetClassInfo 42 | - [x] GetAssetPrices 43 | ## ✓ ISteamNews 44 | - [x] GetNewsForApp 45 | ## ✓ ISteamRemoteStorage 46 | - [ ] EnumerateUserSubscribedFiles **(requires publisher key)** 47 | - [x] GetCollectionDetails 48 | - [x] GetPublishedFileDetails 49 | - [ ] GetUGCFileDetails 50 | - [ ] SetUGCUsedByGC **(requires publisher key)** 51 | - [ ] SubscribePublishedFile **(requires publisher key)** 52 | - [ ] UnsubscribePublishedFile **(requires publisher key)** 53 | ## ISteamUser 54 | - [ ] CheckAppOwnership **(requires publisher key)** 55 | - [ ] GetAppPriceInfo **(requires publisher key)** 56 | - [ ] GetDeletedSteamIDs **(requires publisher key)** 57 | - [x] GetFriendList 58 | - [x] GetPlayerAchievements 59 | - [x] GetPlayerBans 60 | - [x] GetPlayerSummaries 61 | - [ ] GetPublisherAppOwnership **(requires publisher key)** 62 | - [ ] GetPublisherAppOwnershipChanges **(requires publisher key)** 63 | - [x] GetUserGroupList 64 | - [ ] GrantPackage **(requires publisher key)** 65 | - [x] ResolveVanityURL 66 | - [ ] RevokePackage **(requires publisher key)** 67 | ## ISteamUserAuth 68 | - [ ] AuthenticateUser 69 | - [x] AuthenticateUserTicket 70 | ## ISteamUserStats 71 | - [x] GetGlobalAchievementPercentagesForApp 72 | - [ ] GetGlobalStatsForGame 73 | - [x] GetNumberOfCurrentPlayers 74 | - [x] GetPlayerAchievements 75 | - [x] GetSchemaForGame 76 | - [x] GetUserStatsForGame 77 | ## ✓ ISteamWebAPIUtil 78 | - [x] GetServerInfo 79 | - [x] GetSupportedAPIList 80 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Garrett Howard 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # steam-rs 2 | 3 | [![Crate at crates.io](https://img.shields.io/crates/v/steam-rs.svg)](https://crates.io/crates/steam-rs) 4 | [![Documentation at docs.rs](https://docs.rs/steam-rs/badge.svg)](https://docs.rs/steam-rs) 5 | [![MIT licensed](https://img.shields.io/crates/l/steam-rs.svg)](./LICENSE) 6 | [![CI](https://github.com/garhow/steam-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/garhow/steam-rs/actions/workflows/ci.yml) 7 | 8 | Provides safe and convenient Rust bindings for the Steam Web API. 9 | 10 | > [!IMPORTANT] 11 | > As this project is still a work-in-progress, not all API endpoints are currently supported. Most unsupported endpoints require a Steamworks Publisher API key, something that none of the developers of this project currently have. For a list of all supported API interfaces and endpoints, please see [ENDPOINTS.md](./ENDPOINTS.md). 12 | 13 | > [!NOTE] 14 | > This project is in early stages of development, so bug reports, suggestions, and pull requests are highly appreciated! 15 | 16 | ## Usage 17 | 18 | ```rust 19 | use std::env; 20 | use steam_rs::{Steam, SteamId}; 21 | 22 | #[tokio::main] 23 | async fn main() { 24 | // Get the Steam API Key as an environment variable 25 | let steam_api_key = env::var("STEAM_API_KEY").expect("Missing an API key"); 26 | 27 | // Initialize the Steam API client 28 | let steam = Steam::new(steam_api_key); 29 | 30 | // Request the recently played games of SteamID `76561197960434622` 31 | let steam_id = SteamId::new(76561197960434622); 32 | let recently_played_games = steam.get_recently_played_games(steam_id, None).await.unwrap(); 33 | 34 | // Print the total count of the user's recently played games 35 | println!("{}", recently_played_games.total_count); 36 | } 37 | ``` 38 | 39 | ## Acknowledgements 40 | 41 | The following resources were used in the developement in this crate: 42 | - [Better Steam Web API Documentation](https://steamwebapi.azurewebsites.net/) 43 | - [Steam Web API - Valve Developer Community](https://developer.valvesoftware.com/wiki/Steam_Web_API) 44 | - [Steamworks Web API Reference](https://partner.steamgames.com/doc/webapi) 45 | - [WebAPI - Official TF2 Wiki](https://wiki.teamfortress.com/wiki/WebAPI) 46 | - [xpaw's Steam Web API Documentation](https://steamapi.xpaw.me/) 47 | 48 | Special thanks to [@marcohoovy](https://github.com/marcohoovy/) for developing the custom macros and error types used in this project. 49 | -------------------------------------------------------------------------------- /branding/docs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garhow/steam-rs/2661fe13ae22b60fee9b6d748fba2a0ec075e931/branding/docs.png -------------------------------------------------------------------------------- /branding/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/garhow/steam-rs/2661fe13ae22b60fee9b6d748fba2a0ec075e931/branding/logo.png -------------------------------------------------------------------------------- /branding/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 241 | -------------------------------------------------------------------------------- /examples/player_summaries.rs: -------------------------------------------------------------------------------- 1 | use steam_rs::{steam_id::SteamId, Steam}; 2 | 3 | #[tokio::main] 4 | async fn main() { 5 | // Get the Steam API Key as an environment variable. 6 | let steam_api_key = &std::env::var("STEAM_API_KEY").expect("Missing an API key"); 7 | 8 | // Initialize the Steam API client. 9 | let steam = Steam::new(steam_api_key); 10 | 11 | // Request the player summaries of SteamIDs `76561198136162943` and `76561197960435530`. 12 | let steam_ids = vec![ 13 | SteamId::new(76561198136162943), // Garrett Howard 14 | SteamId(76561197960435530), // Robin Walker 15 | ]; 16 | 17 | let player_summaries = steam.get_player_summaries(steam_ids).await.unwrap(); 18 | 19 | // Print the recieved information about the players. 20 | for player in player_summaries { 21 | println!( 22 | "{:?}'s SteamID is {:?}", 23 | player.persona_name, player.steam_id 24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/econ_service/get_trade_history.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | use serde::Serialize; 3 | use serde_json::Value; 4 | 5 | use crate::errors::ErrorHandle; 6 | use crate::{ 7 | errors::EconServiceError, 8 | macros::{do_http, gen_args}, 9 | Steam, 10 | }; 11 | 12 | const END_POINT: &str = "https://api.steampowered.com/IEconService/GetTradeHistory/v1/?"; 13 | 14 | #[derive(Debug, Clone, Serialize, Deserialize)] 15 | pub struct TradeHistory { 16 | pub total_trades: u32, 17 | pub more: bool, 18 | pub trades: Vec, 19 | } 20 | 21 | #[derive(Debug, Clone, Deserialize, Serialize)] 22 | pub struct Trade { 23 | pub tradeid: String, 24 | pub steamid_other: String, 25 | pub time_init: u32, 26 | pub status: u32, 27 | pub assets_given: Option>, 28 | pub assets_recieved: Option>, 29 | } 30 | 31 | #[derive(Debug, Clone, Deserialize, Serialize)] 32 | pub struct Asset { 33 | pub appid: u32, 34 | pub contextid: String, 35 | pub assetid: String, 36 | pub amount: String, 37 | pub classid: String, 38 | pub instanceid: String, 39 | pub new_assetid: String, 40 | pub new_contextid: String, 41 | } 42 | 43 | #[derive(Debug, Deserialize, Serialize)] 44 | struct Wrapper { 45 | response: TradeHistory, 46 | } 47 | 48 | impl Steam { 49 | pub async fn get_trade_history( 50 | &self, 51 | max_trades: u32, 52 | start_after_time: u32, 53 | start_after_trade_id: u64, 54 | navigating_back: bool, 55 | get_descriptions: bool, 56 | language: &str, 57 | include_failed: bool, 58 | include_total: bool, 59 | ) -> Result { 60 | let key = &self.api_key.clone(); 61 | let args = gen_args!( 62 | key, 63 | max_trades, 64 | start_after_time, 65 | start_after_trade_id, 66 | navigating_back, 67 | get_descriptions, 68 | language, 69 | include_failed, 70 | include_total 71 | ); 72 | let url = format!("{END_POINT}{args}"); 73 | let data = do_http!(url, Value, ErrorHandle, EconServiceError::GetTradeHistory); 74 | let trade_history: Wrapper = ErrorHandle!( 75 | serde_json::from_value(data.to_owned()), 76 | EconServiceError::GetTradeHistory 77 | ); 78 | Ok(trade_history.response) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/econ_service/get_trade_offer.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | use serde::Serialize; 3 | use serde_json::Value; 4 | 5 | use crate::errors::ErrorHandle; 6 | use crate::{ 7 | errors::EconServiceError, 8 | macros::{do_http, gen_args}, 9 | Steam, 10 | }; 11 | 12 | const END_POINT: &str = "https://api.steampowered.com/IEconService/GetTradeOffer/v1/?"; 13 | 14 | #[derive(Debug, Clone, Deserialize, Serialize)] 15 | pub struct TradeOffer { 16 | #[serde(rename = "tradeofferid")] 17 | pub id: String, 18 | pub accountid_other: u32, 19 | pub message: String, 20 | pub expiration_time: u32, 21 | pub trade_offer_state: u32, 22 | pub items_to_give: Option>, 23 | pub items_to_recieve: Option>, 24 | pub is_our_offer: bool, 25 | pub time_created: u32, 26 | pub time_updated: u32, 27 | pub from_real_time_trade: bool, 28 | pub escrow_end_date: u32, 29 | pub confirmation_method: u32, 30 | pub eresult: u32, 31 | } 32 | 33 | #[derive(Debug, Clone, Deserialize, Serialize)] 34 | pub struct Item { 35 | pub appid: u32, 36 | pub contextid: String, 37 | pub assetid: String, 38 | pub classid: String, 39 | pub instanceid: String, 40 | pub amount: String, 41 | pub missing: bool, 42 | pub est_usd: String, 43 | } 44 | 45 | #[derive(Debug, Clone, Deserialize, Serialize)] 46 | pub struct Offer { 47 | pub offer: TradeOffer, 48 | } 49 | 50 | #[derive(Debug, Deserialize, Serialize)] 51 | struct Wrapper { 52 | response: Offer, 53 | } 54 | 55 | impl Steam { 56 | pub async fn get_trade_offer( 57 | &self, 58 | tradeofferid: u64, 59 | language: &str, 60 | ) -> Result { 61 | let key = &self.api_key.clone(); 62 | let args = gen_args!(key, tradeofferid, language); 63 | let url = format!("{END_POINT}{args}"); 64 | let data = do_http!(url, Value, ErrorHandle, EconServiceError::GetTradeOffer); 65 | let trade_offer: Wrapper = ErrorHandle!( 66 | serde_json::from_value(data.to_owned()), 67 | EconServiceError::GetTradeOffer 68 | ); 69 | Ok(trade_offer.response.offer) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/econ_service/get_trade_offers.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | use serde::Serialize; 3 | use serde_json::Value; 4 | 5 | use crate::errors::ErrorHandle; 6 | use crate::{ 7 | errors::EconServiceError, 8 | macros::{do_http, gen_args}, 9 | Steam, 10 | }; 11 | 12 | const END_POINT: &str = "https://api.steampowered.com/IEconService/GetTradeOffers/v1/?"; 13 | 14 | #[derive(Debug, Clone, Serialize, Deserialize)] 15 | pub struct TradeOffer { 16 | pub next_cursor: u32, 17 | pub trade_offers_received: Option>, 18 | } 19 | 20 | #[derive(Debug, Clone, Serialize, Deserialize)] 21 | pub struct OfferReceived { 22 | pub tradeofferid: String, 23 | pub accountid_other: u32, 24 | pub message: String, 25 | pub expiration_time: u32, 26 | pub trade_offer_state: u32, 27 | pub items_to_give: Vec, 28 | } 29 | 30 | #[derive(Debug, Clone, Serialize, Deserialize)] 31 | pub struct Item { 32 | pub appid: u32, 33 | pub contextid: String, 34 | pub assetid: String, 35 | pub classid: String, 36 | pub instanceid: String, 37 | pub amount: String, 38 | pub missing: bool, 39 | } 40 | 41 | #[derive(Debug, Deserialize, Serialize)] 42 | struct Wrapper { 43 | response: TradeOffer, 44 | } 45 | 46 | impl Steam { 47 | pub async fn get_trade_offers( 48 | &self, 49 | get_sent_offers: bool, 50 | get_received_offers: bool, 51 | get_descriptions: bool, 52 | language: &str, 53 | active_only: bool, 54 | historical_only: bool, 55 | time_historical_cutoff: u32, 56 | ) -> Result { 57 | let key = &self.api_key.clone(); 58 | let args = gen_args!( 59 | key, 60 | get_sent_offers, 61 | get_received_offers, 62 | get_descriptions, 63 | language, 64 | active_only, 65 | historical_only, 66 | time_historical_cutoff 67 | ); 68 | let url = format!("{END_POINT}{args}"); 69 | let data: Value = do_http!(url, Value, ErrorHandle, EconServiceError::GetTradeOffers); 70 | let trade_offer: Wrapper = ErrorHandle!( 71 | serde_json::from_value(data.to_owned()), 72 | EconServiceError::GetTradeOffers 73 | ); 74 | Ok(trade_offer.response) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/econ_service/get_trade_offers_summary.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | use serde::Serialize; 3 | use serde_json::Value; 4 | 5 | use crate::errors::ErrorHandle; 6 | use crate::{ 7 | errors::EconServiceError, 8 | macros::{do_http, gen_args}, 9 | Steam, 10 | }; 11 | 12 | const END_POINT: &str = "https://api.steampowered.com/IEconService/GetTradeOffersSummary/v1/?"; 13 | 14 | //NOT FINISHED BRUH, DONT RUN OR COMPUTER BUSTS 15 | #[derive(Debug, Clone, Serialize, Deserialize)] 16 | pub struct TradeOffersSummary { 17 | pub pending_recieved_count: Option, 18 | pub new_recieved_count: Option, 19 | pub updated_recieved_count: Option, 20 | pub historical_recieved_count: Option, 21 | pub pending_sent_count: Option, 22 | pub newly_recieved_count: Option, 23 | pub updated_sent_count: Option, 24 | pub historical_sent_count: Option, 25 | pub escrow_recieved_count: Option, 26 | pub escrow_sent_count: Option, 27 | } 28 | 29 | #[derive(Debug, Deserialize, Serialize)] 30 | struct Wrapper { 31 | response: TradeOffersSummary, 32 | } 33 | 34 | impl Steam { 35 | pub async fn get_trade_offers_summary( 36 | &self, 37 | time_last_visit: u32, 38 | ) -> Result { 39 | let key = &self.api_key.clone(); 40 | let args = gen_args!(key, time_last_visit); 41 | let url = format!("{END_POINT}{args}"); 42 | let data = do_http!( 43 | url, 44 | Value, 45 | ErrorHandle, 46 | EconServiceError::GetTradeOffersSummary 47 | ); 48 | let trade_offers_summary: Wrapper = ErrorHandle!( 49 | serde_json::from_value(data.to_owned()), 50 | EconServiceError::GetTradeOffersSummary 51 | ); 52 | Ok(trade_offers_summary.response) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/econ_service/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Implements the `IEconService` interface 2 | //! 3 | //! Additional Steam Economy methods that provide access to Steam Trading. 4 | //! 5 | //! **Note:** This implementation is incomplete! The following endpoints are currently unimplemented 6 | //! 7 | //! - FlushAssetAppearanceCache (requires publisher key) 8 | //! - FlushContextCache (requires publisher key) 9 | //! - FlushInventoryCache (requires publisher key) 10 | //! 11 | //! Endpoints that require a publisher key are not likely to be 12 | //! implemented in the near future, as they cannot be tested by developers. 13 | 14 | pub mod get_trade_history; 15 | pub mod get_trade_offer; 16 | pub mod get_trade_offers; 17 | pub mod get_trade_offers_summary; 18 | -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 1 | //! Definitions for errors 2 | 3 | use crate::macros::error; 4 | 5 | error!(PublishedFileServiceError{ 6 | QueryFiles(String) 7 | }); 8 | 9 | error!(PlayerServiceError{ 10 | GetBadges(String), 11 | GetCommunityBadgeProgress(String), 12 | GetOwnedGames(String), 13 | GetRecentlyPlayedGames(String), 14 | GetSteamLevel(String) 15 | }); 16 | 17 | error!(GameServersServiceError{ 18 | GetAccountList(String), 19 | GetAccountPublicInfo(String), 20 | QueryLoginToken(String), 21 | }); 22 | 23 | error!(SiteLicenseServiceError{ 24 | GetCurrentClientConnections(String), 25 | GetTotalPlaytime(String) 26 | }); 27 | 28 | error!(SteamAppsError{ 29 | GetAppList(String), 30 | GetServersAtAddress(String), 31 | UpToDateCheck(String) 32 | }); 33 | 34 | error!(SteamNewsError{ 35 | GetNews(String) 36 | }); 37 | 38 | error!(SteamUserError{ 39 | GetFriendList(String), 40 | GetPlayerBans(String), 41 | GetPlayerSummaries(String), 42 | GetUserGroupList(String), 43 | ResolveVanityURL(String), 44 | }); 45 | 46 | error!(SteamUserStatsError { 47 | GetGlobalAchievements(String), 48 | GetNumberOfCurrentPlayers(String), 49 | GetPlayerAchievements(String), 50 | GetSchemaForGame(String), 51 | GetUserStatsForGame(String), 52 | }); 53 | 54 | error!(EconServiceError{ 55 | GetTradeHistory(String), 56 | GetTradeOffers(String), 57 | GetTradeOffer(String), 58 | GetTradeOffersSummary(String), 59 | }); 60 | 61 | error!(SteamEconomyError{ 62 | GetAssetPrices(String), 63 | GetAssetClassInfo(String), 64 | }); 65 | 66 | error!(SteamWebAPIUtilError{ 67 | GetServerInfo(String), 68 | GetSupportedAPIList(String), 69 | }); 70 | 71 | error!(SteamRemoteStorageError{ 72 | GetCollectionDetails(String), 73 | GetPublishedFile(String) 74 | }); 75 | 76 | error!(SteamUserAuthError{ 77 | AuthenticateUserTicket(String) 78 | }); 79 | 80 | macro_rules! ErrorHandle { 81 | ($function:expr, $error:expr) => { 82 | $function.map_err(move |error| $error(error.to_string()))? 83 | }; 84 | } 85 | 86 | pub(crate) use ErrorHandle; 87 | -------------------------------------------------------------------------------- /src/game_servers_service/get_account_list.rs: -------------------------------------------------------------------------------- 1 | //! Implements the 'GetAccountList' endpoint. 2 | 3 | use serde::{Deserialize, Serialize}; 4 | use serde_json::{from_value, Value}; 5 | 6 | use crate::{ 7 | errors::{ErrorHandle, GameServersServiceError}, 8 | macros::do_http, 9 | steam_id::{de_steamid_from_str, SteamId}, 10 | Steam, BASE, 11 | }; 12 | 13 | use super::INTERFACE; 14 | 15 | const ENDPOINT: &str = "GetAccountList"; 16 | const VERSION: &str = "1"; 17 | 18 | #[derive(Debug, Deserialize, Serialize, Clone)] 19 | pub struct Server { 20 | #[serde(rename = "steamid")] 21 | #[serde(deserialize_with = "de_steamid_from_str")] 22 | pub server_steam_id: SteamId, 23 | pub appid: u32, 24 | pub login_token: String, 25 | pub memo: String, 26 | pub is_deleted: bool, 27 | pub is_expired: bool, 28 | pub rt_last_logon: u32, 29 | } 30 | 31 | #[derive(Debug, Deserialize, Serialize, Clone)] 32 | pub struct AccountsResponse { 33 | pub servers: Vec, 34 | pub is_banned: bool, 35 | pub expires: u32, 36 | #[serde(deserialize_with = "de_steamid_from_str")] 37 | pub actor: SteamId, 38 | pub last_action_time: u32, 39 | } 40 | 41 | #[derive(Debug, Deserialize, Serialize)] 42 | struct Wrapper { 43 | response: AccountsResponse, 44 | } 45 | 46 | impl Steam { 47 | /// Get the List of server accounts linked to the account the steam key is connected to 48 | pub async fn get_account_list(&self) -> Result { 49 | let query = format!("?key={}", &self.api_key); 50 | let url = format!("{}/{}/{}/v{}/{}", BASE, INTERFACE, ENDPOINT, VERSION, query); 51 | let json = do_http!( 52 | url, 53 | Value, 54 | ErrorHandle, 55 | GameServersServiceError::GetAccountList 56 | ); 57 | let wrapper: Wrapper = ErrorHandle!( 58 | from_value(json.to_owned()), 59 | GameServersServiceError::GetAccountList 60 | ); 61 | Ok(wrapper.response) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/game_servers_service/get_account_public_info.rs: -------------------------------------------------------------------------------- 1 | //! Implements the 'GetAccountPublicInfo' endpoint. 2 | 3 | use serde::{Deserialize, Serialize}; 4 | use serde_json::{from_value, Value}; 5 | 6 | use crate::{ 7 | errors::{ErrorHandle, GameServersServiceError}, 8 | macros::do_http, 9 | steam_id::{de_steamid_from_str_opt, SteamId}, 10 | Steam, BASE, 11 | }; 12 | 13 | use super::INTERFACE; 14 | 15 | const ENDPOINT: &str = "GetAccountPublicInfo"; 16 | const VERSION: &str = "1"; 17 | 18 | #[derive(Debug, Deserialize, Serialize)] 19 | pub struct Wrapper { 20 | response: PublicInfoResponse, 21 | } 22 | 23 | #[derive(Debug, Deserialize, Serialize, Clone)] 24 | pub struct PublicInfoResponse { 25 | #[serde(rename = "steamid")] 26 | #[serde(default, deserialize_with = "de_steamid_from_str_opt")] 27 | server_steam_id: Option, 28 | appid: Option, 29 | } 30 | 31 | impl Steam { 32 | // Get the public accessable info about a steam server from it's steam id. 33 | // 34 | // # Arguments 35 | // * `server_steam_id` - The server's steam id, this can be got from such functions as 'get_account_list` user accounts wont return anything 36 | pub async fn get_account_public_info( 37 | &self, 38 | server_steam_id: SteamId, 39 | ) -> Result { 40 | let query = format!("?key={}&steamid={}", &self.api_key, server_steam_id.0); 41 | let url = format!("{}/{}/{}/v{}/{}", BASE, INTERFACE, ENDPOINT, VERSION, query); 42 | let json = do_http!( 43 | url, 44 | Value, 45 | ErrorHandle, 46 | GameServersServiceError::GetAccountPublicInfo 47 | ); 48 | let wrapper: Wrapper = ErrorHandle!( 49 | from_value(json.to_owned()), 50 | GameServersServiceError::GetAccountPublicInfo 51 | ); 52 | Ok(wrapper.response) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/game_servers_service/get_servers_steam_ids_by_ip.rs: -------------------------------------------------------------------------------- 1 | //! Implements the 'GetServerSteamIDsByIP' endpoint. 2 | //! 3 | //! This endpoint is poorly explained in the steam docs. 4 | //! The server that is sent through the "server_ips" argument really refers to 5 | //! severs linked to the key (set up with `Steam::new(key)`) basically only linked 6 | //! servers can be polled, if you have these servers they should be also viewable 7 | //! through the function `get_account_list` as the `servers` field. 8 | 9 | // Currently I don't have access to something to run a steam server on so I can't test the returns of the endpoint 10 | // and since steam doesn't offer an example return it is a bit hard to develop this code, this should be fixable by 11 | // creating my own server which will be possible soon. 12 | -------------------------------------------------------------------------------- /src/game_servers_service/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Implements the `IGameServersService` interface 2 | //! 3 | //! Provides addtional methods for administration of Steam Game Servers 4 | //! It's worth noting that steam ids aren't just for user accounts, servers also have steam ids 5 | //! These aren't well differentiated within the docs for the steam api 6 | //! 7 | //! **Note:** This implementation is incomplete! The following endpoints are currently unimplemented 8 | //! 9 | //! - CreateAccount 10 | //! - SetMemo 11 | //! - ResetLoginToken 12 | //! - DeleteAccount 13 | //! - GetServerSteamIDsByIP 14 | //! - GetServerIPsBySteamID 15 | 16 | const INTERFACE: &str = "IGameServersService"; 17 | 18 | pub mod get_account_list; 19 | pub mod get_account_public_info; 20 | pub mod query_login_token; 21 | -------------------------------------------------------------------------------- /src/game_servers_service/query_login_token.rs: -------------------------------------------------------------------------------- 1 | //! Implements the `QueryLoginToken` endpoint 2 | 3 | use crate::steam_id::de_steamid_from_str; 4 | use serde::{Deserialize, Serialize}; 5 | use serde_json::{from_value, Value}; 6 | 7 | use crate::{ 8 | errors::{ErrorHandle, GameServersServiceError}, 9 | macros::do_http, 10 | steam_id::SteamId, 11 | Steam, BASE, 12 | }; 13 | 14 | use super::INTERFACE; 15 | 16 | const ENDPOINT: &str = "QueryLoginToken"; 17 | const VERSION: &str = "1"; 18 | 19 | #[derive(Debug, Clone, Deserialize, Serialize)] 20 | struct Wrapper { 21 | response: LoginTokenResponse, 22 | } 23 | 24 | #[derive(Debug, Clone, Deserialize, Serialize)] 25 | pub struct LoginTokenResponse { 26 | is_banned: bool, 27 | expires: u32, 28 | #[serde(rename = "steamid")] 29 | #[serde(deserialize_with = "de_steamid_from_str")] 30 | server_steam_id: SteamId, 31 | } 32 | 33 | impl Steam { 34 | // Get the information on a login token for a server 35 | // 36 | // # Arguments 37 | // * login_token : Token for a server, can be sourced from 'get_account_list' 38 | pub async fn query_login_token( 39 | &self, 40 | login_token: &str, 41 | ) -> Result { 42 | let query = format!("?key={}&login_token={}", &self.api_key, login_token); 43 | let url = format!("{}/{}/{}/v{}/{}", BASE, INTERFACE, ENDPOINT, VERSION, query); 44 | let json = do_http!( 45 | url, 46 | Value, 47 | ErrorHandle, 48 | GameServersServiceError::QueryLoginToken 49 | ); 50 | let wrapper: Wrapper = ErrorHandle!( 51 | from_value(json.to_owned()), 52 | GameServersServiceError::QueryLoginToken 53 | ); 54 | Ok(wrapper.response) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # steam-rs 2 | //! 3 | //! Safe and convenient Rust bindings for the Steam Web API. 4 | //! 5 | //! **Warning**: This crate is still a work in progress. Breaking changes and instability are to be expected. Use with caution—**this is not production-ready**. 6 | //! 7 | //! The core of this crate is the [`Steam`] struct, which interacts with the Steam Web API. It typically[^1] needs to be initialized with a valid Steam API key. 8 | //! 9 | //! ``` 10 | //! use steam_rs::Steam; 11 | //! 12 | //! // Retrieve the Steam API key from an environment variable. 13 | //! let steam_api_key = &std::env::var("STEAM_API_KEY").expect("Missing an API key"); 14 | //! 15 | //! // Initialize the Steam API client. 16 | //! let steam = Steam::new(steam_api_key); 17 | //! ``` 18 | //! 19 | //! Another key component of this crate is the [`SteamId`](`steam_id::SteamId`) struct. It represents a Steam user ID[^2], which is often used when querying user data. 20 | //! 21 | //! ## Example 22 | //! 23 | //! Here is an example, where the [`Steam`] client requests data about two users using the [`.get_player_summaries(steam_ids)`](`Steam::get_player_summaries()`) method: 24 | //! 25 | //! ``` 26 | //! use steam_rs::{steam_id::SteamId, Steam}; 27 | //! 28 | //! #[tokio::main] 29 | //! async fn main() { 30 | //! // Get the Steam API Key as an environment variable. 31 | //! let steam_api_key = &std::env::var("STEAM_API_KEY").expect("Missing an API key"); 32 | //! 33 | //! // Initialize the Steam API client. 34 | //! let steam = Steam::new(steam_api_key); 35 | //! 36 | //! // Request the player summaries of SteamIDs `76561198136162943` and `76561197960435530`. 37 | //! let steam_ids = vec![ 38 | //! SteamId::new(76561198136162943), // Garrett Howard 39 | //! SteamId(76561197960435530), // Robin Walker 40 | //! ]; 41 | //! 42 | //! let player_summaries = steam.get_player_summaries(steam_ids).await.unwrap(); 43 | //! 44 | //! // Print the recieved information about the players. 45 | //! for player in player_summaries { 46 | //! println!( 47 | //! "{:?}'s SteamID is {:?}", 48 | //! player.persona_name, player.steam_id 49 | //! ) 50 | //! } 51 | //! } 52 | //! ``` 53 | //! 54 | //! [^1]: Not all API endpoints require an API key, and in that case providing one is optional. 55 | //! [^2]: Specifically, [`SteamId`](`steam_id::SteamId`) represents a SteamID64 type, but more types, such as SteamID and SteamID3 are planned in future releases. 56 | 57 | #![doc( 58 | html_logo_url = "https://raw.githubusercontent.com/garhow/steam-rs/refs/heads/main/branding/docs.png" 59 | )] 60 | 61 | pub mod econ_service; 62 | pub mod game_servers_service; 63 | pub mod player_service; 64 | pub mod published_file_service; 65 | pub mod site_license_service; 66 | pub mod steam_apps; 67 | pub mod steam_economy; 68 | pub mod steam_id; 69 | pub mod steam_news; 70 | pub mod steam_remote_storage; 71 | pub mod steam_user; 72 | pub mod steam_user_auth; 73 | pub mod steam_user_stats; 74 | pub mod steam_webapi_util; 75 | 76 | pub mod errors; 77 | mod macros; // This remains private 78 | 79 | const BASE: &str = "https://api.steampowered.com"; 80 | 81 | pub struct Steam { 82 | api_key: String, 83 | } 84 | 85 | impl Steam { 86 | pub fn new(api_key: &str) -> Steam { 87 | Steam { 88 | api_key: api_key.to_string(), 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | macro_rules! do_http { 2 | ($url:ident, $output_type:ty, $error_handle:ident, $error:expr) => { 3 | if let Ok(response) = minreq::get($url).send() { 4 | match response.status_code { 5 | 200 => { 6 | $error_handle!(response.json::<$output_type>(), $error) 7 | } 8 | 404 => { 9 | $error_handle!(response.json::<$output_type>(), $error) 10 | } 11 | _ => { 12 | return Err($error(format!( 13 | "Expected 200 Status, got {}", 14 | response.status_code 15 | ))); 16 | } 17 | } 18 | } else { 19 | // TODO: Make this more descriptive 20 | return Err($error("HTTPS Error".to_string())); 21 | } 22 | }; 23 | 24 | // Post Support 25 | ($url:ident, $output_type:ty, $error_handle:ident, $error:expr, $body:ident) => { 26 | if let Ok(response) = minreq::post($url) 27 | .with_header("content-type", "application/x-www-form-urlencoded") 28 | .with_body($body) 29 | .send() 30 | { 31 | match response.status_code { 32 | 200 => { 33 | $error_handle!(response.json::<$output_type>(), $error) 34 | } 35 | 36 | _ => { 37 | return Err($error(format!( 38 | "Expected 200 Status, got {}", 39 | response.status_code 40 | ))); 41 | } 42 | } 43 | } else { 44 | // TODO: Make this more descriptive 45 | return Err($error("HTTPS Error".to_string())); 46 | } 47 | }; 48 | 49 | // Post support with debugging 50 | ($url:ident, $output_type:ty, $error_handle:ident, $error:expr, $json_data:ident, $debug:literal) => { 51 | if let Ok(response) = minreq::post($url) 52 | .with_header("Content-Type", "application/json") 53 | .with_body($json_data.to_owned()) 54 | .send() 55 | { 56 | match response.status_code { 57 | 200 => $error_handle!(response.text().await, $error), 58 | 59 | _ => { 60 | return Err($error(format!( 61 | "Expected 200 Status, got {}", 62 | response.status_code 63 | ))); 64 | } 65 | } 66 | } else { 67 | // TODO: Make this more descriptive 68 | return Err($error("HTTPS Error".to_string())); 69 | } 70 | }; 71 | 72 | ($url:ident, $output_type:ty) => { 73 | minreq::get($url).send()?.json::<$output_type>()? 74 | }; 75 | 76 | ($url:ident, $error_handle:ident, $error:expr) => { 77 | $error_handle!( 78 | $error_handle!(minreq::get($url).send()?, $error).json()?, 79 | $error 80 | ) 81 | }; 82 | ($url:ident, $error:expr) => { 83 | use crate::errors::ErrorHandle; 84 | ErrorHandle!( 85 | ErrorHandle!(minreq::get($url).send()?, $error).json().await, 86 | $error 87 | ) 88 | }; 89 | ($url:ident) => { 90 | minreq::get($url).send()?.as_str()? 91 | }; 92 | } 93 | 94 | pub(crate) use do_http; 95 | 96 | macro_rules! error { 97 | ($enum_name:ident { $($variant:ident($err:expr)),* $(,)? }) => { 98 | #[derive(Debug, Clone, PartialEq, Eq)] 99 | pub enum $enum_name { 100 | $($variant(String),)* 101 | } 102 | 103 | impl std::fmt::Display for $enum_name { 104 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 105 | match self { 106 | $($enum_name::$variant(err) => write!(f, "{}", err),)* 107 | } 108 | } 109 | } 110 | 111 | impl std::error::Error for $enum_name {} 112 | }; 113 | } 114 | 115 | pub(crate) use error; 116 | 117 | macro_rules! optional_argument { 118 | () => { String::new() }; 119 | 120 | ($key:ident, $rename:literal) => { 121 | match $key { 122 | Some(value) => format!("&{}={}", String::from($rename), value), 123 | None => String::new() 124 | } 125 | }; 126 | 127 | ($first:expr $(, $rest:expr)*) => { 128 | match $first { 129 | Some(value) => format!("&{}={:?}{}", stringify!($first), value,optional_argument!($($rest),*)), 130 | None => format!("{}", optional_argument!($($rest),*) ) 131 | } 132 | }; 133 | 134 | 135 | } 136 | 137 | pub(crate) use optional_argument; 138 | 139 | macro_rules! gen_args { 140 | // Base case: When there are no variables left to concatenate, return an empty string. 141 | () => { String::new() }; 142 | 143 | // Recursive case: Concatenate the first variable with the rest of the variables recursively. 144 | ($first:expr $(, $rest:expr)*) => { 145 | format!("&{}={}{}", stringify!($first), $first, gen_args!($($rest),*)) 146 | }; 147 | } 148 | 149 | pub(crate) use gen_args; 150 | -------------------------------------------------------------------------------- /src/player_service/get_badges.rs: -------------------------------------------------------------------------------- 1 | //! Implements the `GetBadges` endpoint. 2 | 3 | use serde::{Deserialize, Serialize}; 4 | use serde_json::Value; 5 | 6 | use crate::{ 7 | errors::{ErrorHandle, PlayerServiceError}, 8 | macros::do_http, 9 | steam_id::SteamId, 10 | Steam, BASE, 11 | }; 12 | 13 | use super::INTERFACE; 14 | 15 | const ENDPOINT: &str = "GetBadges"; 16 | const VERSION: &str = "1"; 17 | 18 | #[derive(Debug, Deserialize, Serialize, Clone)] 19 | pub struct Badge { 20 | pub badgeid: u32, 21 | pub level: u32, 22 | pub completion_time: u32, 23 | pub xp: u32, 24 | pub scarcity: u32, 25 | } 26 | 27 | #[derive(Debug, Deserialize, Serialize, Clone)] 28 | pub struct BadgeResponse { 29 | pub badges: Vec, 30 | pub player_xp: u32, 31 | pub player_level: u32, 32 | pub player_xp_needed_to_level_up: u32, 33 | pub player_xp_needed_current_level: u32, 34 | } 35 | 36 | #[derive(Debug, Deserialize, Serialize)] 37 | struct Wrapper { 38 | response: BadgeResponse, 39 | } 40 | 41 | impl Steam { 42 | /// Gets badges that are owned by a specific user. 43 | /// 44 | /// # Arguments 45 | /// 46 | /// * `steam_id` - The SteamID of the player we're asking about. 47 | pub async fn get_badges(&self, steam_id: SteamId) -> Result { 48 | let query = format!("?key={}&steamid={}", &self.api_key, steam_id); 49 | let url = format!("{}/{}/{}/v{}/{}", BASE, INTERFACE, ENDPOINT, VERSION, query); 50 | let json = do_http!(url, Value, ErrorHandle, PlayerServiceError::GetOwnedGames); 51 | let wrapper: Wrapper = ErrorHandle!( 52 | serde_json::from_value(json.to_owned()), 53 | PlayerServiceError::GetBadges 54 | ); 55 | Ok(wrapper.response) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/player_service/get_community_badge_progress.rs: -------------------------------------------------------------------------------- 1 | //! Implements the `GetCommunityBadgeProgress` endpoint. 2 | 3 | use serde::{Deserialize, Serialize}; 4 | use serde_json::Value; 5 | 6 | use crate::{ 7 | errors::{ErrorHandle, PlayerServiceError}, 8 | macros::{do_http, optional_argument}, 9 | steam_id::SteamId, 10 | Steam, BASE, 11 | }; 12 | 13 | use super::INTERFACE; 14 | 15 | const ENDPOINT: &str = "GetCommunityBadgeProgress"; 16 | const VERSION: &str = "1"; 17 | 18 | // https://wiki.teamfortress.com/wiki/WebAPI/GetCommunityBadgeProgress 19 | 20 | #[derive(Debug, Deserialize, Serialize, Clone)] 21 | pub struct Quest { 22 | #[serde(rename = "questid")] 23 | pub quest_id: u32, 24 | pub completed: bool, 25 | } 26 | 27 | #[derive(Debug, Deserialize, Serialize, Clone)] 28 | pub struct CommunityBadgeProgress { 29 | pub quests: Option>, 30 | } 31 | 32 | #[derive(Debug, Deserialize, Serialize)] 33 | struct Wrapper { 34 | response: CommunityBadgeProgress, 35 | } 36 | 37 | impl Steam { 38 | /// Gets all the quests needed to get the specified badge, and which are completed. 39 | /// 40 | /// # Arguments 41 | /// 42 | /// * `steam_id` - The SteamID of the player we're asking about. 43 | /// * `badge_id` - The badge we're asking about. 44 | pub async fn get_community_badge_progress( 45 | &self, 46 | steam_id: SteamId, 47 | badge_id: Option, 48 | ) -> Result { 49 | let query = format!( 50 | "?key={}&steamid={}{}", 51 | &self.api_key, 52 | steam_id, 53 | optional_argument!(badge_id, "badgeid") 54 | ); 55 | let url = format!("{}/{}/{}/v{}/{}", BASE, INTERFACE, ENDPOINT, VERSION, query); 56 | let json = do_http!( 57 | url, 58 | Value, 59 | ErrorHandle, 60 | PlayerServiceError::GetCommunityBadgeProgress 61 | ); 62 | let community_badge_progress: Wrapper = ErrorHandle!( 63 | serde_json::from_value(json.to_owned()), 64 | PlayerServiceError::GetCommunityBadgeProgress 65 | ); 66 | Ok(community_badge_progress.response) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/player_service/get_owned_games.rs: -------------------------------------------------------------------------------- 1 | //! Implements the `GetOwnedGames` endpoint. 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use crate::{ 6 | errors::{ErrorHandle, PlayerServiceError}, 7 | macros::{do_http, gen_args, optional_argument}, 8 | steam_id::SteamId, 9 | Steam, BASE, 10 | }; 11 | 12 | use super::INTERFACE; 13 | 14 | const ENDPOINT: &str = "GetOwnedGames"; 15 | const VERSION: &str = "1"; 16 | 17 | #[derive(Debug, Clone, Deserialize, Serialize)] 18 | struct Wrapper { 19 | response: OwnedGames, 20 | } 21 | 22 | #[derive(Debug, Clone, Deserialize, Serialize)] 23 | pub struct OwnedGames { 24 | pub game_count: u64, 25 | pub games: Vec, 26 | } 27 | 28 | #[derive(Debug, Clone, Deserialize, Serialize)] 29 | pub struct Game { 30 | pub appid: u32, 31 | pub name: Option, 32 | pub playtime_forever: u64, 33 | pub img_icon_url: Option, 34 | pub capsule_filename: Option, 35 | pub has_workshop: Option, 36 | pub has_market: Option, 37 | pub has_dlc: Option, 38 | pub rtime_last_played: Option, 39 | } 40 | 41 | impl Steam { 42 | /// Return a list of games owned by the player. 43 | /// 44 | /// # Arguments 45 | /// 46 | /// * `steamid` - The SteamID of the player we're asking about. 47 | /// * `include_appinfo` - True if we want additional details (name, icon) about each game. 48 | /// * `include_played_free_games` - Free games are excluded by default. If this is set, free games the user has played will be returned. 49 | /// * `appids_filter` - If set, restricts result set to the passed in apps. 50 | /// * `include_free_sub` - Some games are in the free sub, which are excluded by default. 51 | /// * `skip_unvetted_apps` - If set, skip unvetted store apps. 52 | /// * `language` - Will return data in this language (english, french, etc.). 53 | /// * `include_extended_appinfo` - True if we want even more details (capsule, sortas, and capabilities) about each game. include_appinfo must also be true. 54 | pub async fn get_owned_games( 55 | // TODO: Extensive testing for each argument 56 | &self, 57 | steamid: SteamId, 58 | include_appinfo: bool, 59 | include_played_free_games: bool, 60 | appids_filter: u32, // TODO: should this be an option? 61 | include_free_sub: bool, 62 | skip_unvetted_apps: Option, 63 | language: &str, 64 | include_extended_appinfo: bool, 65 | ) -> Result { 66 | let key = &self.api_key.clone(); 67 | let steamid = steamid.into_u64(); 68 | let args = gen_args!( 69 | key, 70 | steamid, 71 | include_appinfo, 72 | include_played_free_games, 73 | appids_filter, 74 | include_free_sub, 75 | language, 76 | include_extended_appinfo 77 | ) + &optional_argument!(skip_unvetted_apps); 78 | let url = format!("{BASE}/{INTERFACE}/{ENDPOINT}/v{VERSION}/?{args}"); 79 | let wrapper = do_http!(url, Wrapper, ErrorHandle, PlayerServiceError::GetOwnedGames); 80 | Ok(wrapper.response) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/player_service/get_recently_played_games.rs: -------------------------------------------------------------------------------- 1 | //! Implements the `GetRecentlyPlayedGames` endpoint. 2 | 3 | use serde::{Deserialize, Serialize}; 4 | use serde_json::Value; 5 | 6 | use crate::{ 7 | errors::{ErrorHandle, PlayerServiceError}, 8 | macros::{do_http, optional_argument}, 9 | steam_id::SteamId, 10 | Steam, BASE, 11 | }; 12 | 13 | use super::INTERFACE; 14 | 15 | const ENDPOINT: &str = "GetRecentlyPlayedGames"; 16 | const VERSION: &str = "0001"; 17 | 18 | #[derive(Debug, Deserialize, Serialize, Clone)] 19 | pub struct Game { 20 | pub appid: u32, 21 | pub name: Option, 22 | pub playtime_2weeks: Option, 23 | pub playtime_forever: u32, 24 | pub img_icon_url: Option, 25 | pub img_logo_url: Option, 26 | } 27 | 28 | #[derive(Debug, Deserialize, Serialize, Clone)] 29 | pub struct RecentlyPlayedGames { 30 | pub total_count: u32, 31 | pub games: Vec, 32 | } 33 | 34 | #[derive(Debug, Deserialize, Serialize)] 35 | struct Response { 36 | response: RecentlyPlayedGames, 37 | } 38 | 39 | impl Steam { 40 | /// Gets information about a player's recently played games 41 | /// 42 | /// # Arguments 43 | /// 44 | /// * `steam_id` - The SteamID of the player we're asking about. 45 | /// * `count` - The number of games to return. 46 | pub async fn get_recently_played_games( 47 | &self, 48 | steam_id: SteamId, 49 | count: Option, 50 | ) -> Result { 51 | let query = format!( 52 | "?key={}&steamid={}{}", 53 | &self.api_key, 54 | steam_id, 55 | optional_argument!(count) 56 | ); 57 | let url = format!("{}/{}/{}/v{}/{}", BASE, INTERFACE, ENDPOINT, VERSION, query); 58 | let json = do_http!(url, Value, ErrorHandle, PlayerServiceError::GetOwnedGames); 59 | let recently_played_games: Response = ErrorHandle!( 60 | serde_json::from_value(json.to_owned()), 61 | PlayerServiceError::GetRecentlyPlayedGames 62 | ); 63 | Ok(recently_played_games.response) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/player_service/get_steam_level.rs: -------------------------------------------------------------------------------- 1 | //! Implements the `GetSteamLevel` endpoint. 2 | 3 | use serde::{Deserialize, Serialize}; 4 | use serde_json::Value; 5 | 6 | use crate::{ 7 | errors::{ErrorHandle, PlayerServiceError}, 8 | macros::do_http, 9 | steam_id::SteamId, 10 | Steam, BASE, 11 | }; 12 | 13 | use super::INTERFACE; 14 | 15 | const ENDPOINT: &str = "GetSteamLevel"; 16 | const VERSION: &str = "1"; 17 | 18 | #[derive(Debug, Deserialize, Serialize)] 19 | struct Response { 20 | pub player_level: u16, 21 | } 22 | 23 | #[derive(Debug, Deserialize, Serialize)] 24 | struct Wrapper { 25 | response: Response, 26 | } 27 | 28 | impl Steam { 29 | /// Returns the Steam Level of a user. 30 | /// 31 | /// # Arguments 32 | /// 33 | /// * `steam_id` - The SteamID of the player we're asking about. 34 | pub async fn get_steam_level(&self, steam_id: SteamId) -> Result { 35 | let query = format!("?key={}&steamid={}", &self.api_key, steam_id); 36 | let url = format!("{}/{}/{}/v{}/{}", BASE, INTERFACE, ENDPOINT, VERSION, query); 37 | let json = do_http!(url, Value, ErrorHandle, PlayerServiceError::GetOwnedGames); 38 | let player_level: Wrapper = ErrorHandle!( 39 | serde_json::from_value(json.to_owned()), 40 | PlayerServiceError::GetOwnedGames 41 | ); 42 | Ok(player_level.response.player_level) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/player_service/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Implements the `IPlayerService` interface 2 | //! 3 | //! Provides additional methods for interacting with Steam Users. 4 | 5 | const INTERFACE: &str = "IPlayerService"; 6 | 7 | pub mod get_badges; 8 | pub mod get_community_badge_progress; 9 | pub mod get_owned_games; 10 | pub mod get_recently_played_games; 11 | pub mod get_steam_level; 12 | -------------------------------------------------------------------------------- /src/published_file_service/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Implements the `IPublishedFileService` interface 2 | //! 3 | //! **Note:** This implementation is incomplete! 4 | //! The following endpoints are currently unimplemented: 5 | //! 6 | //! - CanSubscribe (undocumented) 7 | //! - Delete (undocumented) 8 | //! - GetDetails 9 | //! - GetSubSectionData 10 | //! - GetUserFileCount 11 | //! - GetUserFiles 12 | //! - GetUserVoteSummary 13 | //! - Publish (undocumented) 14 | //! - RefreshVotingQueue (requires publisher key) 15 | //! - SetDeveloperMetadata (requires publisher key) 16 | //! - Subscribe (requires publisher key) 17 | //! - Unsubscribe (requires publisher key) 18 | //! - Update (requires publisher key) 19 | //! - UpdateAppUGCBan (requires publisher key) 20 | //! - UpdateBanStatus (requires publisher key) 21 | //! - UpdateIncompatibleStatus (requires publisher key) 22 | //! - UpdateKeyValueTags (requires publisher key) 23 | //! - UpdateTags (requires publisher key) 24 | //! - Vote (requires publisher key) 25 | //! 26 | //! Endpoints that require a publisher key are not likely to be 27 | //! implemented in the near future, as they cannot be tested by developers. 28 | 29 | pub mod query_files; 30 | 31 | const INTERFACE: &str = "IPublishedFileService"; 32 | -------------------------------------------------------------------------------- /src/published_file_service/query_files.rs: -------------------------------------------------------------------------------- 1 | //! Implements the `QueryFiles` endpoint 2 | 3 | use core::fmt; 4 | 5 | use serde::{Deserialize, Serialize}; 6 | 7 | use crate::{ 8 | errors::{ErrorHandle, PublishedFileServiceError}, 9 | macros::{do_http, optional_argument}, 10 | steam_id::{de_steamid_from_str, SteamId}, 11 | Steam, BASE, 12 | }; 13 | 14 | use super::INTERFACE; 15 | 16 | const ENDPOINT: &str = "QueryFiles"; 17 | const VERSION: &str = "1"; 18 | 19 | /// Represents the query types used when querying published files on Steam Workshop. 20 | #[derive(Debug)] 21 | pub enum PublishedFileQueryType { 22 | /// Ranked by vote. 23 | RankedByVote, 24 | /// Ranked by publication date. 25 | RankedByPublicationDate, 26 | /// Accepted for the game, ranked by acceptance date. 27 | AcceptedForGameRankedByAcceptanceDate, 28 | /// Ranked by trend. 29 | RankedByTrend, 30 | /// Favorited by friends, ranked by publication date. 31 | FavoritedByFriendsRankedByPublicationDate, 32 | /// Created by friends, ranked by publication date. 33 | CreatedByFriendsRankedByPublicationDate, 34 | /// Ranked by number of times reported. 35 | RankedByNumTimesReported, 36 | /// Created by followed users, ranked by publication date. 37 | CreatedByFollowedUsersRankedByPublicationDate, 38 | /// Not yet rated. 39 | NotYetRated, 40 | /// Ranked by total unique subscriptions. 41 | RankedByTotalUniqueSubscriptions, 42 | /// Ranked by total votes ascending. 43 | RankedByTotalVotesAsc, 44 | /// Ranked by votes up. 45 | RankedByVotesUp, 46 | /// Ranked by text search. 47 | RankedByTextSearch, 48 | /// Ranked by playtime trend. 49 | RankedByPlaytimeTrend, 50 | /// Ranked by total playtime. 51 | RankedByTotalPlaytime, 52 | /// Ranked by average playtime trend. 53 | RankedByAveragePlaytimeTrend, 54 | /// Ranked by lifetime average playtime. 55 | RankedByLifetimeAveragePlaytime, 56 | /// Ranked by playtime sessions trend. 57 | RankedByPlaytimeSessionsTrend, 58 | /// Ranked by lifetime playtime sessions. 59 | RankedByLifetimePlaytimeSessions, 60 | /// Ranked by inappropriate content rating. 61 | RankedByInappropriateContentRating, 62 | /// Ranked by ban content check. 63 | RankedByBanContentCheck, 64 | /// Ranked by last updated date. 65 | RankedByLastUpdatedDate, 66 | } 67 | 68 | impl fmt::Display for PublishedFileQueryType { 69 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 70 | let value = match self { 71 | PublishedFileQueryType::RankedByVote => 0, 72 | PublishedFileQueryType::RankedByPublicationDate => 1, 73 | PublishedFileQueryType::AcceptedForGameRankedByAcceptanceDate => 2, 74 | PublishedFileQueryType::RankedByTrend => 3, 75 | PublishedFileQueryType::FavoritedByFriendsRankedByPublicationDate => 4, 76 | PublishedFileQueryType::CreatedByFriendsRankedByPublicationDate => 5, 77 | PublishedFileQueryType::RankedByNumTimesReported => 6, 78 | PublishedFileQueryType::CreatedByFollowedUsersRankedByPublicationDate => 7, 79 | PublishedFileQueryType::NotYetRated => 8, 80 | PublishedFileQueryType::RankedByTotalUniqueSubscriptions => 9, 81 | PublishedFileQueryType::RankedByTotalVotesAsc => 10, 82 | PublishedFileQueryType::RankedByVotesUp => 11, 83 | PublishedFileQueryType::RankedByTextSearch => 12, 84 | PublishedFileQueryType::RankedByPlaytimeTrend => 13, 85 | PublishedFileQueryType::RankedByTotalPlaytime => 14, 86 | PublishedFileQueryType::RankedByAveragePlaytimeTrend => 15, 87 | PublishedFileQueryType::RankedByLifetimeAveragePlaytime => 16, 88 | PublishedFileQueryType::RankedByPlaytimeSessionsTrend => 17, 89 | PublishedFileQueryType::RankedByLifetimePlaytimeSessions => 18, 90 | PublishedFileQueryType::RankedByInappropriateContentRating => 19, 91 | PublishedFileQueryType::RankedByBanContentCheck => 20, 92 | PublishedFileQueryType::RankedByLastUpdatedDate => 21, 93 | }; 94 | write!(f, "{}", value) 95 | } 96 | } 97 | 98 | /// Represents the matching file type for published file information. 99 | #[derive(Debug)] 100 | pub enum PublishedFileInfoMatchingFileType { 101 | /// Items. 102 | Items, 103 | /// A collection of Workshop items. 104 | Collections, 105 | /// Artwork. 106 | Art, 107 | /// Videos. 108 | Videos, 109 | /// Screenshots 110 | Screenshots, 111 | /// Items that can be put inside a collection. 112 | CollectionEligible, 113 | /// Unused. 114 | Games, 115 | /// Unused. 116 | Software, 117 | /// Unused. 118 | Concepts, 119 | /// Unused. 120 | GreenlightItems, 121 | /// Guides. 122 | AllGuides, 123 | /// Steam web guide. 124 | WebGuides, 125 | /// Application integrated guide. 126 | IntegratedGuides, 127 | /// Usable in-game. 128 | UsableInGame, 129 | /// Workshop merchandise meant to be voted on for the purpose of being sold 130 | Merch, 131 | /// Steam Controller bindings. 132 | ControllerBindings, 133 | /// Used internally. 134 | SteamworksAccessInvites, 135 | /// Workshop items that can be sold in-game. 136 | ItemsMtx, 137 | /// Workshop items that can be used right away by the user. 138 | ItemsReadyToUse, 139 | /// Workshop showcase. 140 | WorkshopShowcase, 141 | /// Managed completely by the game, not the user, and not shown on the web. 142 | GameManagedItems, 143 | } 144 | 145 | impl fmt::Display for PublishedFileInfoMatchingFileType { 146 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 147 | let value = match self { 148 | PublishedFileInfoMatchingFileType::Items => 0, 149 | PublishedFileInfoMatchingFileType::Collections => 1, 150 | PublishedFileInfoMatchingFileType::Art => 2, 151 | PublishedFileInfoMatchingFileType::Videos => 3, 152 | PublishedFileInfoMatchingFileType::Screenshots => 4, 153 | PublishedFileInfoMatchingFileType::CollectionEligible => 5, 154 | PublishedFileInfoMatchingFileType::Games => 6, 155 | PublishedFileInfoMatchingFileType::Software => 7, 156 | PublishedFileInfoMatchingFileType::Concepts => 8, 157 | PublishedFileInfoMatchingFileType::GreenlightItems => 9, 158 | PublishedFileInfoMatchingFileType::AllGuides => 10, 159 | PublishedFileInfoMatchingFileType::WebGuides => 11, 160 | PublishedFileInfoMatchingFileType::IntegratedGuides => 12, 161 | PublishedFileInfoMatchingFileType::UsableInGame => 13, 162 | PublishedFileInfoMatchingFileType::Merch => 14, 163 | PublishedFileInfoMatchingFileType::ControllerBindings => 15, 164 | PublishedFileInfoMatchingFileType::SteamworksAccessInvites => 16, 165 | PublishedFileInfoMatchingFileType::ItemsMtx => 17, 166 | PublishedFileInfoMatchingFileType::ItemsReadyToUse => 18, 167 | PublishedFileInfoMatchingFileType::WorkshopShowcase => 19, 168 | PublishedFileInfoMatchingFileType::GameManagedItems => 20, 169 | }; 170 | write!(f, "{}", value) 171 | } 172 | } 173 | 174 | /// Represents a preview associated with a file. 175 | #[derive(Serialize, Deserialize, Debug, Clone)] 176 | pub struct Preview { 177 | /// The ID of the preview. 178 | #[serde(rename = "previewid")] 179 | pub preview_id: String, 180 | /// The sort order of the preview. 181 | #[serde(rename = "sortorder")] 182 | pub sort_order: u16, 183 | /// The URL of the preview, if available. 184 | pub url: Option, 185 | /// The size of the preview, if available. 186 | pub size: Option, 187 | /// The filename of the preview, if available. 188 | #[serde(rename = "filename")] 189 | pub file_name: Option, 190 | /// The type of the preview. 191 | pub preview_type: u8, 192 | } 193 | 194 | /// Represents a tag associated with a file. 195 | #[derive(Serialize, Deserialize, Debug, Clone)] 196 | pub struct Tag { 197 | /// The tag string. 198 | pub tag: String, 199 | /// The display name of the tag. 200 | pub display_name: String, 201 | } 202 | 203 | /// Represents voting data associated with a file. 204 | #[derive(Serialize, Deserialize, Debug, Clone)] 205 | pub struct VoteData { 206 | /// The score associated with the vote. 207 | pub score: f32, 208 | } 209 | 210 | /// Represents playtime statistics. 211 | #[derive(Serialize, Deserialize, Debug, Clone)] 212 | pub struct PlaytimeStats { 213 | /// The total playtime in seconds. 214 | pub playtime_seconds: String, 215 | /// The number of play sessions. 216 | pub num_sessions: String, 217 | } 218 | 219 | /// Represents file information retrieved from the Steam Workshop. 220 | #[derive(Serialize, Deserialize, Debug, Clone)] 221 | pub struct File { 222 | /// Result status of the file. 223 | pub result: u64, 224 | /// The published file ID. 225 | #[serde(rename = "publishedfileid")] 226 | pub published_file_id: String, 227 | /// The Steam ID of the creator. 228 | #[serde(deserialize_with = "de_steamid_from_str")] 229 | pub creator: SteamId, 230 | /// The ID of the application (game) that created the file. 231 | pub creator_appid: u32, 232 | /// The ID of the application (game) that consumes the file. 233 | pub consumer_appid: u32, 234 | /// The ID of the shortcut used to create the file. 235 | pub consumer_shortcutid: u32, 236 | /// The name of the file. 237 | #[serde(rename = "filename")] 238 | pub file_name: String, 239 | /// The size of the file. 240 | pub file_size: String, 241 | /// The size of the preview file. 242 | pub preview_file_size: String, 243 | /// The URL of the preview. 244 | pub preview_url: String, 245 | /// The URL of the file. 246 | pub url: String, 247 | /// The content file. 248 | pub hcontent_file: Option, 249 | /// The content preview. 250 | pub hcontent_preview: String, 251 | /// The title of the file. 252 | pub title: String, 253 | /// The short description of the file. 254 | pub short_description: String, 255 | /// The time the file was created. 256 | pub time_created: u32, 257 | /// The time the file was last updated. 258 | pub time_updated: u32, 259 | /// The visibility status of the file. 260 | pub visibility: u8, 261 | /// Flags associated with the file. 262 | pub flags: u32, 263 | /// Indicates if the file is from the Steam Workshop. 264 | pub workshop_file: bool, 265 | /// Indicates if the file has been accepted on the Steam Workshop. 266 | pub workshop_accepted: bool, 267 | /// Indicates if all subscribers are shown. 268 | pub show_subscribe_all: bool, 269 | /// The number of public comments. 270 | pub num_comments_public: u64, 271 | /// Indicates if the file has been banned. 272 | pub banned: bool, 273 | /// The reason for the ban. 274 | pub ban_reason: String, 275 | /// The banner of the file. 276 | pub banner: String, 277 | /// Indicates if the file can be deleted. 278 | pub can_be_deleted: bool, 279 | /// The name of the application (game). 280 | pub app_name: String, 281 | /// The file type. 282 | pub file_type: u8, 283 | /// Indicates if the user can subscribe to the file. 284 | pub can_subscribe: bool, 285 | /// The number of subscriptions. 286 | pub subscriptions: u64, 287 | /// The number of favorites. 288 | pub favorited: u64, 289 | /// The number of followers. 290 | pub followers: u64, 291 | /// The lifetime number of subscriptions. 292 | pub lifetime_subscriptions: u64, 293 | /// The lifetime number of favorites. 294 | pub lifetime_favorited: u64, 295 | /// The lifetime number of followers. 296 | pub lifetime_followers: u64, 297 | /// The lifetime playtime. 298 | pub lifetime_playtime: String, 299 | /// The lifetime playtime sessions. 300 | pub lifetime_playtime_sessions: String, 301 | /// The number of views. 302 | pub views: u64, 303 | /// The number of children. 304 | pub num_children: u32, 305 | /// The number of reports. 306 | pub num_reports: u32, 307 | /// Previews associated with the file. 308 | pub previews: Vec, 309 | /// Tags associated with the file. 310 | pub tags: Vec, 311 | /// Vote data associated with the file. 312 | pub vote_data: VoteData, 313 | /// Playtime statistics associated with the file. 314 | pub playtime_stats: PlaytimeStats, 315 | /// The language of the file. 316 | pub language: u32, 317 | /// Indicates if the file may contain inappropriate content related to sex. 318 | pub maybe_inappropriate_sex: bool, 319 | /// Indicates if the file may contain inappropriate content related to violence. 320 | pub maybe_inappropriate_violence: bool, 321 | /// The revision change number. 322 | pub revision_change_number: String, 323 | /// The revision number. 324 | pub revision: u32, 325 | /// Available revisions for the file. 326 | pub available_revisions: Vec, 327 | /// Ban text check result. 328 | pub ban_text_check_result: u32, 329 | } 330 | 331 | /// Represents published files information. 332 | #[derive(Serialize, Deserialize, Debug, Clone)] 333 | pub struct PublishedFiles { 334 | /// The total number of published files. 335 | pub total: u64, 336 | /// Details of the published files. 337 | #[serde(rename = "publishedfiledetails")] 338 | pub published_file_details: Vec, 339 | } 340 | 341 | #[derive(Serialize, Deserialize, Debug)] 342 | struct Response { 343 | response: PublishedFiles, 344 | } 345 | 346 | impl Steam { 347 | /// Performs a search query for published files. 348 | /// 349 | /// # Arguments 350 | /// 351 | /// * `query_type` - Type of the query, see [PublishedFileQueryType]. 352 | /// * `page` - Current page. Currently there is an upper limit of 1000. 353 | /// * `cursor` - Cursor to paginate through the results (set to '*' for the first request). Prefer this over using the page parameter, as it will allow you to do deep pagination. When used, the page parameter will be ignored. Use the "next_cursor" value returned in the response to set up the next query to get the next set of results. 354 | /// * `numperpage` - The number of results, per page to return. 355 | /// * `creator_app_id` - App that created the files. 356 | /// * `app_id` - App that consumes the files. 357 | /// * `required_tags` - Tags to match on. See `match_all_tags` parameter. 358 | /// * `excluded_tags` - Tags that must NOT be present on a published file to satisfy the query. 359 | /// * `match_all_tags` - If true, then items must have all the tags specified, otherwise they must have at least one of the tags. 360 | /// * `required_flags` - Required flags that must be set on any returned items. 361 | /// * `omitted_flags` - Flags that must not be set on any returned items 362 | /// * `search_text` - Text to match in the item's title or description. 363 | /// * `file_type` - 364 | /// * `child_published_file_id` - Find all items that reference the given item. 365 | /// * `days` - If `query_type` is [RankedByTrend](crate::published_file_service::query_files::PublishedFileQueryType::RankedByTrend), then this is the number of days to get votes for \[1,7\]. 366 | /// * `include_recent_votes_only` - If `query_type` is [RankedByTrend](crate::published_file_service::query_files::PublishedFileQueryType::RankedByTrend), then limit result set just to items that have votes within the day range given. 367 | /// * `cache_max_age_seconds` - Allow stale data to be returned for the specified number of seconds. 368 | /// * `language` - Language to search in and also what gets returned. Defaults to English. 369 | /// * `required_kv_tags` - Required key-value tags to match on. 370 | /// * `total_only` - If true, only return the total number of files that satisfy this query. 371 | /// * `ids_only` - If true, only return the published file ids of files that satisfy this query. 372 | /// * `return_vote_data` - Return vote data. 373 | /// * `return_tags` - Return tags in the file details. 374 | /// * `return_kv_tags` - Return key-value tags in the file details. 375 | /// * `return_previews` - Return preview image and video details in the file details. 376 | /// * `return_children` - Return child item ids in the file details. 377 | /// * `return_short_description` - Populate the short_description field instead of file_description. 378 | /// * `return_for_sale_data` - Return pricing information, if applicable. 379 | /// * `return_metadata` - Populate the metadata. 380 | /// * `return_playtime_stats` Return playtime stats for the specified number of days before today. 381 | pub async fn query_files( 382 | &self, 383 | query_type: PublishedFileQueryType, 384 | page: u32, 385 | cursor: &str, 386 | numperpage: Option, // numperpage 387 | creator_app_id: u32, 388 | app_id: u32, 389 | required_tags: &str, 390 | excluded_tags: &str, 391 | match_all_tags: Option, 392 | required_flags: &str, 393 | omitted_flags: &str, 394 | search_text: &str, 395 | file_type: PublishedFileInfoMatchingFileType, 396 | child_published_file_id: u64, 397 | days: u32, 398 | include_recent_votes_only: bool, 399 | cache_max_age_seconds: Option, 400 | language: Option, 401 | required_kv_tags: &str, // Documentation says this is type {message} ?? 402 | total_only: bool, 403 | ids_only: bool, 404 | return_vote_data: bool, 405 | return_tags: bool, 406 | return_kv_tags: bool, 407 | return_previews: bool, 408 | return_children: bool, 409 | return_short_description: bool, 410 | return_for_sale_data: bool, 411 | return_metadata: Option, 412 | return_playtime_stats: u32, 413 | ) -> Result { 414 | let query = vec![ 415 | format!("?key={}", &self.api_key), 416 | format!("&query_type={}", query_type), 417 | format!("&page={}", page), 418 | format!("&cursor={}", cursor), 419 | format!("&creator_appid={}", creator_app_id), 420 | format!("&appid={}", app_id), 421 | format!("&requiredtags={}", required_tags), 422 | format!("&excludedtags={}", excluded_tags), 423 | format!("&required_flags={}", required_flags), 424 | format!("&omitted_flags={}", omitted_flags), 425 | format!("&search_text={}", search_text), 426 | format!("&filetype={}", file_type), 427 | format!("&child_publishedfileid={}", child_published_file_id), 428 | format!("&days={}", days), 429 | format!("&include_recent_votes_only={}", include_recent_votes_only), 430 | format!("&required_kv_tags={}", required_kv_tags), 431 | format!("&totalonly={}", total_only), 432 | format!("&ids_only={}", ids_only), 433 | format!("&return_vote_data={}", return_vote_data), 434 | format!("&return_tags={}", return_tags), 435 | format!("&return_kv_tags={}", return_kv_tags), 436 | format!("&return_previews={}", return_previews), 437 | format!("&return_children={}", return_children), 438 | format!("&return_short_description={}", return_short_description), 439 | format!("&return_for_sale_data={}", return_for_sale_data), 440 | format!("&return_playtime_stats={}", return_playtime_stats), 441 | optional_argument!(numperpage), 442 | optional_argument!(match_all_tags), 443 | optional_argument!(cache_max_age_seconds), 444 | optional_argument!(language), 445 | optional_argument!(return_metadata), 446 | ]; 447 | 448 | let url = format!( 449 | "{}/{}/{}/v{}/{}", 450 | BASE, 451 | INTERFACE, 452 | ENDPOINT, 453 | VERSION, 454 | query.concat() 455 | ); 456 | 457 | let response = do_http!( 458 | url, 459 | Response, 460 | ErrorHandle, 461 | PublishedFileServiceError::QueryFiles 462 | ); 463 | 464 | Ok(response.response) 465 | } 466 | } 467 | -------------------------------------------------------------------------------- /src/site_license_service/get_current_client_connections.rs: -------------------------------------------------------------------------------- 1 | //! Implements the `GetCurrentClientConnections` endpoint 2 | 3 | use serde::{Deserialize, Serialize}; 4 | use serde_json::Value; 5 | 6 | use crate::{ 7 | errors::{ErrorHandle, SiteLicenseServiceError}, 8 | macros::do_http, 9 | Steam, BASE, 10 | }; 11 | 12 | use super::{GameName, INTERFACE}; 13 | 14 | const ENDPOINT: &str = "GetCurrentClientConnections"; 15 | const VERSION: &str = "1"; 16 | 17 | #[derive(Debug, Deserialize, Serialize, Clone)] 18 | pub struct Game { 19 | /// ID of game in the `game_names` section of the response. 20 | pub id: u32, 21 | 22 | /// Time this game session started (RFC 3339 UTC format). 23 | pub start_time: String, 24 | 25 | /// Playtime is divided up for each game by the type of license used. See the GetCurrentClientConnections section above for the list. 26 | pub license_type: String, // TODO: Define LicenseType enum as defined in https://partner.steamgames.com/doc/webapi/isitelicenseservice 27 | } 28 | 29 | #[derive(Debug, Deserialize, Serialize, Clone)] 30 | pub struct Client { 31 | /// IP address of client computer. 32 | pub ip_address: String, 33 | 34 | /// Machine name of client computer. 35 | pub hostname: String, 36 | 37 | /// Vector of active game sessions on the client computer. 38 | pub games: Vec, 39 | } 40 | 41 | #[derive(Debug, Deserialize, Serialize, Clone)] 42 | pub struct Site { 43 | /// 64-bit SiteID of the site. 44 | #[serde(rename = "siteid")] 45 | pub id: u64, 46 | 47 | /// Site name assigned to the site. 48 | pub site_name: String, 49 | 50 | /// Vector of active clients at the site. 51 | pub clients: Vec, 52 | } 53 | 54 | #[derive(Debug, Deserialize, Serialize, Clone)] 55 | pub struct CurrentClientConnections { 56 | /// A vector of site details. 57 | pub sites: Vec, 58 | 59 | /// Mapped vector of game names. 60 | pub game_names: Vec, 61 | } 62 | 63 | #[derive(Debug, Deserialize, Serialize)] 64 | struct Wrapper { 65 | response: CurrentClientConnections, 66 | } 67 | 68 | impl Steam { 69 | /// See current activity at one or more sites. 70 | /// 71 | /// # Warning 72 | /// 73 | /// This method is unsupported as it requires a Steamworks Web API publisher authentication key and we cannot test it at the moment! 74 | /// Bug reports are appreciated for unsupported methods, so please file one if you encounter errors. 75 | /// 76 | /// # Arguments 77 | /// 78 | /// * `publisher_key` - Steamworks Web API publisher authentication key 79 | /// * `site_id` - Site ID to see; zero for all sites 80 | pub async fn get_current_client_connections( 81 | publisher_key: &str, 82 | site_id: u64, 83 | ) -> Result { 84 | let url = format!( 85 | "{}/{}/{}/v{}/?key={}&siteid={}", 86 | BASE, INTERFACE, ENDPOINT, VERSION, publisher_key, site_id 87 | ); 88 | let json = do_http!( 89 | url, 90 | Value, 91 | ErrorHandle, 92 | SiteLicenseServiceError::GetCurrentClientConnections 93 | ); 94 | let wrapper: Wrapper = ErrorHandle!( 95 | serde_json::from_value(json.to_owned()), 96 | SiteLicenseServiceError::GetCurrentClientConnections 97 | ); 98 | Ok(wrapper.response) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/site_license_service/get_total_playtime.rs: -------------------------------------------------------------------------------- 1 | //! Implements the `GetTotalPlaytime` endpoint 2 | 3 | use serde::{Deserialize, Serialize}; 4 | use serde_json::Value; 5 | 6 | use crate::{ 7 | errors::{ErrorHandle, SiteLicenseServiceError}, 8 | macros::do_http, 9 | Steam, BASE, 10 | }; 11 | 12 | use super::{GameName, INTERFACE}; 13 | 14 | const ENDPOINT: &str = "GetTotalPlaytime"; 15 | const VERSION: &str = "1"; 16 | 17 | #[derive(Debug, Deserialize, Serialize, Clone)] 18 | pub struct Game { 19 | /// ID of game in the `game_names` section of the response. 20 | pub id: u32, 21 | 22 | /// Playtime is divided up for each game by the type of license used. See the `GetCurrentClientConnections` endpoint for the list. 23 | pub license_type: String, // TODO: Define LicenseType enum as defined in https://partner.steamgames.com/doc/webapi/isitelicenseservice 24 | 25 | /// Total playtime for this game and license type, in seconds, for the requested period. 26 | pub playtime_seconds: u32, 27 | } 28 | 29 | #[derive(Debug, Deserialize, Serialize, Clone)] 30 | pub struct Site { 31 | /// 64-bit SiteID of the site. 32 | #[serde(rename = "siteid")] 33 | pub id: u64, 34 | 35 | /// Site name assigned to the site. 36 | pub site_name: String, 37 | 38 | /// List of all game titles played. 39 | pub games: Vec, 40 | } 41 | 42 | #[derive(Debug, Deserialize, Serialize, Clone)] 43 | pub struct TotalPlaytime { 44 | /// A vector of site details. 45 | pub sites: Vec, 46 | 47 | /// Mapped vector of game names. 48 | pub game_names: Vec, 49 | } 50 | 51 | #[derive(Debug, Deserialize, Serialize)] 52 | struct Wrapper { 53 | response: TotalPlaytime, 54 | } 55 | 56 | impl Steam { 57 | /// Get total playtime amounts for all games over a period of time; for one or all sites. 58 | /// 59 | /// # Warning 60 | /// 61 | /// This method is unsupported as it requires a Steamworks Web API publisher authentication key and we cannot test it at the moment! 62 | /// Bug reports are appreciated for unsupported methods, so please file one if you encounter errors. 63 | /// 64 | /// # Arguments 65 | /// 66 | /// * `publisher_key` - Steamworks Web API publisher authentication key 67 | /// * `start_time` - Report activity starting on or after this time. RFC 3339 UTC format. 68 | /// * `end_time` - Report activity starting before this time. RFC 3339 UTC format. 69 | /// * `site_id` - Site ID to see; zero for all sites 70 | pub async fn get_total_playtime( 71 | publisher_key: &str, 72 | start_time: &str, 73 | end_time: &str, 74 | site_id: u64, 75 | ) -> Result { 76 | let url = format!( 77 | "{}/{}/{}/v{}/?key={}&start_time={}&end_time={}&siteid={}", 78 | BASE, INTERFACE, ENDPOINT, VERSION, publisher_key, start_time, end_time, site_id 79 | ); 80 | let json = do_http!( 81 | url, 82 | Value, 83 | ErrorHandle, 84 | SiteLicenseServiceError::GetCurrentClientConnections 85 | ); 86 | let wrapper: Wrapper = ErrorHandle!( 87 | serde_json::from_value(json.to_owned()), 88 | SiteLicenseServiceError::GetCurrentClientConnections 89 | ); 90 | Ok(wrapper.response) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/site_license_service/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Implements the `ISiteLicenseService` interface 2 | //! 3 | //! Provides access to services related to operating sites which are part of the Steam PC Cafe program. 4 | 5 | pub mod get_current_client_connections; 6 | pub mod get_total_playtime; 7 | 8 | use serde::{Deserialize, Serialize}; 9 | 10 | const INTERFACE: &str = "ISiteLicenseService"; 11 | 12 | #[derive(Debug, Deserialize, Serialize, Clone)] 13 | pub struct GameName { 14 | /// ID number of entry. 15 | pub id: u32, 16 | 17 | /// Game name. 18 | pub name: String, 19 | } 20 | -------------------------------------------------------------------------------- /src/steam_apps/get_app_list.rs: -------------------------------------------------------------------------------- 1 | //! # Implements the `GetAppList` endpoint 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use crate::{ 6 | errors::{ErrorHandle, SteamAppsError}, 7 | macros::do_http, 8 | Steam, BASE, 9 | }; 10 | 11 | use super::INTERFACE; 12 | 13 | const ENDPOINT: &str = "GetAppList"; 14 | const VERSION: &str = "2"; 15 | 16 | /// Represents a Steam app. 17 | #[derive(Deserialize, Serialize, Debug, Clone)] 18 | pub struct App { 19 | /// App ID of this application. 20 | pub appid: u32, 21 | 22 | /// The name of this application. 23 | pub name: String, 24 | } 25 | 26 | #[derive(Deserialize, Serialize, Debug, Clone)] 27 | pub struct AppList { 28 | /// The vector containing the applications. 29 | pub apps: Vec, 30 | } 31 | 32 | #[derive(Deserialize, Serialize, Debug)] 33 | struct Wrapper { 34 | applist: AppList, 35 | } 36 | 37 | impl Steam { 38 | /// Gets the complete list of public apps. 39 | pub async fn get_app_list() -> Result { 40 | let url = format!("{}/{}/{}/v{}/", BASE, INTERFACE, ENDPOINT, VERSION); 41 | let wrapper = do_http!(url, Wrapper, ErrorHandle, SteamAppsError::GetAppList); 42 | Ok(wrapper.applist) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/steam_apps/get_servers_at_address.rs: -------------------------------------------------------------------------------- 1 | //! # Implements the `GetServersAtAddress` endpoint 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use crate::{ 6 | errors::{ErrorHandle, SteamAppsError}, 7 | macros::do_http, 8 | Steam, BASE, 9 | }; 10 | 11 | use super::INTERFACE; 12 | 13 | const ENDPOINT: &str = "GetServersAtAddress"; 14 | const VERSION: &str = "1"; 15 | 16 | #[derive(Deserialize, Serialize, Debug, Clone)] 17 | pub struct Server { 18 | /// Gives the ip address, and the port number. Ex: "64.94.100.204:27015" 19 | #[serde(rename = "addr")] 20 | pub address: String, 21 | 22 | /// Gives the GMS index. Ex: 65534 23 | #[serde(rename = "gmsindex")] 24 | pub gms_index: i32, 25 | 26 | // TODO: provide documentation for this field 27 | pub steamid: String, 28 | 29 | // TODO: provide documentation for this field 30 | pub reject: Option, 31 | 32 | /// Gives the steam game appid. Ex: 730 33 | pub appid: u32, 34 | 35 | /// Tells which directory the game is from. Ex: "csgo" 36 | pub gamedir: String, 37 | 38 | /// Gives the region of the server. Ex: 1 39 | pub region: i32, 40 | 41 | /// If server is secure or not. 42 | pub secure: bool, 43 | 44 | /// If server is a lan game. 45 | pub lan: bool, 46 | 47 | /// Gives the port number for the server. Ex: 27015 48 | pub gameport: u32, 49 | 50 | /// Gives the specport. Ex: 0 51 | pub specport: u32, 52 | } 53 | 54 | #[derive(Deserialize, Serialize, Debug, Clone)] 55 | pub struct ServersResponse { 56 | /// Returns true if IP address is valid, does not mean server is functioning properly. 57 | pub success: bool, 58 | 59 | /// A list of every server from this IP address. 60 | pub servers: Option>, 61 | 62 | /// Message given if success is false. 63 | pub message: Option, 64 | } 65 | 66 | #[derive(Deserialize, Serialize, Debug)] 67 | struct Wrapper { 68 | response: ServersResponse, 69 | } 70 | 71 | impl Steam { 72 | /// Gets a list of all Steam-compatible game servers running at the specified IP address. 73 | /// 74 | /// # Arguments 75 | /// 76 | /// * `address` - IP or IP:queryport to list. 77 | pub async fn get_servers_at_address(address: &str) -> Result { 78 | let url = format!( 79 | "{}/{}/{}/v{}/?addr={}", 80 | BASE, INTERFACE, ENDPOINT, VERSION, address 81 | ); 82 | let wrapper = do_http!( 83 | url, 84 | Wrapper, 85 | ErrorHandle, 86 | SteamAppsError::GetServersAtAddress 87 | ); 88 | Ok(wrapper.response) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/steam_apps/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Implements the `ISteamApps` interface 2 | //! 3 | //! Used to access data about applications on Steam. 4 | //! 5 | //! **Note:** This implementation is incomplete! 6 | //! The following endpoints are currently unimplemented: 7 | //! 8 | //! - GetAppBetas (requires publisher key) 9 | //! - GetAppBuilds (requires publisher key) 10 | //! - GetAppDepotVersions (requires publisher key) 11 | //! - GetCheatingReports (requires publisher key) 12 | //! - GetPartnerAppListForWebAPIKey (requires publisher key) 13 | //! - GetPlayersBanned (requires publisher key) 14 | //! - GetSDRConfig 15 | //! - GetServerList (requires publisher key) 16 | //! - SetAppBuildLive (requires publisher key) 17 | //! 18 | //! Endpoints that require a publisher key are not likely to be 19 | //! implemented in the near future, as they cannot be tested by developers. 20 | 21 | pub mod get_app_list; 22 | pub mod get_servers_at_address; 23 | pub mod up_to_date_check; 24 | 25 | const INTERFACE: &str = "ISteamApps"; 26 | -------------------------------------------------------------------------------- /src/steam_apps/up_to_date_check.rs: -------------------------------------------------------------------------------- 1 | //! # Implements the `UpToDateCheck` endpoint 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use crate::{ 6 | errors::{ErrorHandle, SteamAppsError}, 7 | macros::do_http, 8 | Steam, BASE, 9 | }; 10 | 11 | use super::INTERFACE; 12 | 13 | const ENDPOINT: &str = "UpToDateCheck"; 14 | const VERSION: &str = "1"; 15 | 16 | #[derive(Deserialize, Serialize, Debug, Clone)] 17 | pub struct UpToDateResponse { 18 | /// Boolean indicating if request was successful. 19 | pub success: bool, 20 | 21 | /// Boolean indicating if the given version number is the most current version. 22 | pub up_to_date: bool, 23 | 24 | /// Boolean indicating if the given version can be listed in public changelogs. [\[debated\]](https://wiki.teamfortress.com/wiki/WebAPI/UpToDateCheck#cite_note-1) 25 | pub version_is_listable: bool, 26 | 27 | /// Integer of the most current version of the app available. 28 | pub required_version: Option, 29 | 30 | /// A string giving the status message if applicable. 31 | pub message: Option, 32 | } 33 | 34 | #[derive(Deserialize, Serialize, Debug)] 35 | struct Wrapper { 36 | response: UpToDateResponse, 37 | } 38 | 39 | impl Steam { 40 | /// Check if a given app version is the most current available. 41 | /// 42 | /// # Arguments 43 | /// 44 | /// * `appid` - AppID of game. 45 | /// * `version` - The installed version of the game. 46 | pub async fn up_to_date_check( 47 | appid: u32, 48 | version: u32, 49 | ) -> Result { 50 | let url = format!( 51 | "{}/{}/{}/v{}/?appid={}&version={}", 52 | BASE, INTERFACE, ENDPOINT, VERSION, appid, version 53 | ); 54 | let wrapper = do_http!(url, Wrapper, ErrorHandle, SteamAppsError::GetAppList); 55 | Ok(wrapper.response) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/steam_economy/get_asset_class_info.rs: -------------------------------------------------------------------------------- 1 | //! Implements the `GetAssetClassInfo` endpoint 2 | 3 | use std::collections::HashMap; 4 | 5 | use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; 6 | use serde::{Deserialize, Serialize}; 7 | use serde_json::Value; 8 | 9 | use crate::{ 10 | errors::{ErrorHandle, SteamEconomyError}, 11 | macros::{do_http, gen_args, optional_argument}, 12 | Steam, BASE, 13 | }; 14 | 15 | use super::INTERFACE; 16 | 17 | const ENDPOINT: &str = "GetAssetClassInfo"; 18 | const VERSION: &str = "1"; 19 | 20 | #[derive(Debug, Clone, Deserialize, Serialize)] 21 | #[serde(rename_all = "lowercase")] 22 | struct UncleanAssetClassInfo { 23 | pub result: HashMap, 24 | } 25 | 26 | /// Represents information about an asset class. 27 | #[derive(Debug, Clone, Deserialize, Serialize)] 28 | pub struct AssetClassInfo { 29 | /// The URL of the asset class icon. 30 | pub icon_url: String, 31 | /// The URL of the large asset class icon. 32 | pub icon_url_large: String, 33 | /// The URL used for dragging the asset class icon. 34 | pub icon_drag_url: String, 35 | /// The name of the asset class. 36 | pub name: String, 37 | /// The market hash name of the asset class. 38 | pub market_hash_name: String, 39 | /// The market name of the asset class. 40 | pub market_name: String, 41 | /// The color of the name associated with the asset class. 42 | pub name_color: String, 43 | /// The background color associated with the asset class. 44 | pub background_color: String, 45 | /// The type of the asset class. 46 | pub r#type: String, 47 | /// Indicates if the asset class is tradable. 48 | pub tradable: String, 49 | /// Indicates if the asset class is marketable. 50 | pub marketable: String, 51 | /// Optional market tradeable restriction of the asset class. 52 | pub market_tradeable_restriction: Option, 53 | /// Optional market marketable restriction of the asset class. 54 | pub market_marketable_restriction: Option, 55 | /// Fraud warnings associated with the asset class. 56 | pub fraudwarnings: String, 57 | /// Descriptions associated with the asset class. 58 | pub descriptions: HashMap, 59 | /// Actions associated with the asset class. 60 | pub actions: HashMap, 61 | /// Market actions associated with the asset class. 62 | pub market_actions: HashMap, 63 | /// Tags associated with the asset class. 64 | pub tags: HashMap, 65 | /// Application data associated with the asset class. 66 | pub app_data: AppData, 67 | /// Optional class ID of the asset class. 68 | pub class_id: Option, 69 | } 70 | 71 | /// Represents a description associated with an asset class. 72 | #[derive(Debug, Clone, Deserialize, Serialize)] 73 | pub struct Description { 74 | /// The type of the description. 75 | pub r#type: String, 76 | /// The value of the description. 77 | pub value: String, 78 | /// Application data associated with the description. 79 | pub app_data: String, 80 | } 81 | 82 | /// Represents an action associated with an asset class. 83 | #[derive(Debug, Clone, Deserialize, Serialize)] 84 | pub struct Action { 85 | /// The name of the action. 86 | pub name: String, 87 | /// The link associated with the action. 88 | pub link: String, 89 | } 90 | 91 | /// Represents application data associated with an asset class. 92 | #[derive(Debug, Clone, Deserialize, Serialize)] 93 | pub struct AppData { 94 | pub def_index: String, 95 | pub quality: String, 96 | pub slot: String, 97 | pub filter_data: HashMap>, 98 | pub player_class_ids: HashMap, 99 | pub highlight_color: String, 100 | } 101 | 102 | /// Represents a tag associated with an asset class. 103 | #[derive(Debug, Clone, Deserialize, Serialize)] 104 | pub struct Tag { 105 | /// The name of the tag. 106 | pub name: String, 107 | /// The internal name of the tag. 108 | pub internal_name: String, 109 | /// The category of the tag. 110 | pub category: String, 111 | /// The color of the tag, if available. 112 | pub color: Option, 113 | /// The name of the category associated with the tag. 114 | pub category_name: String, 115 | } 116 | 117 | impl Steam { 118 | /// Retrieves asset class information for a specified app. 119 | /// 120 | /// # Arguments 121 | /// 122 | /// * `appid` - The ID of the application (game) for which to retrieve asset class information. Must be a steam economy app. 123 | /// * `language` - An optional parameter specifying the user's local language. 124 | /// * `class_count` - Number of classes requested. Must be at least one. 125 | /// * `classid0` - Class ID of the nth class. 126 | /// * `instanceid0` - Instance ID of the nth class. 127 | /// 128 | /// Note: This endpoint gets modified to provide easier access to the data! This will not give an exact copy of the data outputed by the API. 129 | pub async fn get_asset_class_info( 130 | &self, 131 | appid: u32, 132 | language: Option<&str>, 133 | class_count: u32, 134 | classid0: u64, 135 | instanceid0: Option, 136 | ) -> Result, SteamEconomyError> { 137 | let key = &self.api_key.clone(); 138 | let args = gen_args!(key, appid, class_count, classid0) 139 | + &optional_argument!(language, instanceid0); 140 | let url = format!("{}/{}/{}/v{}/?{}", BASE, INTERFACE, ENDPOINT, VERSION, args); 141 | 142 | let response = do_http!( 143 | url, 144 | UncleanAssetClassInfo, 145 | ErrorHandle, 146 | SteamEconomyError::GetAssetClassInfo 147 | ) 148 | .clean()?; 149 | 150 | Ok(response) 151 | } 152 | } 153 | 154 | impl UncleanAssetClassInfo { 155 | pub fn clean(mut self) -> Result, SteamEconomyError> { 156 | // Discard success status 157 | self.result.remove("success"); 158 | 159 | // TODO: make sure that order isn't needed 160 | let data = self 161 | .result 162 | .par_iter() 163 | .map(move |(id, data)| -> (String, AssetClassInfo) { 164 | println!("{data:?}"); 165 | ( 166 | id.clone(), 167 | serde_json::from_value::(data.to_owned()).unwrap(), 168 | ) 169 | }) 170 | .collect::>(); 171 | 172 | // Convert the data into a hashmap 173 | let data = data 174 | .into_iter() 175 | .collect::>(); 176 | Ok(data) 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/steam_economy/get_asset_prices.rs: -------------------------------------------------------------------------------- 1 | //! Implements the `GetAssetPrices` endpoint 2 | 3 | use std::collections::HashMap; 4 | 5 | use serde::{Deserialize, Serialize}; 6 | use serde_json::{from_value, Value}; 7 | 8 | use crate::{ 9 | errors::{ErrorHandle, SteamEconomyError}, 10 | macros::{do_http, gen_args, optional_argument}, 11 | Steam, BASE, 12 | }; 13 | 14 | use super::INTERFACE; 15 | 16 | const ENDPOINT: &str = "GetAssetPrices"; 17 | const VERSION: &str = "1"; 18 | 19 | /// Represents asset prices for a specific app. 20 | #[derive(Debug, Clone, Deserialize, Serialize)] 21 | #[serde(rename_all = "lowercase")] 22 | pub struct AssetPrices { 23 | /// Indicates whether the request to retrieve asset prices was successful. 24 | pub success: bool, 25 | 26 | /// A vector containing information about each asset. 27 | pub assets: Vec, 28 | 29 | /// Optional tags associated with the assets. 30 | pub tags: Option>, 31 | 32 | /// Optional tag IDs associated with the assets. 33 | pub tag_ids: Option>, 34 | } 35 | 36 | /// Represents an asset. 37 | #[derive(Debug, Clone, Deserialize, Serialize)] 38 | pub struct Asset { 39 | /// Prices associated with the asset. 40 | /// 41 | /// This maps the price of the asset in different currencies (USD, GBP, EUR, etc.). 42 | pub prices: HashMap, 43 | 44 | /// The name of the asset. 45 | pub name: String, 46 | 47 | /// The date of the asset's creation. 48 | pub date: String, 49 | 50 | /// The classes associated with the asset. 51 | pub class: Vec>, 52 | 53 | /// The class ID of the asset. 54 | pub classid: String, 55 | 56 | /// Optional tags associated with the asset. 57 | pub tags: Option>, 58 | 59 | /// Optional tag IDs associated with the asset. 60 | pub tag_ids: Option>, 61 | } 62 | 63 | #[derive(Deserialize, Debug)] 64 | struct Wrapper { 65 | result: Option, 66 | } 67 | 68 | impl Steam { 69 | /// Retrieves asset prices for a specified app. 70 | /// 71 | /// # Arguments 72 | /// 73 | /// * `appid` - The ID of the application (game) for which to retrieve asset prices. Must be a Steam economy app. 74 | /// * `language` - An optional parameter specifying the user's local language. 75 | /// * `currency` - An optional parameter specifying the currency to filter for. 76 | pub async fn get_asset_prices( 77 | &self, 78 | appid: u32, 79 | language: Option<&str>, 80 | currency: Option<&str>, 81 | ) -> Result { 82 | let key = &self.api_key.clone(); 83 | let args = gen_args!(key, appid) + &optional_argument!(language, currency); 84 | let url = format!("{}/{}/{}/v{}/?{}", BASE, INTERFACE, ENDPOINT, VERSION, args); 85 | 86 | let json = do_http!(url, Value, ErrorHandle, SteamEconomyError::GetAssetPrices); 87 | let response: Wrapper = ErrorHandle!( 88 | from_value(json.to_owned()), 89 | SteamEconomyError::GetAssetPrices 90 | ); 91 | 92 | Ok(response.result.unwrap()) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/steam_economy/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Implements the `ISteamEconomy` interface 2 | 3 | const INTERFACE: &str = "ISteamEconomy"; 4 | 5 | pub mod get_asset_class_info; 6 | pub mod get_asset_prices; 7 | -------------------------------------------------------------------------------- /src/steam_id.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | 3 | use num_enum::TryFromPrimitive; 4 | use serde::{Deserialize, Deserializer, Serialize}; 5 | 6 | /// Represents a SteamID64 type which is used to uniquely identify users on the Steam platform. 7 | /// SteamID64 is a 64-bit unsigned integer. 8 | /// For more information on SteamID and SteamID64, refer to the official Steam documentation: 9 | /// 10 | /// 11 | /// # Examples 12 | /// 13 | /// ``` 14 | /// use steam_rs::steam_id::SteamId; 15 | /// 16 | /// let steam_id = SteamId::new(76561197960287930); 17 | /// println!("SteamId: {}", steam_id); 18 | /// ``` 19 | /// 20 | /// # Conversions 21 | /// 22 | /// You can convert a `u64` to a `SteamId` using the `into()` method: 23 | /// ``` 24 | /// use steam_rs::steam_id::SteamId; 25 | /// 26 | /// let steam_id: SteamId = 76561197960287930.into(); 27 | /// println!("SteamId from u64: {}", steam_id); 28 | /// ``` 29 | /// 30 | /// # Formatting 31 | /// 32 | /// The `SteamId` can be formatted as a string for display purposes: 33 | /// ``` 34 | /// use steam_rs::steam_id::SteamId; 35 | /// 36 | /// let steam_id = SteamId::new(76561197960287930); 37 | /// println!("Formatted SteamId: {}", steam_id.to_string()); 38 | /// ``` 39 | /// 40 | /// # Parsing 41 | /// 42 | /// You can parse a `SteamId` from a string: 43 | /// ``` 44 | /// use steam_rs::steam_id::SteamId; 45 | /// 46 | /// let steam_id = SteamId::from( 47 | /// "76561197960287930".to_string() 48 | /// ); 49 | /// 50 | /// println!("Parsed SteamId: {}", steam_id); 51 | /// ``` 52 | #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Hash, Serialize)] 53 | pub struct SteamId(pub u64); 54 | impl fmt::Display for SteamId { 55 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 56 | write!(f, "{}", self.0) 57 | } 58 | } 59 | 60 | impl SteamId { 61 | /// Creates a new `SteamId` instance with the given 64-bit unsigned integer value. 62 | pub fn new(value: u64) -> Self { 63 | SteamId(value) 64 | } 65 | 66 | /// Converts the `SteamId` into its underlying 64-bit unsigned integer value. 67 | pub fn into_u64(&self) -> u64 { 68 | self.0 69 | } 70 | 71 | /// Converts the `SteamId` into the unsigned 32-bit account ID used in its SteamID3 (and to some extent in the SteamID2). 72 | pub fn get_account_id(&self) -> u32 { 73 | (self.0 & 0xFFFFFFFF) as u32 74 | } 75 | 76 | /// Get Universe that the `SteamId` belongs to. 77 | pub fn get_universe(&self) -> Universe { 78 | Universe::try_from((self.0 >> 56) & 0xF).unwrap_or(Universe::Invalid) 79 | } 80 | 81 | /// Get account type of the `SteamId`. 82 | pub fn get_account_type(&self) -> AccountType { 83 | AccountType::try_from((self.0 >> 52) & 0xF).unwrap_or(AccountType::Invalid) 84 | } 85 | 86 | /// Get the `SteamId`'s SteamID2 string representation. 87 | pub fn to_id2_string(&self) -> String { 88 | let id = self.get_account_id(); 89 | format!( 90 | "STEAM_{}:{}:{}", 91 | self.get_universe() as u64, 92 | id & 1, 93 | id >> 1 94 | ) 95 | } 96 | } 97 | 98 | impl From for SteamId { 99 | /// Converts a `u64` into a `SteamId`. 100 | fn from(value: u64) -> Self { 101 | SteamId(value) 102 | } 103 | } 104 | 105 | impl From for SteamId { 106 | /// Formats the `SteamId` as a `String`. 107 | fn from(value: String) -> Self { 108 | SteamId(value.parse::().unwrap()) 109 | } 110 | } 111 | 112 | /// Deserializes the `SteamId` from a `String` 113 | pub fn de_steamid_from_str<'de, D>(deserializer: D) -> Result 114 | where 115 | D: serde::Deserializer<'de>, 116 | { 117 | let s = String::deserialize(deserializer)?; 118 | Ok(SteamId::from(s)) 119 | } 120 | 121 | fn callback<'de, D>(deserializer: D) -> Result 122 | where 123 | D: Deserializer<'de>, 124 | { 125 | Ok(SteamId::from(String::deserialize(deserializer)?)) 126 | } 127 | 128 | #[derive(Debug, Deserialize)] 129 | struct WrappedSteamId(#[serde(deserialize_with = "callback")] SteamId); 130 | 131 | pub fn de_steamid_from_str_opt<'de, D>(deserializer: D) -> Result, D::Error> 132 | where 133 | D: serde::Deserializer<'de>, 134 | { 135 | Option::::deserialize(deserializer).map( 136 | |opt_wrapped: Option| opt_wrapped.map(|wrapped: WrappedSteamId| wrapped.0), 137 | ) 138 | } 139 | 140 | /// Error type for parsing a `SteamId` from a `string`. 141 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 142 | pub struct ParseSteamIdError; 143 | 144 | impl std::fmt::Display for ParseSteamIdError { 145 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 146 | write!(f, "Failed to parse SteamID") 147 | } 148 | } 149 | 150 | impl std::error::Error for ParseSteamIdError {} 151 | 152 | /// Denotes what kind of account the SteamID belongs to. 153 | #[derive(Copy, Clone, PartialEq, Eq, Debug, TryFromPrimitive)] 154 | #[repr(u64)] 155 | pub enum AccountType { 156 | /// Used for bots and accounts which do not belong to another class. 157 | Invalid, 158 | /// Single user account. 159 | Individual, 160 | /// Multiseat (e.g. cybercafe) account. 161 | Multiseat, 162 | /// Game server account. 163 | GameServer, 164 | /// Anonymous game server account. 165 | AnonGameServer, 166 | /// Sometimes used to temporarily refer to Individual accounts until their credentials are verified with Steam. 167 | Pending, 168 | ContentServer, 169 | Clan, 170 | Chat, 171 | /// Fake SteamID for local PSN account on PS3 or Live account on 360, etc. 172 | P2PSuperSeeder, 173 | AnonUser, 174 | } 175 | 176 | /// An "Universe" is an instance of Steam an account can belong to. "Public" is probably the one you'll be interacting with. 177 | #[derive(Copy, Clone, PartialEq, Eq, Debug, TryFromPrimitive)] 178 | #[repr(u64)] 179 | pub enum Universe { 180 | Invalid, 181 | Public, 182 | Beta, 183 | Internal, 184 | Dev, 185 | } 186 | -------------------------------------------------------------------------------- /src/steam_news/get_news_for_app.rs: -------------------------------------------------------------------------------- 1 | //! Implements the `GetNewsForApp` endpoint 2 | 3 | use crate::{ 4 | errors::{ErrorHandle, SteamNewsError}, 5 | macros::{do_http, optional_argument}, 6 | Steam, BASE, 7 | }; 8 | use serde::{Deserialize, Serialize}; 9 | 10 | use super::INTERFACE; 11 | 12 | const ENDPOINT: &str = "GetNewsForApp"; 13 | const VERSION: &str = "0002"; 14 | 15 | #[derive(Serialize, Deserialize, Debug, Clone)] 16 | pub struct NewsItem { 17 | pub gid: String, 18 | pub title: String, 19 | pub url: String, 20 | pub is_external_url: bool, 21 | pub author: String, 22 | pub contents: String, 23 | pub feedlabel: String, 24 | pub date: u32, 25 | pub feedname: String, 26 | pub feed_type: u8, 27 | pub appid: u32, 28 | pub tags: Option>, 29 | } 30 | 31 | #[derive(Serialize, Deserialize, Debug, Clone)] 32 | pub struct AppNews { 33 | pub appid: u32, // TODO: Convert to AppId 34 | pub newsitems: Vec, 35 | pub count: u32, 36 | } 37 | 38 | #[derive(Serialize, Deserialize, Debug)] 39 | struct Response { 40 | appnews: AppNews, 41 | } 42 | 43 | impl Steam { 44 | /// Get the news for the specified app. 45 | /// 46 | /// # Arguments 47 | /// 48 | /// * `appid` - The ID of the application (game) for which to retrieve news for. 49 | /// * `max_length` - Maximum length for the content to return, if this is 0 the full content is returned, if it's less then a blurb is generated to fit. 50 | /// * `end_date` - Retrieve posts earlier than this date (unix epoch timestamp). 51 | /// * `count` - Number of posts to retrieve (default 20). 52 | /// * `feeds` - Comma-seperated list of feed names to return news for. 53 | pub async fn get_news_for_app( 54 | appid: u32, 55 | max_length: Option, 56 | end_date: Option, 57 | count: Option, 58 | feeds: Option>, 59 | ) -> Result { 60 | // DO NOT RAYON THIS! - Rayon doesn't protect the order of data! 61 | let feeds: Option = 62 | feeds.map(|feeds| feeds.iter().map(|&feed| feed.to_string() + ",").collect()); 63 | 64 | let optional_arguments = vec![ 65 | optional_argument!(max_length, "maxlength"), 66 | optional_argument!(end_date, "enddate"), 67 | optional_argument!(count), 68 | optional_argument!(feeds), 69 | ]; 70 | 71 | let query = format!("?appid={}{}", appid, optional_arguments.concat()); 72 | let url = format!("{}/{}/{}/v{}/{}", BASE, INTERFACE, ENDPOINT, VERSION, query); 73 | 74 | let response = do_http!(url, Response, ErrorHandle, SteamNewsError::GetNews); 75 | 76 | Ok(response.appnews) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/steam_news/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Implements the `ISteamNews` interface 2 | //! 3 | //! Provides access to the Steam News functionality. 4 | 5 | const INTERFACE: &str = "ISteamNews"; 6 | 7 | pub mod get_news_for_app; 8 | -------------------------------------------------------------------------------- /src/steam_remote_storage/get_collection_details.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::{ 4 | errors::{ErrorHandle, SteamRemoteStorageError}, 5 | macros::do_http, 6 | Steam, BASE, 7 | }; 8 | 9 | use super::INTERFACE; 10 | 11 | const ENDPOINT: &str = "GetCollectionDetails"; 12 | const VERSION: &str = "1"; 13 | 14 | #[derive(Debug, Deserialize, Serialize, Clone)] 15 | pub struct CollectionDetails { 16 | #[serde(rename = "publishedfileid")] 17 | pub published_file_id: String, 18 | pub result: u32, 19 | } 20 | 21 | #[derive(Debug, Deserialize, Serialize, Clone)] 22 | pub struct Response { 23 | pub result: u32, 24 | #[serde(rename = "resultcount")] 25 | pub result_count: u32, 26 | #[serde(rename = "collectiondetails")] 27 | pub collection_details: Vec, 28 | } 29 | 30 | #[derive(Debug, Deserialize, Serialize)] 31 | struct Wrapper { 32 | response: Response, 33 | } 34 | 35 | impl Steam { 36 | pub async fn get_collection_details( 37 | published_fileids: &[u64], 38 | ) -> Result { 39 | let url = format!("{BASE}/{INTERFACE}/{ENDPOINT}/v{VERSION}"); 40 | 41 | let mut params = String::new(); 42 | 43 | params.push_str(&format!("collectioncount={}", published_fileids.len())); 44 | 45 | for (index, fileid) in published_fileids.iter().enumerate() { 46 | params.push_str(&format!("&publishedfileids[{}]={}", index, fileid)); 47 | } 48 | 49 | let wrapper = do_http!( 50 | url, 51 | Wrapper, 52 | ErrorHandle, 53 | SteamRemoteStorageError::GetCollectionDetails, 54 | params 55 | ); 56 | 57 | Ok(wrapper.response) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/steam_remote_storage/get_published_file.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::{ 4 | errors::{ErrorHandle, SteamRemoteStorageError}, 5 | macros::do_http, 6 | Steam, BASE, 7 | }; 8 | 9 | use super::INTERFACE; 10 | 11 | const ENDPOINT: &str = "GetPublishedFileDetails"; 12 | const VERSION: &str = "1"; 13 | 14 | #[derive(Debug, Deserialize, Serialize, Clone)] 15 | pub struct PublishedFileDetails { 16 | #[serde(rename = "publishedfileid")] 17 | pub published_file_id: String, 18 | pub result: u32, 19 | } 20 | 21 | #[derive(Debug, Deserialize, Serialize, Clone)] 22 | pub struct Response { 23 | pub result: u32, 24 | #[serde(rename = "resultcount")] 25 | pub result_count: u32, 26 | #[serde(rename = "publishedfiledetails")] 27 | pub published_file_details: Vec, 28 | } 29 | 30 | #[derive(Debug, Deserialize, Serialize)] 31 | struct Wrapper { 32 | response: Response, 33 | } 34 | 35 | impl Steam { 36 | pub async fn get_published_file( 37 | published_fileids: &[u64], 38 | ) -> Result { 39 | let url = format!("{BASE}/{INTERFACE}/{ENDPOINT}/v{VERSION}"); 40 | 41 | let mut params = String::new(); 42 | 43 | params.push_str(&format!("itemcount={}", published_fileids.len())); 44 | 45 | for (index, fileid) in published_fileids.iter().enumerate() { 46 | params.push_str(&format!("&publishedfileids[{}]={}", index, fileid)); 47 | } 48 | 49 | let wrapper = do_http!( 50 | url, 51 | Wrapper, 52 | ErrorHandle, 53 | SteamRemoteStorageError::GetCollectionDetails, 54 | params 55 | ); 56 | 57 | Ok(wrapper.response) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/steam_remote_storage/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Implements the `ISteamRemoteStorage` interface 2 | //! 3 | //! Primary interface for interacting with the Steam Workshop and User Generated Content (UGC). 4 | //! 5 | //! **Note:** This implementation is incomplete! The following endpoints are currently unimplemented 6 | //! 7 | //! - EnumerateUserSubscribedFiles (requires publisher key) 8 | //! - GetUGCFileDetails 9 | //! - SetUGCUsedByGC (requires publisher key) 10 | //! - SubscribePublishedFile (requires publisher key) 11 | //! - UnsubscribePublishedFile (requires publisher key) 12 | //! 13 | //! Endpoints that require a publisher key are not likely to be 14 | //! implemented in the near future, as they cannot be tested by developers. 15 | 16 | const INTERFACE: &str = "ISteamRemoteStorage"; 17 | 18 | pub mod get_collection_details; 19 | pub mod get_published_file; 20 | -------------------------------------------------------------------------------- /src/steam_user/get_friend_list.rs: -------------------------------------------------------------------------------- 1 | //! # Implements the `GetFriendList` endpoint 2 | 3 | use core::fmt; 4 | 5 | use serde::Deserialize; 6 | use serde_json::{from_value, Value}; 7 | 8 | use crate::{ 9 | errors::{ErrorHandle, SteamUserError}, 10 | macros::{do_http, optional_argument}, 11 | steam_id::{de_steamid_from_str, SteamId}, 12 | Steam, BASE, 13 | }; 14 | 15 | use super::INTERFACE; 16 | 17 | const ENDPOINT: &str = "GetFriendList"; 18 | const VERSION: &str = "1"; 19 | 20 | /// Represents the types of relationships a user can have with their friends on Steam. 21 | #[derive(PartialEq, Debug, Deserialize, Clone, Copy)] 22 | #[serde(rename_all = "lowercase")] 23 | pub enum Relationship { 24 | /// All relationships. 25 | All, 26 | /// Friends relationship. 27 | Friend, 28 | } 29 | 30 | impl fmt::Display for Relationship { 31 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 32 | match self { 33 | Relationship::All => write!(f, "all"), 34 | Relationship::Friend => write!(f, "friends"), 35 | } 36 | } 37 | } 38 | 39 | /// Represents a friend of a Steam user. 40 | #[derive(Deserialize, Debug, Clone)] 41 | pub struct Friend { 42 | /// The 64 bit ID of the friend. 43 | #[serde(rename = "steamid")] 44 | #[serde(deserialize_with = "de_steamid_from_str")] 45 | pub steam_id: SteamId, 46 | 47 | /// Role in relation to the given SteamID. 48 | pub relationship: Relationship, 49 | 50 | /// A unix timestamp of when the friend was added to the list. 51 | pub friend_since: u32, 52 | } 53 | 54 | // Represents the user's friend list. 55 | // 56 | // **Note:** If the profile is not public or there are no available entries for the given relationship only an empty object will be returned. 57 | #[derive(Deserialize, Debug)] 58 | struct FriendsList { 59 | /// A vector of Friend objects. 60 | friends: Vec, 61 | } 62 | 63 | #[derive(Deserialize, Debug)] 64 | struct Wrapper { 65 | /// If the profile is not public or there are no available entries for the given relationship only an empty object will be returned. 66 | #[serde(rename = "friendslist")] 67 | friends_list: Option, 68 | } 69 | 70 | impl Steam { 71 | /// Get a user's friend list. 72 | /// 73 | /// # Arguments 74 | /// 75 | /// * `steam_id` - The SteamID of the user. 76 | /// * `relationship` - Optional relationship type (e.g., `Relationship::Friend`). 77 | pub async fn get_friend_list( 78 | &self, 79 | steam_id: SteamId, // SteamID of user 80 | relationship: Option, // relationship type (ex: Relationship::Friend) 81 | ) -> Result, SteamUserError> { 82 | let query = format!( 83 | "?key={}&steamid={}{}", 84 | &self.api_key, 85 | steam_id, 86 | optional_argument!(relationship) 87 | ); 88 | let url = format!("{}/{}/{}/v{}/{}", BASE, INTERFACE, ENDPOINT, VERSION, query); 89 | let json = do_http!(url, Value, ErrorHandle, SteamUserError::GetFriendList); 90 | let wrapper: Wrapper = 91 | ErrorHandle!(from_value(json.to_owned()), SteamUserError::GetFriendList); 92 | 93 | Ok(wrapper.friends_list.unwrap().friends) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/steam_user/get_player_bans.rs: -------------------------------------------------------------------------------- 1 | //! Implements the `GetPlayerBans` endpoint 2 | 3 | use serde::{Deserialize, Serialize}; 4 | use serde_json::{from_value, Value}; 5 | 6 | use crate::{ 7 | errors::{ErrorHandle, SteamUserError}, 8 | macros::do_http, 9 | steam_id::SteamId, 10 | Steam, BASE, 11 | }; 12 | 13 | use super::INTERFACE; 14 | 15 | const ENDPOINT: &str = "GetPlayerBans"; 16 | const VERSION: &str = "1"; 17 | 18 | #[derive(Serialize, Deserialize, Debug)] 19 | struct Wrapper { 20 | /// Vector of player objects for each given (and valid) 64-bit ID. 21 | players: Vec, 22 | } 23 | 24 | /// Represents a player object with data about bans. 25 | #[derive(Serialize, Deserialize, Debug, Clone)] 26 | pub struct Player { 27 | /// A string containing the player's 64-bit ID. 28 | #[serde(rename = "SteamId")] 29 | pub steam_id: String, // TODO: Convert to SteamId 30 | 31 | /// Boolean indicating whether or not the player is banned 32 | /// from the [Steam Community](http://steamcommunity.com/). 33 | #[serde(rename = "CommunityBanned")] 34 | pub community_banned: bool, 35 | 36 | /// Boolean indicating whether or not the player has VAC bans on record. 37 | #[serde(rename = "VACBanned")] 38 | pub vac_banned: bool, 39 | 40 | /// Number of VAC bans. 41 | #[serde(rename = "NumberOfVACBans")] 42 | pub number_of_vac_bans: u32, 43 | 44 | /// Days since the player's last ban. 45 | #[serde(rename = "DaysSinceLastBan")] 46 | pub days_since_last_ban: u32, 47 | 48 | /// Number of bans in games. 49 | #[serde(rename = "NumberOfGameBans")] 50 | pub number_of_game_bans: u32, 51 | 52 | /// String containing the player's ban status in the economy. 53 | /// If the player has no bans on record the string will be "none", 54 | /// if the player is on probation it will say "probation", and so forth. 55 | #[serde(rename = "EconomyBan")] 56 | pub economy_ban: String, 57 | } 58 | 59 | impl Steam { 60 | /// Get player ban/probation status. 61 | /// 62 | /// # Arguments 63 | /// 64 | /// * `steam_ids` - A vector of `SteamId` objects 65 | pub async fn get_player_bans( 66 | &self, 67 | steam_ids: Vec, 68 | ) -> Result, SteamUserError> { 69 | let steam_ids: String = steam_ids.iter().map(|&id| id.to_string() + ",").collect(); 70 | 71 | let query = format!("?key={}&steamids={}", &self.api_key, steam_ids); 72 | let url = format!("{}/{}/{}/v{}/{}", BASE, INTERFACE, ENDPOINT, VERSION, query); 73 | 74 | let json = do_http!(url, Value, ErrorHandle, SteamUserError::GetPlayerBans); 75 | let wrapper: Wrapper = 76 | ErrorHandle!(from_value(json.to_owned()), SteamUserError::GetPlayerBans); 77 | 78 | Ok(wrapper.players) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/steam_user/get_player_summaries.rs: -------------------------------------------------------------------------------- 1 | //! Implements the `GetPlayerSummaries` endpoint 2 | 3 | use serde::{Deserialize, Serialize}; 4 | use serde_json::{from_value, Value}; 5 | 6 | use crate::{ 7 | errors::{ErrorHandle, SteamUserError}, 8 | macros::do_http, 9 | steam_id::{de_steamid_from_str, SteamId}, 10 | Steam, BASE, 11 | }; 12 | 13 | use super::INTERFACE; 14 | 15 | const ENDPOINT: &str = "GetPlayerSummaries"; 16 | const VERSION: &str = "0002"; 17 | 18 | /// Represents a user profile object. 19 | /// 20 | /// Contained information varies depending on whether or not the user has their profile set to Friends only or Private. 21 | #[derive(Serialize, Deserialize, Debug, Clone)] 22 | pub struct Player { 23 | /// The user's 64-bit ID 24 | #[serde(rename = "steamid")] 25 | #[serde(deserialize_with = "de_steamid_from_str")] 26 | pub steam_id: SteamId, 27 | 28 | /// User's display name. 29 | #[serde(rename = "personaname")] 30 | pub persona_name: String, 31 | 32 | /// The URL to the user's Steam Community profile. 33 | #[serde(rename = "profileurl")] 34 | pub profile_url: String, 35 | 36 | /// The URL to the user's avatar as a 32x32 image 37 | pub avatar: String, 38 | 39 | /// The URL to the user's avatar as a 64x64 image 40 | #[serde(rename = "avatarmedium")] 41 | pub avatar_medium: String, 42 | 43 | /// The URL to the user's avatar as a 184x184 image 44 | #[serde(rename = "avatarfull")] 45 | pub avatar_full: String, 46 | 47 | /// Hash of the user's avatar 48 | #[serde(rename = "avatarhash")] 49 | pub avatar_hash: String, 50 | 51 | /// The user's status 52 | /// - 0: Offline (Also set when the profile is Private) 53 | /// - 1: Online 54 | /// - 2: Busy 55 | /// - 3: Away 56 | /// - 4: Snooze 57 | /// - 5: Looking to trade 58 | /// - 6: Looking to play 59 | #[serde(rename = "personastate")] 60 | pub persona_state: u8, 61 | 62 | /// An integer that describes the access setting of the profile. 63 | /// - 1: Private 64 | /// - 2: Friends only 65 | /// - 3: Friends of Friends 66 | /// - 4: Users Only 67 | /// - 5: Public 68 | #[serde(rename = "communityvisibilitystate")] 69 | pub community_visibility_state: u8, 70 | 71 | /// If set to 1, the user has configured the profile. 72 | #[serde(rename = "profilestate")] 73 | pub profile_state: Option, 74 | 75 | /// A unix timestamp of when the user was last online. 76 | #[serde(rename = "lastlogoff")] 77 | pub last_logoff: Option, 78 | 79 | /// If present the profile allows public comments. 80 | #[serde(rename = "commentpermission")] 81 | pub comment_permission: Option, 82 | 83 | /// The user's real name. 84 | #[serde(rename = "realname")] 85 | pub real_name: Option, 86 | 87 | /// The 64-bit ID of the user's primary group. 88 | #[serde(rename = "primaryclanid")] 89 | pub primary_clan_id: Option, 90 | 91 | /// A unix timestamp of the date the profile was created. 92 | #[serde(rename = "timecreated")] 93 | pub time_created: Option, 94 | 95 | /// If the user is in game this will be set to it's app ID as a string. 96 | #[serde(rename = "gameid")] 97 | pub game_id: Option, 98 | 99 | /// The server URL given as an IP address and port number separated by a colon. 100 | /// This will not be present or set to "0.0.0.0:0" if none is available. 101 | #[serde(rename = "gameserverip")] 102 | pub game_server_ip: Option, 103 | 104 | /// The title of the game that the user is playing. 105 | #[serde(rename = "gameextrainfo")] 106 | pub game_extra_info: Option, 107 | 108 | /// ISO 3166 code of where the user is located. 109 | #[serde(rename = "loccountrycode")] 110 | pub loc_country_code: Option, 111 | 112 | /// Variable length code representing the state the user is located in. 113 | #[serde(rename = "locstatecode")] 114 | pub loc_state_code: Option, 115 | 116 | /// An integer ID internal to Steam representing the user's city. 117 | #[serde(rename = "loccityid")] 118 | pub loc_city_id: Option, 119 | } 120 | 121 | #[derive(Serialize, Deserialize, Debug)] 122 | struct PlayerSummary { 123 | /// A list of profile objects. Contained information varies depending on 124 | /// whether or not the user has their profile set to Friends only or Private. 125 | players: Vec, 126 | } 127 | 128 | #[derive(Serialize, Deserialize, Debug)] 129 | struct Wrapper { 130 | response: PlayerSummary, 131 | } 132 | 133 | impl Steam { 134 | /// Get user profile data. 135 | /// 136 | /// # Arguments 137 | /// 138 | /// * `steam_ids` - A vector of `SteamId` objects 139 | pub async fn get_player_summaries( 140 | &self, 141 | steam_ids: Vec, 142 | ) -> Result, SteamUserError> { 143 | let steam_ids: String = steam_ids.iter().map(|&id| id.to_string() + ",").collect(); 144 | 145 | let query = format!("?key={}&steamids={}", &self.api_key, steam_ids); 146 | let url = format!("{}/{}/{}/v{}/{}", BASE, INTERFACE, ENDPOINT, VERSION, query); 147 | 148 | let json = do_http!(url, Value, ErrorHandle, SteamUserError::GetPlayerSummaries); 149 | let wrapper: Wrapper = ErrorHandle!( 150 | from_value(json.to_owned()), 151 | SteamUserError::GetPlayerSummaries 152 | ); 153 | 154 | Ok(wrapper.response.players) 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/steam_user/get_user_group_list.rs: -------------------------------------------------------------------------------- 1 | //! # Implements the `GetUserGroupList` endpoint 2 | 3 | use serde::Deserialize; 4 | use serde_json::{from_value, Value}; 5 | 6 | use crate::{ 7 | errors::{ErrorHandle, SteamUserError}, 8 | macros::do_http, 9 | steam_id::SteamId, 10 | Steam, BASE, 11 | }; 12 | 13 | use super::INTERFACE; 14 | 15 | const ENDPOINT: &str = "GetUserGroupList"; 16 | const VERSION: &str = "1"; 17 | 18 | #[derive(Deserialize, Debug)] 19 | struct Wrapper { 20 | response: Response, 21 | } 22 | 23 | #[derive(Deserialize, Debug, Clone)] 24 | pub struct Response { 25 | /// Boolean indicating if request was successful. 26 | pub success: bool, 27 | 28 | /// Error message that is given only if request was unsuccessful 29 | pub error: Option, 30 | 31 | /// A vector of `Group` objects, representing the groups that the user is a member of 32 | pub groups: Vec, 33 | } 34 | 35 | /// Represents a Steam group 36 | #[derive(Deserialize, Debug, Clone)] 37 | pub struct Group { 38 | /// The group's ID 39 | pub gid: String, 40 | } 41 | 42 | impl Steam { 43 | /// Get a list of groups that a user is a member of. 44 | /// 45 | /// # Arguments 46 | /// 47 | /// * `steam_id` - The SteamID of the user. 48 | pub async fn get_user_group_list(&self, steam_id: SteamId) -> Result { 49 | let query = format!("?key={}&steamid={}", &self.api_key, steam_id); 50 | let url = format!("{}/{}/{}/v{}/{}", BASE, INTERFACE, ENDPOINT, VERSION, query); 51 | println!("{}", url); 52 | let json = do_http!(url, Value, ErrorHandle, SteamUserError::GetUserGroupList); 53 | let wrapper: Wrapper = ErrorHandle!( 54 | from_value(json.to_owned()), 55 | SteamUserError::GetUserGroupList 56 | ); 57 | 58 | Ok(wrapper.response) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/steam_user/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Implements the `ISteamUser` interface 2 | //! 3 | //! **Note:** This implementation is incomplete! 4 | //! The following endpoints are currently unimplemented: 5 | //! 6 | //! - CheckAppOwnership (requires publisher key) 7 | //! - GetAppPriceInfo (requires publisher key) 8 | //! - GetDeletedSteamIDs (requires publisher key) 9 | //! - GetPublisherAppOwnership (requires publisher key) 10 | //! - GetPublisherAppOwnershipChanges (requires publisher key) 11 | //! - GrantPackage (requires publisher key) 12 | //! - RevokePackage (requires publisher key) 13 | //! 14 | //! Endpoints that require a publisher key are not likely to be 15 | //! implemented in the near future, as they cannot be tested by developers. 16 | 17 | const INTERFACE: &str = "ISteamUser"; 18 | 19 | pub mod get_friend_list; 20 | pub mod get_player_bans; 21 | pub mod get_player_summaries; 22 | pub mod get_user_group_list; 23 | pub mod resolve_vanity_url; 24 | -------------------------------------------------------------------------------- /src/steam_user/resolve_vanity_url.rs: -------------------------------------------------------------------------------- 1 | //! Implements the `ResolveVanityURL` endpoint 2 | 3 | use serde::{Deserialize, Serialize}; 4 | use serde_json::{from_value, Value}; 5 | 6 | use crate::{ 7 | errors::{ErrorHandle, SteamUserError}, 8 | macros::{do_http, optional_argument}, 9 | Steam, BASE, 10 | }; 11 | 12 | use super::INTERFACE; 13 | 14 | const ENDPOINT: &str = "ResolveVanityURL"; 15 | const VERSION: &str = "1"; 16 | 17 | #[derive(Serialize, Deserialize, Debug)] 18 | struct Wrapper { 19 | response: Response, 20 | } 21 | 22 | #[derive(Serialize, Deserialize, Debug, Clone)] 23 | pub struct Response { 24 | /// The message associated with the request status. Currently only used on resolution failures. 25 | pub message: Option, 26 | 27 | /// The 64 bit Steam ID the vanity URL resolves to. Not returned on resolution failures. 28 | pub steamid: Option, 29 | 30 | /// The status of the request. 1 if successful, 42 if there was no match. 31 | pub success: u8, 32 | } 33 | 34 | impl Steam { 35 | /// Resolve vanity URL parts to a 64-bit ID. 36 | /// 37 | /// # Arguments 38 | /// 39 | /// * `vanity_url` - The user's vanity URL that you would like to retrieve a steam ID for, e.g. would use "gabelogannewell" 40 | /// * `url_type` - The type of vanity URL. 41 | /// * 1 (default): Individual profile 42 | /// * 2: Group, 43 | /// * 3: Official game group 44 | pub async fn resolve_vanity_url( 45 | &self, 46 | vanity_url: &str, 47 | url_type: Option, 48 | ) -> Result { 49 | let query = vec![ 50 | format!("?key={}", &self.api_key), 51 | format!("&vanityurl={}", vanity_url), 52 | optional_argument!(url_type), 53 | ]; 54 | 55 | let url = format!( 56 | "{}/{}/{}/v{}/{}", 57 | BASE, 58 | INTERFACE, 59 | ENDPOINT, 60 | VERSION, 61 | query.concat() 62 | ); 63 | 64 | let json = do_http!(url, Value, ErrorHandle, SteamUserError::ResolveVanityURL); 65 | let wrapper: Wrapper = ErrorHandle!( 66 | from_value(json.to_owned()), 67 | SteamUserError::ResolveVanityURL 68 | ); 69 | 70 | Ok(wrapper.response) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/steam_user_auth/authenticate_user_ticket.rs: -------------------------------------------------------------------------------- 1 | //! # Implements the `AuthenticateUserTicket` endpoint 2 | 3 | use std::fmt::Debug; 4 | 5 | use serde::{Deserialize, Serialize}; 6 | 7 | use crate::{ 8 | errors::{ErrorHandle, SteamUserAuthError}, 9 | macros::do_http, 10 | Steam, BASE, 11 | }; 12 | 13 | use super::INTERFACE; 14 | 15 | const ENDPOINT: &str = "AuthenticateUserTicket"; 16 | const VERSION: &str = "1"; 17 | 18 | #[derive(Debug, Deserialize, Serialize, Clone)] 19 | pub struct TicketAuthResponse { 20 | pub result: String, 21 | #[serde(rename = "steamid")] 22 | pub steam_id: String, 23 | #[serde(rename = "ownersteamid")] 24 | pub owner_steam_id: String, 25 | #[serde(rename = "vacbanned")] 26 | pub vac_banned: bool, 27 | #[serde(rename = "publisherbanned")] 28 | pub publisher_banned: bool, 29 | } 30 | 31 | #[derive(Debug, Deserialize, Serialize)] 32 | pub struct WrapperParams { 33 | pub params: TicketAuthResponse, 34 | } 35 | 36 | #[derive(Debug, Deserialize, Serialize)] 37 | struct Wrapper { 38 | response: WrapperParams, 39 | } 40 | 41 | impl Steam { 42 | pub async fn authenticate_user_ticket( 43 | &self, 44 | app_id: u32, 45 | ticket: &str, 46 | ) -> Result { 47 | let key = &self.api_key.clone(); 48 | let url = format!( 49 | "{BASE}/{INTERFACE}/{ENDPOINT}/v{VERSION}/?key={key}&appid={app_id}&ticket={ticket}" 50 | ); 51 | let wrapper = do_http!( 52 | url, 53 | Wrapper, 54 | ErrorHandle, 55 | SteamUserAuthError::AuthenticateUserTicket 56 | ); 57 | Ok(wrapper.response.params) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/steam_user_auth/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Implements the `ISteamUserAuth` interface 2 | //! 3 | //! Used to authenticate users in your application. 4 | //! 5 | //! **Note:** This implementation is incomplete! 6 | //! The following endpoints are currently unimplemented: 7 | //! 8 | //! - AuthenticateUser 9 | 10 | const INTERFACE: &str = "ISteamUserAuth"; 11 | 12 | pub mod authenticate_user_ticket; 13 | -------------------------------------------------------------------------------- /src/steam_user_stats/get_global_achievement_percentages_for_app.rs: -------------------------------------------------------------------------------- 1 | //! Implements the `GetGlobalAchievementPercentagesForApp` endpoint 2 | 3 | use serde::{Deserialize, Serialize}; 4 | use serde_this_or_that::as_f64; 5 | 6 | use crate::{ 7 | errors::{ErrorHandle, SteamUserStatsError}, 8 | macros::do_http, 9 | Steam, BASE, 10 | }; 11 | 12 | use super::INTERFACE; 13 | 14 | const ENDPOINT: &str = "GetGlobalAchievementPercentagesForApp"; 15 | const VERSION: &str = "0002"; 16 | 17 | #[derive(Deserialize, Serialize, Debug, Clone)] 18 | pub struct Achievement { 19 | pub name: String, 20 | #[serde(deserialize_with = "as_f64")] 21 | pub percent: f64, 22 | } 23 | 24 | #[derive(Deserialize, Serialize, Debug, Clone)] 25 | pub struct AchievementPercentages { 26 | pub achievements: Vec, 27 | } 28 | 29 | #[derive(Deserialize, Serialize, Debug)] 30 | struct Response { 31 | achievementpercentages: AchievementPercentages, 32 | } 33 | 34 | impl Steam { 35 | /// Retrieves the global achievement percentages for the specified app. 36 | /// 37 | /// # Arguments 38 | /// 39 | /// * `game_id` - GameID to retrieve the achievement percentages for. 40 | pub async fn get_global_achievement_percentages_for_app( 41 | &self, 42 | game_id: u32, 43 | ) -> Result { 44 | let query = format!("?gameid={}", game_id); 45 | let url = format!("{}/{}/{}/v{}/{}", BASE, INTERFACE, ENDPOINT, VERSION, query); 46 | let response = do_http!( 47 | url, 48 | Response, 49 | ErrorHandle, 50 | SteamUserStatsError::GetGlobalAchievements 51 | ); 52 | Ok(response.achievementpercentages) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/steam_user_stats/get_number_of_current_players.rs: -------------------------------------------------------------------------------- 1 | //! Implements the `GetNumberOfCurrentPlayers` endpoint 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use crate::{ 6 | errors::{ErrorHandle, SteamUserStatsError}, 7 | macros::{do_http, gen_args}, 8 | Steam, BASE, 9 | }; 10 | 11 | use super::INTERFACE; 12 | 13 | const ENDPOINT: &str = "GetNumberOfCurrentPlayers"; 14 | const VERSION: &str = "1"; 15 | 16 | #[derive(Debug, Clone, Deserialize, Serialize)] 17 | struct Wrapper { 18 | response: Response, 19 | } 20 | 21 | #[derive(Debug, Clone, Deserialize, Serialize)] 22 | struct Response { 23 | player_count: Option, 24 | result: u8, 25 | } 26 | 27 | impl Steam { 28 | /// Retrieves the number of current players for a game. 29 | /// 30 | /// # Arguments 31 | /// 32 | /// * `appid` - The ID of the application (game) for which to retrieve the number of current players. 33 | pub async fn get_number_of_current_players( 34 | &self, 35 | appid: u32, 36 | ) -> Result { 37 | let key = &self.api_key.clone(); 38 | let args = gen_args!(key, appid); 39 | let url = format!("{BASE}/{INTERFACE}/{ENDPOINT}/v{VERSION}/?{args}"); 40 | 41 | let wrapper = do_http!( 42 | url, 43 | Wrapper, 44 | ErrorHandle, 45 | SteamUserStatsError::GetNumberOfCurrentPlayers 46 | ); 47 | 48 | match wrapper.response.result { 49 | 1 => Ok(wrapper.response.player_count.unwrap()), 50 | _ => Err(SteamUserStatsError::GetNumberOfCurrentPlayers( 51 | "App not found.".to_string(), 52 | )), 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/steam_user_stats/get_player_achievements.rs: -------------------------------------------------------------------------------- 1 | //! Implements the `GetPlayerAchievements` endpoint 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use crate::{ 6 | errors::{ErrorHandle, SteamUserStatsError}, 7 | macros::{do_http, gen_args, optional_argument}, 8 | steam_id::SteamId, 9 | Steam, BASE, 10 | }; 11 | 12 | use super::INTERFACE; 13 | 14 | const ENDPOINT: &str = "GetPlayerAchievements"; 15 | const VERSION: &str = "1"; 16 | 17 | #[derive(Debug, Clone, Deserialize, Serialize)] 18 | struct Wrapper { 19 | playerstats: PlayerStats, 20 | } 21 | 22 | #[derive(Debug, Clone, Deserialize, Serialize)] 23 | pub struct PlayerStats { 24 | pub error: Option, 25 | pub success: bool, 26 | #[serde(rename = "steamID")] 27 | // TODO: Make this SteamId 28 | pub steam_id: Option, 29 | #[serde(rename = "gameName")] 30 | pub game_name: Option, 31 | pub achievements: Option>, 32 | } 33 | 34 | #[derive(Debug, Clone, Deserialize, Serialize)] 35 | pub struct Achievement { 36 | pub apiname: Option, 37 | pub achieved: u8, 38 | // TODO: Reconsider this 39 | pub unlocktime: Option, 40 | } 41 | 42 | impl Steam { 43 | /// Gets the list of achievements the specified user has unlocked in an app. 44 | /// 45 | /// # Arguments 46 | /// * `steamid` - The user's SteamID. 47 | /// * `appid` - The ID of the application (game) to get achievements for. 48 | /// * `language` - Localized language to return (english, french, etc.). 49 | pub async fn get_player_achievements( 50 | &self, 51 | steamid: SteamId, 52 | appid: u32, 53 | language: Option<&str>, 54 | ) -> Result { 55 | let key = &self.api_key.clone(); 56 | let steamid = steamid.into_u64(); 57 | let args = gen_args!(key, appid, steamid) + &optional_argument!(language, "l"); 58 | let url = format!("{BASE}/{INTERFACE}/{ENDPOINT}/v{VERSION}/?{args}"); 59 | let wrapper = do_http!( 60 | url, 61 | Wrapper, 62 | ErrorHandle, 63 | SteamUserStatsError::GetPlayerAchievements 64 | ); 65 | Ok(wrapper.playerstats) 66 | } 67 | } 68 | 69 | impl Achievement { 70 | pub fn achieved(&self) -> bool { 71 | self.achieved == 1 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/steam_user_stats/get_schema_for_game.rs: -------------------------------------------------------------------------------- 1 | //! Implements the `GetSchemaForGame` endpoint 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use crate::{ 6 | errors::{ErrorHandle, SteamUserStatsError}, 7 | macros::{do_http, gen_args, optional_argument}, 8 | Steam, BASE, 9 | }; 10 | 11 | use super::INTERFACE; 12 | 13 | const ENDPOINT: &str = "GetSchemaForGame"; 14 | const VERSION: &str = "2"; 15 | 16 | #[derive(Deserialize, Serialize, Debug)] 17 | struct Response { 18 | game: Game, 19 | } 20 | 21 | #[derive(Deserialize, Serialize, Debug, Clone)] 22 | pub struct Game { 23 | #[serde(rename = "gameName")] 24 | pub game_name: String, 25 | #[serde(rename = "gameVersion")] 26 | pub game_version: String, 27 | #[serde(rename = "availableGameStats")] 28 | pub available_game_stats: AvailableGameStats, 29 | } 30 | 31 | #[derive(Deserialize, Serialize, Debug, Clone)] 32 | pub struct AvailableGameStats { 33 | pub stats: Vec, 34 | pub achievements: Vec, 35 | } 36 | 37 | #[derive(Deserialize, Serialize, Debug, Clone)] 38 | pub struct Stat { 39 | pub name: String, 40 | #[serde(rename = "defaultvalue")] 41 | pub default_value: i32, 42 | #[serde(rename = "displayName")] 43 | pub display_name: String, 44 | } 45 | 46 | #[derive(Deserialize, Serialize, Debug, Clone)] 47 | pub struct Achievement { 48 | pub name: String, 49 | #[serde(rename = "defaultvalue")] 50 | pub default_value: i32, 51 | #[serde(rename = "displayName")] 52 | pub display_name: String, 53 | pub hidden: i8, 54 | pub description: String, 55 | pub icon: String, 56 | #[serde(rename = "icongray")] 57 | pub icon_gray: String, 58 | } 59 | 60 | impl Steam { 61 | /// Gets the complete list of stats and achievements for the specified game. 62 | /// 63 | /// # Arguments 64 | /// 65 | /// * `appid` - The ID of the application (game) for which to retrieve the number of current players. 66 | /// * `language` - Localized language to return (english, french, etc.). 67 | pub async fn get_schema_for_game( 68 | &self, 69 | appid: u32, 70 | language: Option<&str>, 71 | ) -> Result { 72 | let key = &self.api_key; 73 | let query = format!( 74 | "?key={}{}{}", 75 | key, 76 | gen_args!(appid), 77 | optional_argument!(language, "l") 78 | ); 79 | let url = format!("{}/{}/{}/v{}/{}", BASE, INTERFACE, ENDPOINT, VERSION, query); 80 | let response = do_http!( 81 | url, 82 | Response, 83 | ErrorHandle, 84 | SteamUserStatsError::GetSchemaForGame 85 | ); 86 | Ok(response.game) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/steam_user_stats/get_user_stats_for_game.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use crate::{ 6 | errors::{ErrorHandle, SteamUserStatsError}, 7 | macros::{do_http, gen_args}, 8 | steam_id::SteamId, 9 | Steam, 10 | }; 11 | 12 | const END_POINT: &str = "https://api.steampowered.com/ISteamUserStats/GetUserStatsForGame/v1"; 13 | 14 | #[derive(Debug, Clone, Deserialize, Serialize)] 15 | pub enum UserGameStats { 16 | #[serde(rename = "playerstats")] 17 | PlayerStats { 18 | #[serde(rename = "steamID")] 19 | /// TODO: Make this SteamId 20 | steam_id: String, 21 | #[serde(rename = "gameName")] 22 | game_name: String, 23 | achievements: HashMap, 24 | stats: HashMap, 25 | }, 26 | } 27 | 28 | #[derive(Debug, Clone, Deserialize, Serialize)] 29 | pub struct Achievement { 30 | pub apiname: Option, 31 | pub achieved: u8, 32 | /// TODO: Reconsider this 33 | pub unlocktime: Option, 34 | } 35 | 36 | #[derive(Debug, Clone, Deserialize, Serialize)] 37 | pub struct Stat { 38 | pub value: u64, 39 | } 40 | 41 | impl Steam { 42 | /// This end point gives a 1:1 output of the api, if you were to put the endpoint into a browser, expect the same layout 43 | /// ( carbon-copy ) 44 | pub async fn get_user_stats_for_game( 45 | &self, 46 | steamid: SteamId, 47 | appid: u32, 48 | ) -> Result { 49 | let key = &self.api_key.clone(); 50 | let steamid = steamid.into_u64(); 51 | let args = gen_args!(key, appid, steamid); 52 | let url = format!("{END_POINT}?{args}"); 53 | Ok(do_http!( 54 | url, 55 | UserGameStats, 56 | ErrorHandle, 57 | SteamUserStatsError::GetUserStatsForGame 58 | )) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/steam_user_stats/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Implements the `ISteamUserStats` interface 2 | //! 3 | //! Provides various statistics about Steam users and applications. 4 | //! 5 | //! **Note:** This implementation is incomplete! 6 | //! The following endpoints are currently unimplemented: 7 | //! 8 | //! - GetGlobalStatsForGame 9 | 10 | const INTERFACE: &str = "ISteamUserStats"; 11 | 12 | pub mod get_global_achievement_percentages_for_app; 13 | pub mod get_number_of_current_players; 14 | pub mod get_player_achievements; 15 | pub mod get_schema_for_game; 16 | pub mod get_user_stats_for_game; 17 | -------------------------------------------------------------------------------- /src/steam_webapi_util/get_server_info.rs: -------------------------------------------------------------------------------- 1 | //! # Implements the `GetServerInfo` endpoint 2 | 3 | use serde::{Deserialize, Serialize}; 4 | use serde_json::Value; 5 | 6 | use crate::{ 7 | errors::{ErrorHandle, SteamWebAPIUtilError}, 8 | macros::do_http, 9 | Steam, BASE, 10 | }; 11 | 12 | use super::INTERFACE; 13 | 14 | const ENDPOINT: &str = "GetServerInfo"; 15 | const VERSION: &str = "1"; 16 | 17 | #[derive(Debug, Deserialize, Serialize, Clone)] 18 | pub struct ServerInfo { 19 | /// Returns Unix timestamp of WebAPI server time. 20 | #[serde(rename = "servertime")] 21 | pub server_time: u32, 22 | 23 | /// Returns time string of WebAPI server time. 24 | #[serde(rename = "servertimestring")] 25 | pub server_time_string: String, 26 | } 27 | 28 | impl Steam { 29 | /// Returns WebAPI server time & checks server status. 30 | pub async fn get_server_info() -> Result { 31 | let url = format!("{}/{}/{}/v{}/", BASE, INTERFACE, ENDPOINT, VERSION); 32 | let json = do_http!(url, Value, ErrorHandle, SteamWebAPIUtilError::GetServerInfo); 33 | let server_info: ServerInfo = ErrorHandle!( 34 | serde_json::from_value(json.to_owned()), 35 | SteamWebAPIUtilError::GetServerInfo 36 | ); 37 | Ok(server_info) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/steam_webapi_util/get_supported_api_list.rs: -------------------------------------------------------------------------------- 1 | //! # Implements the `GetSupportedAPIList` endpoint 2 | 3 | use serde::{Deserialize, Serialize}; 4 | use serde_json::Value; 5 | 6 | use crate::{ 7 | errors::{ErrorHandle, SteamWebAPIUtilError}, 8 | macros::do_http, 9 | Steam, BASE, 10 | }; 11 | 12 | use super::INTERFACE; 13 | 14 | const ENDPOINT: &str = "GetSupportedAPIList"; 15 | const VERSION: &str = "1"; 16 | 17 | /// Represents a parameter for an WebAPI method. 18 | #[derive(Debug, Deserialize, Serialize, Clone)] 19 | pub struct Parameter { 20 | /// Name of the parameter. 21 | pub name: String, 22 | 23 | /// Expected type of value. 24 | pub r#type: String, 25 | 26 | /// If input is optional for the method, then true. 27 | pub optional: bool, 28 | 29 | /// API documentation of parameter. 30 | pub description: Option, 31 | } 32 | 33 | /// Represents a method within an API interface. 34 | #[derive(Debug, Deserialize, Serialize, Clone)] 35 | pub struct Method { 36 | /// Name of the method. 37 | pub name: String, 38 | 39 | /// Version of the method. 40 | pub version: u8, 41 | 42 | /// Allowed HTTP method for method (GET, POST). 43 | #[serde(rename = "httpmethod")] 44 | pub http_method: String, 45 | 46 | /// Parameters for the method. 47 | pub parameters: Option>, 48 | } 49 | 50 | /// Represents an API interface 51 | #[derive(Debug, Deserialize, Serialize, Clone)] 52 | pub struct Interface { 53 | /// Name of the interface. 54 | pub name: String, 55 | 56 | /// Methods within the interface. 57 | pub methods: Vec, 58 | } 59 | 60 | /// List of WebAPI interfaces 61 | #[derive(Debug, Deserialize, Serialize, Clone)] 62 | pub struct APIList { 63 | /// Vector of API interfaces 64 | pub interfaces: Vec, 65 | } 66 | 67 | #[derive(Debug, Deserialize, Serialize)] 68 | struct Wrapper { 69 | apilist: APIList, 70 | } 71 | 72 | impl Steam { 73 | /// Lists all supported API calls. 74 | pub async fn get_supported_api_list(&self) -> Result { 75 | let url = format!( 76 | "{}/{}/{}/v{}/?key={}", 77 | BASE, INTERFACE, ENDPOINT, VERSION, &self.api_key 78 | ); 79 | let json = do_http!(url, Value, ErrorHandle, SteamWebAPIUtilError::GetServerInfo); 80 | let wrapper: Wrapper = ErrorHandle!( 81 | serde_json::from_value(json.to_owned()), 82 | SteamWebAPIUtilError::GetServerInfo 83 | ); 84 | Ok(wrapper.apilist) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/steam_webapi_util/mod.rs: -------------------------------------------------------------------------------- 1 | //! # Implements the `ISteamWebAPIUtil` interface 2 | //! 3 | //! Provides miscellaneous Web API related functionality through utility methods. 4 | //! 5 | //! For more info on how to use the Steamworks Web API please see the [Web API Overview](https://partner.steamgames.com/doc/webapi_overview). 6 | 7 | const INTERFACE: &str = "ISteamWebAPIUtil"; 8 | 9 | pub mod get_server_info; 10 | pub mod get_supported_api_list; 11 | -------------------------------------------------------------------------------- /tests/common/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | /// DO NOT USE THIS MACRO!! USE `#[tokio::test]` INSTEAD! 3 | macro_rules! async_test { 4 | ($e:expr) => { 5 | tokio_test::block_on($e) 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /tests/econ_service.rs: -------------------------------------------------------------------------------- 1 | use steam_rs::Steam; 2 | mod common; 3 | 4 | #[tokio::test] 5 | pub async fn get_trade_history() { 6 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 7 | steam 8 | .get_trade_history(10, 0, 0, false, true, "english", true, true) 9 | .await 10 | .unwrap(); 11 | } 12 | 13 | #[tokio::test] 14 | pub async fn get_trade_offers() { 15 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 16 | steam 17 | .get_trade_offers(true, true, true, "english", false, false, 10000) 18 | .await 19 | .unwrap(); 20 | } 21 | 22 | /// This Test is inhertintly flawed because trade offers expire after 2 weeks. 23 | /// See: https://help.steampowered.com/en/faqs/view/1115-91C5-050C-1D60 24 | #[tokio::test] 25 | #[ignore = "Test is flawed. See: https://help.steampowered.com/en/faqs/view/1115-91C5-050C-1D60"] 26 | pub async fn get_trade_offer() { 27 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 28 | steam.get_trade_offer(6271878669, "english").await.unwrap(); 29 | } 30 | 31 | #[tokio::test] 32 | pub async fn get_trade_offers_summary() { 33 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 34 | steam.get_trade_offers_summary(0).await.unwrap(); 35 | } 36 | -------------------------------------------------------------------------------- /tests/player_service.rs: -------------------------------------------------------------------------------- 1 | use steam_rs::{steam_id::SteamId, Steam}; 2 | mod common; 3 | 4 | const EXAMPLE_STEAM_ID: SteamId = SteamId(76561197960434622); // Al Farnsworth 5 | 6 | #[tokio::test] 7 | /// In wrong Test file!! 8 | /// TODO: Move this 9 | pub async fn get_badges() { 10 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 11 | println!("{:?}", steam.get_badges(EXAMPLE_STEAM_ID).await.unwrap()); 12 | } 13 | 14 | #[tokio::test] 15 | pub async fn get_community_badge_progress() { 16 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 17 | println!( 18 | "{:?}", 19 | steam 20 | .get_community_badge_progress(EXAMPLE_STEAM_ID, None) 21 | .await 22 | .unwrap() 23 | ); 24 | } 25 | 26 | #[tokio::test] 27 | pub async fn get_owned_games() { 28 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 29 | println!( 30 | "{:?}", 31 | steam 32 | .get_owned_games( 33 | EXAMPLE_STEAM_ID, 34 | true, 35 | true, 36 | 440, 37 | true, 38 | None, 39 | "english", 40 | true 41 | ) 42 | .await 43 | .unwrap() 44 | ); 45 | } 46 | 47 | #[tokio::test] 48 | pub async fn get_recently_played_games() { 49 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 50 | println!( 51 | "{:?}", 52 | steam 53 | .get_recently_played_games(EXAMPLE_STEAM_ID, Some(2)) 54 | .await 55 | .unwrap() 56 | ); 57 | } 58 | 59 | #[tokio::test] 60 | pub async fn get_player_achievements() { 61 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 62 | println!( 63 | "{:?}", 64 | steam 65 | .get_player_achievements(EXAMPLE_STEAM_ID, 440, None) 66 | .await 67 | .unwrap() 68 | ); 69 | } 70 | 71 | #[tokio::test] 72 | pub async fn get_user_stats_for_game() { 73 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 74 | println!( 75 | "{:?}", 76 | steam 77 | .get_user_stats_for_game(EXAMPLE_STEAM_ID, 440) 78 | .await 79 | .unwrap() 80 | ); 81 | } 82 | 83 | // #[test] 84 | // TODO: Extensive testing for each argument 85 | // pub fn get_owned_game() { 86 | // async_test!(async { 87 | // let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 88 | // println!("{:?}", steam.get_owned_games(EXAMPLE_STEAM_ID, true, true, 440, true, None, "english", true).await.unwrap()); 89 | // }); 90 | // } 91 | -------------------------------------------------------------------------------- /tests/published_file_service.rs: -------------------------------------------------------------------------------- 1 | use steam_rs::{ 2 | published_file_service::query_files::{ 3 | PublishedFileInfoMatchingFileType, PublishedFileQueryType, 4 | }, 5 | Steam, 6 | }; 7 | 8 | mod common; 9 | 10 | const EXAMPLE_APP_ID: u32 = 440; // Team Fortress 2 11 | 12 | #[tokio::test] 13 | pub async fn query_files() { 14 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 15 | let query = steam 16 | .query_files( 17 | PublishedFileQueryType::RankedByVote, 18 | 0, 19 | "*", 20 | Some(5), 21 | EXAMPLE_APP_ID, 22 | EXAMPLE_APP_ID, 23 | "", 24 | "", 25 | None, 26 | "", 27 | "", 28 | "", 29 | PublishedFileInfoMatchingFileType::Items, 30 | 0, 31 | 7, 32 | false, 33 | None, 34 | None, 35 | "", 36 | false, 37 | false, 38 | true, 39 | true, 40 | true, 41 | true, 42 | true, 43 | true, 44 | true, 45 | Some(true), 46 | 10, 47 | ) 48 | .await 49 | .unwrap(); 50 | println!("{:?}", query); 51 | } 52 | -------------------------------------------------------------------------------- /tests/steam_apps.rs: -------------------------------------------------------------------------------- 1 | use steam_rs::Steam; 2 | mod common; 3 | 4 | #[tokio::test] 5 | pub async fn get_app_list() { 6 | println!("{:?}", Steam::get_app_list().await.unwrap()); 7 | } 8 | 9 | #[tokio::test] 10 | pub async fn get_servers_at_address() { 11 | println!( 12 | "{:?}", 13 | Steam::get_servers_at_address("158.255.2.173:27016") 14 | .await 15 | .unwrap() 16 | ); 17 | } 18 | 19 | #[tokio::test] 20 | pub async fn up_to_date_check() { 21 | println!("{:?}", Steam::up_to_date_check(440, 8227024).await.unwrap()); 22 | } 23 | -------------------------------------------------------------------------------- /tests/steam_economy.rs: -------------------------------------------------------------------------------- 1 | use steam_rs::Steam; 2 | mod common; 3 | 4 | #[tokio::test] 5 | pub async fn get_asset_class_info() { 6 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 7 | 8 | let data = steam 9 | .get_asset_class_info(440, None, 1, 211447708, None) 10 | .await 11 | .unwrap(); 12 | println!("{data:?}") 13 | } 14 | 15 | #[tokio::test] 16 | pub async fn get_asset_prices() { 17 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 18 | 19 | let tf2_data = steam.get_asset_prices(440, None, None).await.unwrap(); 20 | println!("{tf2_data:?}"); 21 | 22 | let cs2_data = steam 23 | .get_asset_prices(730, None, None) 24 | .await 25 | .unwrap() 26 | .assets; 27 | 28 | println!("{cs2_data:?}"); 29 | } 30 | -------------------------------------------------------------------------------- /tests/steam_id.rs: -------------------------------------------------------------------------------- 1 | use steam_rs::steam_id::SteamId; 2 | 3 | mod common; 4 | 5 | const EXAMPLE_STEAM_ID64: u64 = 76561198307851839; // m1nt_ 6 | 7 | #[tokio::test] 8 | pub async fn new() { 9 | println!("{:?}", SteamId::new(EXAMPLE_STEAM_ID64)); 10 | } 11 | 12 | #[tokio::test] 13 | pub async fn into_u64() { 14 | println!("{:?}", SteamId::new(EXAMPLE_STEAM_ID64).into_u64()); 15 | } 16 | 17 | #[tokio::test] 18 | pub async fn get_account_id() { 19 | println!("{:?}", SteamId::new(EXAMPLE_STEAM_ID64).get_account_id()); 20 | } 21 | 22 | #[tokio::test] 23 | pub async fn get_universe() { 24 | println!("{:?}", SteamId::new(EXAMPLE_STEAM_ID64).get_universe()); 25 | } 26 | 27 | #[tokio::test] 28 | pub async fn get_account_type() { 29 | println!("{:?}", SteamId::new(EXAMPLE_STEAM_ID64).get_account_type()); 30 | } 31 | 32 | #[tokio::test] 33 | pub async fn to_id2_string() { 34 | println!("{:?}", SteamId::new(EXAMPLE_STEAM_ID64).to_id2_string()); 35 | } 36 | -------------------------------------------------------------------------------- /tests/steam_news.rs: -------------------------------------------------------------------------------- 1 | use steam_rs::Steam; 2 | mod common; 3 | 4 | const EXAMPLE_APP_ID: u32 = 440; // Team Fortress 2 5 | 6 | #[tokio::test] 7 | pub async fn get_news() { 8 | // let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 9 | println!( 10 | "{:?}", 11 | Steam::get_news_for_app(EXAMPLE_APP_ID, Some(200), None, Some(10), None) 12 | .await 13 | .unwrap() 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /tests/steam_remote_storage.rs: -------------------------------------------------------------------------------- 1 | use steam_rs::Steam; 2 | mod common; 3 | 4 | const EXAMPLE_COLLECTION: u64 = 532551393; 5 | 6 | #[tokio::test] 7 | pub async fn get_collection_details() { 8 | println!( 9 | "{:?}", 10 | Steam::get_collection_details(&[EXAMPLE_COLLECTION]) 11 | .await 12 | .unwrap() 13 | ); 14 | } 15 | 16 | #[tokio::test] 17 | pub async fn get_published_file() { 18 | println!( 19 | "{:?}", 20 | Steam::get_published_file(&[EXAMPLE_COLLECTION]) 21 | .await 22 | .unwrap() 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /tests/steam_user.rs: -------------------------------------------------------------------------------- 1 | use steam_rs::{steam_id::SteamId, Steam}; 2 | 3 | mod common; 4 | 5 | const EXAMPLE_STEAM_ID: SteamId = SteamId(76561197960435530); // Robin Walker 6 | const EXAMPLE_VANITY_URLS: [&str; 2] = [ 7 | "gabelogannewell", // Represents a working vanity URL 8 | "!@#$%^&*()", // Represents a broken vanity URL that would fail 9 | ]; 10 | 11 | #[tokio::test] 12 | pub async fn get_friend_list() { 13 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 14 | println!( 15 | "{:?}", 16 | steam.get_friend_list(EXAMPLE_STEAM_ID, None).await.unwrap() 17 | ); 18 | } 19 | 20 | #[tokio::test] 21 | pub async fn get_player_bans() { 22 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 23 | println!( 24 | "{:?}", 25 | steam.get_player_bans(vec![EXAMPLE_STEAM_ID]).await.unwrap() 26 | ); 27 | } 28 | 29 | #[tokio::test] 30 | pub async fn get_player_summaries() { 31 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 32 | println!( 33 | "{:?}", 34 | steam 35 | .get_player_summaries(vec![EXAMPLE_STEAM_ID]) 36 | .await 37 | .unwrap() 38 | ); 39 | } 40 | 41 | #[tokio::test] 42 | pub async fn get_user_group_list() { 43 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 44 | println!( 45 | "{:?}", 46 | steam.get_user_group_list(EXAMPLE_STEAM_ID).await.unwrap() 47 | ); 48 | } 49 | 50 | #[tokio::test] 51 | pub async fn resolve_vanity_url() { 52 | for url in EXAMPLE_VANITY_URLS { 53 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 54 | println!( 55 | "{:?}", 56 | steam.resolve_vanity_url(url, Some(1)).await.unwrap() 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/steam_user_auth.rs: -------------------------------------------------------------------------------- 1 | use steam_rs::Steam; 2 | 3 | mod common; 4 | 5 | #[tokio::test] 6 | pub async fn authenticate_user_ticket() { 7 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 8 | let app_id = 12900; // Audiosurf 9 | let ticket = &std::env::var("STEAM_GAME_TICKET").unwrap_or(String::new()); 10 | 11 | if !ticket.is_empty() { 12 | assert!(steam 13 | .authenticate_user_ticket(app_id, ticket,) 14 | .await 15 | .is_ok()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/steam_user_stats.rs: -------------------------------------------------------------------------------- 1 | use steam_rs::{steam_id::SteamId, Steam}; 2 | mod common; 3 | 4 | const EXAMPLE_APP_ID: u32 = 440; // Team Fortress 2 5 | const EXAMPLE_STEAM_ID_PUBLIC: SteamId = SteamId(76561198136162943); // Garrett Howard 6 | const EXAMPLE_STEAM_ID_PRIVATE: SteamId = SteamId(76561197960435530); // Robin Walker 7 | 8 | #[tokio::test] 9 | pub async fn get_global_achievement_percentages_for_app() { 10 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 11 | assert!(steam 12 | .get_global_achievement_percentages_for_app(EXAMPLE_APP_ID) 13 | .await 14 | .is_ok()); 15 | } 16 | 17 | #[tokio::test] 18 | pub async fn get_number_of_current_players() { 19 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 20 | 21 | // Expected result 22 | assert!(steam 23 | .get_number_of_current_players(EXAMPLE_APP_ID) 24 | .await 25 | .is_ok()); 26 | 27 | // Error condition (nonexistent app) 28 | assert!(steam.get_number_of_current_players(1).await.is_err()); 29 | } 30 | 31 | #[tokio::test] 32 | pub async fn get_player_achievements() { 33 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 34 | 35 | // Expected result (public profile) 36 | assert!(steam 37 | .get_player_achievements(EXAMPLE_STEAM_ID_PUBLIC, EXAMPLE_APP_ID, None) 38 | .await 39 | .is_ok()); 40 | 41 | // Error condition (private profile) 42 | assert!(steam 43 | .get_player_achievements(EXAMPLE_STEAM_ID_PRIVATE, EXAMPLE_APP_ID, None) 44 | .await 45 | .is_err()); 46 | } 47 | 48 | #[tokio::test] 49 | pub async fn get_schema_for_game() { 50 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 51 | 52 | assert!(steam 53 | .get_schema_for_game(EXAMPLE_APP_ID, None) 54 | .await 55 | .is_ok()); 56 | } 57 | -------------------------------------------------------------------------------- /tests/steam_webapi_util.rs: -------------------------------------------------------------------------------- 1 | use steam_rs::Steam; 2 | 3 | mod common; 4 | 5 | #[tokio::test] 6 | pub async fn get_server_info() { 7 | println!("{:?}", Steam::get_server_info().await.unwrap()); 8 | } 9 | 10 | #[tokio::test] 11 | pub async fn get_supported_api_list() { 12 | let steam = Steam::new(&std::env::var("STEAM_API_KEY").expect("Missing an API key")); 13 | println!("{:?}", steam.get_supported_api_list().await.unwrap()); 14 | } 15 | --------------------------------------------------------------------------------