steam_rs/published_file_service/
query_files.rs

1//! Implements the `QueryFiles` endpoint
2
3use core::fmt;
4
5use serde::{Deserialize, Serialize};
6
7use crate::{
8    errors::{ErrorHandle, PublishedFileServiceError},
9    macros::{do_http, optional_argument},
10    steam_id::{de_steamid_from_str, SteamId},
11    Steam, BASE,
12};
13
14use super::INTERFACE;
15
16const ENDPOINT: &str = "QueryFiles";
17const VERSION: &str = "1";
18
19/// Represents the query types used when querying published files on Steam Workshop.
20#[derive(Debug)]
21pub 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
68impl 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)]
100pub 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
145impl 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)]
176pub 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<String>,
185    /// The size of the preview, if available.
186    pub size: Option<u32>,
187    /// The filename of the preview, if available.
188    #[serde(rename = "filename")]
189    pub file_name: Option<String>,
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)]
196pub 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)]
205pub 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)]
212pub 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)]
221pub 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<String>,
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<Preview>,
309    /// Tags associated with the file.
310    pub tags: Vec<Tag>,
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<u32>,
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)]
333pub 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<File>,
339}
340
341#[derive(Serialize, Deserialize, Debug)]
342struct Response {
343    response: PublishedFiles,
344}
345
346impl 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<u32>, // numperpage
387        creator_app_id: u32,
388        app_id: u32,
389        required_tags: &str,
390        excluded_tags: &str,
391        match_all_tags: Option<bool>,
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<u32>,
400        language: Option<i32>,
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<bool>,
412        return_playtime_stats: u32,
413    ) -> Result<PublishedFiles, PublishedFileServiceError> {
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}