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}