steam_webapi_rust_sdk/lib.rs
1//! # Steam Web API Rust SDK
2//!
3//! `steam-webapi-rust-sdk` is a set of utility functions to access Steam Web API.
4//!
5//! In order to use this library make sure to set STEAM_WEB_API_KEY system environment variable.
6//!
7//! The library itself tries to minimize number of networks calls through the caching relevant
8//! responses to the 'steam-webapi-cache' folder.
9//!
10//! There is already prebuilt cache for all steam apps, in order to use it,
11//! simply clone [steam-webapi-cache](https://github.com/bohdaq/steam-webapi-cache)
12//! into the root folder of your project.
13
14use crate::idota2match_570::get_heroes::Hero;
15use crate::idota2match_570::get_league_listing::League;
16use crate::idota2match_570::get_live_league_games::LiveLeagueGame;
17use crate::idota2match_570::get_match_details::MatchResult;
18use crate::idota2match_570::get_match_history::ResponseMatchHistory;
19use crate::idota2match_570::get_team_info_by_team_id::TeamInfo;
20use crate::isteam_apps::get_app_list::SteamApp;
21use crate::isteam_news::get_news_for_app::NewsItem;
22use crate::isteam_user::get_friend_list::Friend;
23use crate::isteam_user::get_player_bans::PlayerBans;
24use crate::isteam_user::get_player_summaries::PlayerSummary;
25use crate::isteam_user::resolve_vanity_url::VanityUrlResolution;
26use crate::isteam_user_stats::get_global_achievement_percentages_for_app::AchievementPercentage;
27use crate::isteam_user_stats::get_player_achievements::PlayerAchievements;
28use crate::isteam_user_stats::get_schema_for_game::GameSchema;
29use crate::isteam_user_stats::get_user_stats_for_game::UserStatsForGame;
30use crate::iplayer_service::get_badges::Badges;
31use crate::iplayer_service::get_owned_games::OwnedGames;
32use crate::iplayer_service::get_recently_played_games::RecentlyPlayedGames;
33use crate::store_steampowered_com::appdetails::SteamAppDetails;
34
35pub mod util;
36pub mod isteam_apps;
37pub mod isteam_user;
38pub mod isteam_user_stats;
39pub mod isteam_news;
40pub mod iplayer_service;
41pub mod store_steampowered_com;
42pub mod idota2match_570;
43
44#[cfg(test)]
45mod tests;
46
47/// Retrieves details for the given app id from the local cache. It may return an error
48/// if requested resource is absent, malformed or not readable from local cache.
49///
50/// # Examples
51///
52/// ```
53/// let app_id = 570;
54/// let boxed_result = steam_webapi_rust_sdk::get_cached_app_details(app_id);
55/// if boxed_result.is_ok() {
56/// let app_details = boxed_result.unwrap();
57/// println!("result is ok for {} app id {}", app_details.name, app_details.app_id);
58///
59/// } else {
60/// let error_message = boxed_result.err().unwrap();
61/// println!("{} {}", error_message, app_id);
62///
63/// };
64/// ```
65pub fn get_cached_app_details(app_id: i64) -> Result<SteamAppDetails, String> {
66 let boxed_result = store_steampowered_com::appdetails::get_cached(app_id);
67 boxed_result
68}
69
70/// Retrieves details for the given app id. It will make an API call to Steam and cache response.
71/// It may return an error if API responded with error response. As an example it may be exceeding
72/// the limit of calls from one IP address or if the response contains not valid UTF-8 characters.
73/// Usually Steam API allows 200 requests from single IP address within 5 minutes range.
74///
75/// # Examples
76///
77/// ```
78/// let app_id = 570;
79/// let boxed_result = steam_webapi_rust_sdk::get_app_details(app_id);
80/// if boxed_result.is_ok() {
81/// let app_details = boxed_result.unwrap();
82/// println!("result is ok for {} app id {}", app_details.name, app_details.app_id);
83///
84/// } else {
85/// let error_message = boxed_result.err().unwrap();
86/// println!("{} {}", error_message, app_id);
87///
88/// let is_steam_unsuccessful_response = error_message == "steampowered api returned failed response";
89/// let is_invalid_utf8_sequence = error_message == "invalid utf-8 sequence";
90/// let no_response_from_api = error_message == "no response from API";
91/// let exceeded_api_calls_limit = (!is_steam_unsuccessful_response && !is_invalid_utf8_sequence) || no_response_from_api;
92///
93/// // you can do a retry or continue execution...
94/// };
95/// ```
96pub fn get_app_details(app_id: i64) -> Result<SteamAppDetails, String> {
97 let boxed_result = store_steampowered_com::appdetails::get(app_id);
98 boxed_result
99}
100
101/// Retrieves list of apps available on Steam. Each item consists of 2 fields: appid and name
102///
103/// # Examples
104///
105/// ```
106/// let steam_app_list = steam_webapi_rust_sdk::get_app_list().unwrap();
107///
108/// assert!(steam_app_list.len()>0);
109/// let steam_app = steam_app_list.get(0).unwrap();
110/// assert!(steam_app.appid > 0);
111///
112/// assert!(steam_app.name.len() > 0);
113/// ```
114pub fn get_app_list() -> Result<Vec<SteamApp>, String> {
115 let boxed_result = isteam_apps::get_app_list::get();
116 boxed_result
117}
118
119
120/// Retrieves list of apps available on Steam. First tries to get it from local cache.
121/// Each item consists of 2 fields: appid and name
122///
123/// # Examples
124///
125/// ```
126/// let steam_app_list = steam_webapi_rust_sdk::get_cached_app_list().unwrap();
127///
128/// assert!(steam_app_list.len()>0);
129/// let steam_app = steam_app_list.get(0).unwrap();
130/// assert!(steam_app.appid > 0);
131///
132/// assert!(steam_app.name.len() > 0);
133/// ```
134pub fn get_cached_app_list() -> Result<Vec<SteamApp>, String> {
135 let boxed_result = isteam_apps::get_app_list::get_cached();
136 boxed_result
137}
138
139/// Retrieves list of matches from Dota2
140///
141/// # Examples
142///
143/// ```
144/// let boxed_dota2_match_list = steam_webapi_rust_sdk::get_dota2_match_history(
145/// Some(76561197960361544),
146/// None,
147/// None,
148/// None,
149/// None,
150/// None,
151/// None
152/// );
153///
154/// assert!(boxed_dota2_match_list.is_ok());
155/// if boxed_dota2_match_list.is_ok() {
156/// let dota2_match_list = boxed_dota2_match_list.unwrap();
157/// assert!(dota2_match_list.matches.len()>0);
158/// }
159///
160/// ```
161pub fn get_dota2_match_history(account_id: Option<i64>,
162 game_mode: Option<u8>,
163 skill: Option<u8>,
164 min_players: Option<u32>,
165 start_at_match_id: Option<i64>,
166 matches_requested: Option<u32>,
167 tournament_games_only: Option<bool>)
168 -> Result<ResponseMatchHistory, String> {
169 idota2match_570::get_dota2_match_history(
170 account_id,
171 game_mode,
172 skill,
173 min_players,
174 start_at_match_id,
175 matches_requested,
176 tournament_games_only
177 )
178
179}
180
181/// Retrieves match details for the given Dota2 match id. It will make an API call to Steam and
182/// cache the response.
183///
184/// # Examples
185///
186/// ```
187/// let match_id = 1461414523;
188/// let boxed_match_result = steam_webapi_rust_sdk::get_dota2_match_details(match_id);
189///
190/// assert!(boxed_match_result.is_ok());
191/// if boxed_match_result.is_ok() {
192/// let match_result = boxed_match_result.unwrap();
193/// assert_eq!(10, match_result.players.len());
194/// }
195/// ```
196pub fn get_dota2_match_details(match_id: u64) -> Result<MatchResult, String> {
197 idota2match_570::get_dota2_match_details(match_id)
198}
199
200/// Retrieves match details for the given Dota2 match id from the local cache. It may return an
201/// error if requested resource is absent, malformed or not readable from local cache.
202///
203/// # Examples
204///
205/// ```
206/// let match_id = 1461414523;
207/// let boxed_match_result = steam_webapi_rust_sdk::get_cached_dota2_match_details(match_id);
208/// if boxed_match_result.is_ok() {
209/// let match_result = boxed_match_result.unwrap();
210/// println!("result is ok for match id {}", match_result.match_id);
211///
212/// } else {
213/// let error_message = boxed_match_result.err().unwrap();
214/// println!("{} {}", error_message, match_id);
215///
216/// };
217/// ```
218pub fn get_cached_dota2_match_details(match_id: u64) -> Result<MatchResult, String> {
219 idota2match_570::get_cached_dota2_match_details(match_id)
220}
221
222/// Retrieves the list of all Dota2 heroes.
223///
224/// # Examples
225///
226/// ```no_run
227/// let boxed_heroes = steam_webapi_rust_sdk::get_dota2_heroes(Some("en".to_string()));
228/// assert!(boxed_heroes.is_ok());
229/// ```
230pub fn get_dota2_heroes(language: Option<String>) -> Result<Vec<Hero>, String> {
231 idota2match_570::get_dota2_heroes(language)
232}
233
234/// Retrieves the list of Dota2 leagues.
235///
236/// # Examples
237///
238/// ```no_run
239/// let boxed_leagues = steam_webapi_rust_sdk::get_dota2_league_listing(None);
240/// assert!(boxed_leagues.is_ok());
241/// ```
242pub fn get_dota2_league_listing(language: Option<String>) -> Result<Vec<League>, String> {
243 idota2match_570::get_dota2_league_listing(language)
244}
245
246/// Retrieves currently live Dota2 league games.
247///
248/// # Examples
249///
250/// ```no_run
251/// let boxed_games = steam_webapi_rust_sdk::get_dota2_live_league_games();
252/// assert!(boxed_games.is_ok());
253/// ```
254pub fn get_dota2_live_league_games() -> Result<Vec<LiveLeagueGame>, String> {
255 idota2match_570::get_dota2_live_league_games()
256}
257
258/// Retrieves Dota2 team info, paginated by team id.
259///
260/// # Examples
261///
262/// ```no_run
263/// let boxed_teams = steam_webapi_rust_sdk::get_dota2_team_info_by_team_id(Some(1), Some(100));
264/// assert!(boxed_teams.is_ok());
265/// ```
266pub fn get_dota2_team_info_by_team_id(start_at_team_id: Option<u64>, teams_requested: Option<u32>) -> Result<Vec<TeamInfo>, String> {
267 idota2match_570::get_dota2_team_info_by_team_id(start_at_team_id, teams_requested)
268}
269
270/// Retrieves public profile summaries (up to 100 SteamIDs per call) via `ISteamUser/GetPlayerSummaries`.
271///
272/// # Examples
273///
274/// ```no_run
275/// let boxed_summaries = steam_webapi_rust_sdk::get_player_summaries(vec![76561197960361544]);
276/// assert!(boxed_summaries.is_ok());
277/// ```
278pub fn get_player_summaries(steamids: Vec<u64>) -> Result<Vec<PlayerSummary>, String> {
279 isteam_user::get_player_summaries::get(steamids)
280}
281
282/// Retrieves a Steam account's friend list. Returns an error if the profile's friends list is
283/// not public.
284///
285/// # Examples
286///
287/// ```no_run
288/// let boxed_friends = steam_webapi_rust_sdk::get_friend_list(76561197960361544, None);
289/// assert!(boxed_friends.is_ok());
290/// ```
291pub fn get_friend_list(steamid: u64, relationship: Option<String>) -> Result<Vec<Friend>, String> {
292 isteam_user::get_friend_list::get(steamid, relationship)
293}
294
295/// Retrieves VAC/game/community ban status for up to 100 SteamIDs.
296///
297/// # Examples
298///
299/// ```no_run
300/// let boxed_bans = steam_webapi_rust_sdk::get_player_bans(vec![76561197960361544]);
301/// assert!(boxed_bans.is_ok());
302/// ```
303pub fn get_player_bans(steamids: Vec<u64>) -> Result<Vec<PlayerBans>, String> {
304 isteam_user::get_player_bans::get(steamids)
305}
306
307/// Resolves a Steam Community vanity URL (e.g. `steamcommunity.com/id/<vanity>`) to a 64-bit SteamID.
308///
309/// # Examples
310///
311/// ```no_run
312/// let boxed_resolution = steam_webapi_rust_sdk::resolve_vanity_url("gabelogannewell".to_string(), None);
313/// assert!(boxed_resolution.is_ok());
314/// ```
315pub fn resolve_vanity_url(vanity_url: String, url_type: Option<u8>) -> Result<VanityUrlResolution, String> {
316 isteam_user::resolve_vanity_url::get(vanity_url, url_type)
317}
318
319/// Retrieves the games a Steam account owns, and optionally their playtime.
320///
321/// # Examples
322///
323/// ```no_run
324/// let boxed_games = steam_webapi_rust_sdk::get_owned_games(76561197960361544, Some(true), Some(true));
325/// assert!(boxed_games.is_ok());
326/// ```
327pub fn get_owned_games(steamid: u64, include_appinfo: Option<bool>, include_played_free_games: Option<bool>) -> Result<OwnedGames, String> {
328 iplayer_service::get_owned_games::get(steamid, include_appinfo, include_played_free_games)
329}
330
331/// Retrieves games played by a Steam account in the last two weeks.
332///
333/// # Examples
334///
335/// ```no_run
336/// let boxed_games = steam_webapi_rust_sdk::get_recently_played_games(76561197960361544, None);
337/// assert!(boxed_games.is_ok());
338/// ```
339pub fn get_recently_played_games(steamid: u64, count: Option<u32>) -> Result<RecentlyPlayedGames, String> {
340 iplayer_service::get_recently_played_games::get(steamid, count)
341}
342
343/// Retrieves a Steam account's Steam level.
344///
345/// # Examples
346///
347/// ```no_run
348/// let boxed_level = steam_webapi_rust_sdk::get_steam_level(76561197960361544);
349/// assert!(boxed_level.is_ok());
350/// ```
351pub fn get_steam_level(steamid: u64) -> Result<u64, String> {
352 iplayer_service::get_steam_level::get(steamid)
353}
354
355/// Retrieves a Steam account's badges and badge XP progress.
356///
357/// # Examples
358///
359/// ```no_run
360/// let boxed_badges = steam_webapi_rust_sdk::get_badges(76561197960361544);
361/// assert!(boxed_badges.is_ok());
362/// ```
363pub fn get_badges(steamid: u64) -> Result<Badges, String> {
364 iplayer_service::get_badges::get(steamid)
365}
366
367/// Retrieves a Steam account's achievement status for a given app. Returns an error if the
368/// account's stats for the game aren't public.
369///
370/// # Examples
371///
372/// ```no_run
373/// let boxed_achievements = steam_webapi_rust_sdk::get_player_achievements(76561197960361544, 440, Some("english".to_string()));
374/// assert!(boxed_achievements.is_ok());
375/// ```
376pub fn get_player_achievements(steamid: u64, appid: i64, language: Option<String>) -> Result<PlayerAchievements, String> {
377 isteam_user_stats::get_player_achievements::get(steamid, appid, language)
378}
379
380/// Retrieves a Steam account's stats and achievements for a given app.
381///
382/// # Examples
383///
384/// ```no_run
385/// let boxed_stats = steam_webapi_rust_sdk::get_user_stats_for_game(76561197960361544, 440);
386/// assert!(boxed_stats.is_ok());
387/// ```
388pub fn get_user_stats_for_game(steamid: u64, appid: i64) -> Result<UserStatsForGame, String> {
389 isteam_user_stats::get_user_stats_for_game::get(steamid, appid)
390}
391
392/// Retrieves the stat and achievement schema (names, display names, descriptions) for a given app.
393///
394/// # Examples
395///
396/// ```no_run
397/// let boxed_schema = steam_webapi_rust_sdk::get_schema_for_game(440);
398/// assert!(boxed_schema.is_ok());
399/// ```
400pub fn get_schema_for_game(appid: i64) -> Result<GameSchema, String> {
401 isteam_user_stats::get_schema_for_game::get(appid)
402}
403
404/// Retrieves the global completion percentage for each achievement of a given app. Does not
405/// require a Steam Web API key.
406///
407/// # Examples
408///
409/// ```no_run
410/// let boxed_percentages = steam_webapi_rust_sdk::get_global_achievement_percentages_for_app(440);
411/// assert!(boxed_percentages.is_ok());
412/// ```
413pub fn get_global_achievement_percentages_for_app(appid: i64) -> Result<Vec<AchievementPercentage>, String> {
414 isteam_user_stats::get_global_achievement_percentages_for_app::get(appid)
415}
416
417/// Retrieves the current number of players in-game for a given app. Does not require a Steam
418/// Web API key.
419///
420/// # Examples
421///
422/// ```no_run
423/// let boxed_count = steam_webapi_rust_sdk::get_number_of_current_players(570);
424/// assert!(boxed_count.is_ok());
425/// ```
426pub fn get_number_of_current_players(appid: i64) -> Result<i64, String> {
427 isteam_user_stats::get_number_of_current_players::get(appid)
428}
429
430/// Retrieves news items for a given app. Does not require a Steam Web API key.
431///
432/// # Examples
433///
434/// ```no_run
435/// let boxed_news = steam_webapi_rust_sdk::get_news_for_app(440, Some(3), Some(300));
436/// assert!(boxed_news.is_ok());
437/// ```
438pub fn get_news_for_app(appid: i64, count: Option<u32>, maxlength: Option<u32>) -> Result<Vec<NewsItem>, String> {
439 isteam_news::get_news_for_app::get(appid, count, maxlength)
440}
441
442/// Converts given 32 bit Steam account id to 64 bit
443///
444/// # Examples
445///
446/// ```
447/// use steam_webapi_rust_sdk::convert_32bit_account_id_to_64bit;
448///
449/// let _32bit_id = 95816;
450/// let converted = convert_32bit_account_id_to_64bit(_32bit_id);
451///
452/// let expected_id = 76561197960361544;
453/// assert_eq!(expected_id, converted);
454///
455/// ```
456pub fn convert_32bit_account_id_to_64bit(account_id_32bit: i64) -> i64 {
457 let valves_magic_constant = 76561197960265728;
458 let mut converted_to_64_bit = account_id_32bit;
459 converted_to_64_bit += valves_magic_constant;
460 converted_to_64_bit
461}
462
463
464/// Converts given 64 bit Steam account id to 32 bit
465///
466/// # Examples
467///
468/// ```
469/// use steam_webapi_rust_sdk::convert_64bit_account_id_to_32bit;
470///
471/// let _64bit_id = 76561197960361544;
472/// let converted = convert_64bit_account_id_to_32bit(_64bit_id);
473///
474/// let expected_id = 95816;
475/// assert_eq!(expected_id, converted);
476///
477/// ```
478pub fn convert_64bit_account_id_to_32bit(account_id_32bit: i64) -> i64 {
479 let valves_magic_constant = 76561197960265728;
480 let mut converted_to_32_bit = account_id_32bit;
481 converted_to_32_bit -= valves_magic_constant;
482 converted_to_32_bit
483}
484
485
486pub(crate) fn get_scheme() -> String {
487 "https".to_string()
488}
489
490pub(crate) fn get_host() -> String {
491 let host = "api.steampowered.com".to_string();
492 host
493}
494
495pub(crate) fn make_api_call(url: String) -> Result<String, String> {
496
497 let boxed_response = minreq::get(url).send();
498 if boxed_response.is_err() {
499 return Err("Operation timed out (API call)".to_string());
500 }
501
502 let raw_response : Vec<u8> = boxed_response.unwrap().into_bytes();
503
504 let response_string_boxed = String::from_utf8(raw_response);
505 if response_string_boxed.is_err() {
506 let error_message = response_string_boxed.err().unwrap().to_string();
507 if error_message == "invalid utf-8 sequence of 1 bytes from index 1" {
508 return Err("no response from API".to_string());
509 }
510 return Err("invalid utf-8 sequence".to_string());
511 }
512 let response_string: String = response_string_boxed.unwrap();
513
514 Ok(response_string)
515}