steam_api/
lib.rs

1#![doc = include_str!("../README.md")]
2#![deny(clippy::pedantic)]
3
4pub mod functions;
5pub mod structs;
6
7use anyhow::Result;
8use log::{debug, info};
9use reqwest::{blocking, Error};
10use structs::{bans, friends, level, profile, summaries};
11
12/// Calls the `GetSteamLevel` API
13///
14/// # Returns
15/// Returns an [`anyhow::Result`] containing [`structs::level::User`] and an [`anyhow::Error`]
16///
17/// # Arguments
18/// * `steamids` - A string that contains a single Steam ID
19/// * `api_key` - A string slice containing the API Key to use with the API.
20///
21/// # Errors
22/// * [`reqwest::blocking::get`]
23///
24/// # Examples
25/// Single Steam ID
26/// ```
27/// # fn main() -> anyhow::Result<()> {
28/// let api_key = &std::env::var("API_KEY")?;
29/// let steamid: &str = "76561198421169032";
30///
31/// let user = steam_api::get_steam_level(&steamid, &api_key)?;
32///
33/// match user.player_level {
34///     Some(x) => println!("Player level\t{}", x),
35///     None => println!("The profile is private or has not set up a community profile yet"),
36/// }
37///
38/// # assert_eq!(user.player_level.unwrap_or_default(), 321);
39/// # Ok(())
40/// # }
41/// ```
42///
43/// Multiple Steam IDs
44/// `GetSteamLevel` does **NOT** support multiple steamids, blame Valve.
45pub fn get_steam_level(steamids: &str, api_key: &str) -> Result<level::User, Error> {
46    info!("Calling GetSteamLevel on steamid: {}", &steamids);
47
48    debug!(
49        "{}",
50        functions::manage_api_url("GetSteamLevel", steamids, api_key)
51    );
52
53    Ok(blocking::get(functions::manage_api_url(
54        "GetSteamLevel",
55        steamids,
56        api_key,
57    ))?
58    .json::<level::Response>()?
59    .response)
60}
61
62/// Calls the `GetPlayerSummaries` API
63///
64/// # Returns
65/// Returns an [`anyhow::Result`] containing a Vector of [`structs::summaries::User`] and [`anyhow::Error`]
66///
67/// # Arguments
68/// * `steamids` - A string that contains either a single Steam ID or a comma separated string of
69/// Steam IDs
70/// * `api_key` - A string slice containing the API Key to use with the API.
71///
72/// # Errors
73/// * [`reqwest::blocking::get`]
74///
75/// # Examples
76/// Single Steam ID
77/// ```
78/// # fn main() -> anyhow::Result<()> {
79/// let api_key = &std::env::var("API_KEY")?;
80/// let steamid: &str = "76561198421169032";
81///
82/// let user = steam_api::get_player_summaries(&steamid, &api_key)?;
83///
84/// println!("Steam ID\t{:?}", user[0].steamid);
85/// println!("Persona Name\t{:?}", user[0].personaname);
86///
87/// assert_eq!(user[0].personaname, "dind");
88/// assert_eq!(user[0].profileurl, "https://steamcommunity.com/id/ageofconsent/");
89/// # Ok(())
90/// # }
91/// ```
92/// Multiple Steam IDs
93/// ```
94/// # fn main() -> anyhow::Result<()> {
95/// let api_key = &std::env::var("API_KEY")?;
96/// let steamids: &str = "76561198421169032,76561198421169032";
97///
98/// let users = steam_api::get_player_summaries(&steamids, &api_key)?;
99///
100/// for i in users {
101///     println!("Persona Name\t{}", i.personaname);
102/// }
103/// # Ok(())
104/// # }
105/// ```
106pub fn get_player_summaries(steamids: &str, api_key: &str) -> Result<Vec<summaries::User>, Error> {
107    info!("Calling GetPlayerSummaries on steamids: {}", &steamids);
108
109    debug!(
110        "{}",
111        functions::manage_api_url("GetPlayerSummaries", steamids, api_key)
112    );
113
114    Ok(blocking::get(functions::manage_api_url(
115        "GetPlayerSummaries",
116        steamids,
117        api_key,
118    ))?
119    .json::<summaries::Response>()?
120    .response
121    .players)
122}
123
124/// Calls the `GetPlayerBans` API
125///
126/// # Returns
127/// Returns an [`anyhow::Result`] containing a Vector of [`structs::bans::User`] and [`anyhow::Error`]
128///
129/// # Arguments
130/// * `steamids` - A string that contains either a single Steam ID or a comma separated string of
131/// Steam IDs
132/// * `api_key` - A string slice containing the API Key to use with the API.
133///
134/// # Errors
135/// * [`reqwest::blocking::get`]
136///
137/// # Examples
138/// Single Steam ID
139/// ```
140/// # fn main() -> anyhow::Result<()> {
141/// let api_key = &std::env::var("API_KEY")?;
142/// let steamid: &str = "76561198421169032";
143///
144/// let user = steam_api::get_player_bans(&steamid, &api_key)?;
145///
146/// println!("{}", user[0].SteamId);
147/// println!("{}", user[0].VACBanned);
148///
149/// assert_eq!(user[0].SteamId, "76561198421169032");
150/// assert!(!user[0].VACBanned);
151/// # Ok(())
152/// # }
153/// ```
154/// Multiple Steam IDs
155/// ```
156/// # fn main() -> anyhow::Result<()> {
157/// let api_key = &std::env::var("API_KEY")?;
158/// let steamids: &str = "76561198421169032,76561198421169032";
159///
160/// let user = steam_api::get_player_bans(&steamids, &api_key)?;
161///
162/// for i in user {
163///     println!("Steam ID\t{}", i.SteamId);
164///     println!("VAC Banned\t{}", i.VACBanned);
165/// }
166/// # Ok(())
167/// # }
168/// ```
169pub fn get_player_bans(steamids: &str, api_key: &str) -> Result<Vec<bans::User>, Error> {
170    info!("Calling GetPlayerBans on steamids: {}", &steamids);
171
172    debug!(
173        "{}",
174        functions::manage_api_url("GetPlayerBans", steamids, api_key)
175    );
176
177    Ok(blocking::get(functions::manage_api_url(
178        "GetPlayerBans",
179        steamids,
180        api_key,
181    ))?
182    .json::<bans::Response>()?
183    .players)
184}
185
186/// Calls the `GetFriendList` API
187///
188/// # Returns
189/// Returns an [`anyhow::Result`] containing a Vector of [`structs::friends::User`] and [`anyhow::Error`]
190///
191/// # Arguments
192/// * `steamids` - A string that contains a single Steam ID
193/// * `api_key` - A string slice containing the API Key to use with the API.
194///
195/// # Errors
196/// * [`reqwest::blocking::get`]
197///
198/// # Examples
199/// Single Steam ID
200/// ```
201/// # fn main() -> anyhow::Result<()> {
202/// let api_key = &std::env::var("API_KEY")?;
203/// let steamid: &str = "76561198421169032";
204///
205/// let user = steam_api::get_friends_list(&steamid, &api_key)?;
206///
207/// for i in user {
208///     println!("Steam ID\t{}", i.steamid);
209///     println!("Friends since\t{}", i.friend_since);
210/// }
211/// # Ok(())
212/// # }
213/// ```
214/// Multiple Steam IDs
215///
216/// `GetFriendList` does **NOT** support multiple steamids, blame Valve.
217pub fn get_friends_list(steamids: &str, api_key: &str) -> Result<Vec<friends::User>, Error> {
218    info!("Calling GetFriendList on steamid: {}", &steamids);
219
220    debug!(
221        "{}",
222        functions::manage_api_url("GetFriendList", steamids, api_key)
223    );
224
225    Ok(blocking::get(functions::manage_api_url(
226        "GetFriendList",
227        steamids,
228        api_key,
229    ))?
230    .json::<friends::Response>()?
231    .friendslist
232    .friends)
233}
234
235/// Calls all the APIs builds the [`structs::profile::Users`] struct
236///
237/// # Returns
238/// Returns an `anyhow::Result` containing [`structs::profile::Users`] and [`anyhow::Error`]
239///
240/// # Arguments
241/// * `steamids` - A Vector of `str`s
242/// * `api_key` - A string slice containing the API Key to use with the API.
243///
244/// # Errors
245/// * [`get_friends_list`]
246/// * [`get_player_bans`]
247/// * [`get_player_summaries`]
248/// * [`get_steam_level`]
249///
250/// # Examples
251/// Single Steam ID
252/// ```
253/// # fn main() -> anyhow::Result<()> {
254/// let api_key = &std::env::var("API_KEY")?;
255/// let steamids = vec![
256///     "76561198421169032",
257/// ];
258///
259/// let users = steam_api::get_profile_info(&steamids, &api_key)?;
260///
261/// for user in users.user {
262///     println!("Persona Name\t{}", user.personaname);
263///     println!("Steam Level\t{}", user.player_level);
264///     println!("Profile URL\t{}", user.profileurl);
265/// }
266/// # Ok(())
267/// # }
268/// ```
269/// Multiple Steam IDs
270/// ```
271/// # fn main() -> anyhow::Result<()> {
272/// let api_key = &std::env::var("API_KEY")?;
273/// let steamids = vec![
274///     "76561198421169032",
275///     "76561198421169032",
276///     "76561198421169032",
277///     "76561198421169032",
278/// ];
279///
280/// let users = steam_api::get_profile_info(&steamids, &api_key)?;
281///
282/// for user in users.user {
283///     println!("Persona Name\t{}", user.personaname);
284///     println!("Steam Level\t{}", user.player_level);
285///     println!("Profile URL\t{}", user.profileurl);
286/// }
287/// # Ok(())
288/// # }
289/// ```
290pub fn get_profile_info(steamids: &[&str], api_key: &str) -> Result<profile::Users, Error> {
291    // Initialize users struct
292    let mut users: profile::Users = profile::Users::default();
293
294    // we store the raw responses here
295    let mut summaries_raw: summaries::Response = summaries::Response::default();
296    let mut bans_raw: bans::Response = bans::Response::default();
297
298    if steamids.len() > 32 {
299        info!("Steamids are >32, splitting them into multiple api calls");
300        // split the vector into chunks of 32
301        let split_vec: Vec<&[&str]> = steamids.chunks(32).collect();
302
303        // iterate over the chunks
304        for i in split_vec {
305            // combine chunk into nice sendable string
306            let steamids_string = i.join(",");
307
308            // get ban chunk info and push to raw_data vector
309            let bans = get_player_bans(&steamids_string, api_key)?;
310            users.api_call_count += 1;
311
312            for i in bans {
313                bans_raw.players.push(i);
314            }
315
316            // get summaries chunk info and push to raw_data vector
317            let summaries = get_player_summaries(&steamids_string, api_key)?;
318            users.api_call_count += 1;
319
320            for i in summaries {
321                summaries_raw.response.players.push(i);
322            }
323        }
324    } else {
325        let steamids_string = steamids.join(",");
326        let bans = get_player_bans(&steamids_string, api_key)?;
327        users.api_call_count += 1;
328        for i in bans {
329            bans_raw.players.push(i);
330        }
331
332        // get summaries chunk info and push to raw_data vector
333        let summaries = get_player_summaries(&steamids_string, api_key)?;
334        users.api_call_count += 1;
335        for i in summaries {
336            summaries_raw.response.players.push(i);
337        }
338    }
339
340    // sort the vectors
341    bans_raw.players.sort_by(|a, b| b.SteamId.cmp(&a.SteamId));
342    summaries_raw
343        .response
344        .players
345        .sort_by(|a, b| b.steamid.cmp(&a.steamid));
346
347    // Null values will be 0 or ""
348    // iterate over GetPlayerSummaries response
349    for (index, player) in summaries_raw.response.players.iter().enumerate() {
350        let mut user = profile::User {
351            steamid: player.steamid.clone(),
352            communityvisibilitystate: player.communityvisibilitystate,
353            profilestate: player.profilestate,
354            personaname: player.personaname.clone(),
355            commentpermission: player.commentpermission.unwrap_or(0),
356            profileurl: player.profileurl.clone(),
357            avatar: player.avatar.clone(),
358            avatarmedium: player.avatarmedium.clone(),
359            avatarfull: player.avatarfull.clone(),
360            avatarhash: player.avatarhash.clone(),
361            lastlogoff: player.lastlogoff.unwrap_or(0),
362            personastate: player.personastate,
363            realname: player.realname.clone().unwrap_or_default(),
364            primaryclanid: player.primaryclanid.clone().unwrap_or_default(),
365            timecreated: player.timecreated.unwrap_or(0),
366            gameid: player.gameid.clone().unwrap_or_default(),
367            gameserverip: player.gameserverip.clone().unwrap_or_default(),
368            loccountrycode: player.loccountrycode.clone().unwrap_or_default(),
369            locstatecode: player.locstatecode.clone().unwrap_or_default(),
370            loccityid: player.loccityid.unwrap_or(0),
371            CommunityBanned: bans_raw.players[index].CommunityBanned,
372            VACBanned: bans_raw.players[index].VACBanned,
373            NumberOfVACBans: bans_raw.players[index].NumberOfVACBans,
374            DaysSinceLastBan: bans_raw.players[index].DaysSinceLastBan,
375            NumberOfGameBans: bans_raw.players[index].NumberOfGameBans,
376            EconomyBan: bans_raw.players[index].EconomyBan.clone(),
377            player_level: 0,
378        };
379        // If the profile is private, don't bother calling the api
380        if user.communityvisibilitystate == 3 {
381            user.player_level = get_steam_level(&player.steamid, api_key)?
382                .player_level
383                .unwrap_or_default();
384            users.api_call_count += 1;
385        } else {
386            info!("{} is private, skipping GetSteamLevel", user.steamid);
387        }
388        users.user.push(user);
389    }
390
391    Ok(users)
392}