Skip to main content

steam_client/services/
pubfiles.rs

1//! Workshop/UGC published files for Steam client.
2//!
3//! This module provides functionality for getting details about
4//! Workshop items and other user-generated content.
5
6use std::collections::HashMap;
7
8use steamid::SteamID;
9
10use crate::{error::SteamError, SteamClient};
11
12/// Published file (Workshop item) details.
13#[derive(Debug, Clone)]
14pub struct PublishedFileDetails {
15    /// The published file ID.
16    pub publishedfileid: u64,
17    /// Creator's SteamID.
18    pub creator: Option<SteamID>,
19    /// App ID this file is for.
20    pub consumer_appid: u32,
21    /// App ID where this was published from.
22    pub creator_appid: u32,
23    /// Title of the file.
24    pub title: String,
25    /// Description.
26    pub description: String,
27    /// File name.
28    pub filename: String,
29    /// File size in bytes.
30    pub file_size: u64,
31    /// Preview URL.
32    pub preview_url: Option<String>,
33    /// Time created (Unix timestamp).
34    pub time_created: u32,
35    /// Time updated (Unix timestamp).
36    pub time_updated: u32,
37    /// Visibility status.
38    pub visibility: u32,
39    /// Whether the file is banned.
40    pub banned: bool,
41    /// Ban reason if banned.
42    pub ban_reason: Option<String>,
43    /// Number of subscriptions.
44    pub subscriptions: u64,
45    /// Number of favorites.
46    pub favorited: u64,
47    /// Number of lifetime subscriptions.
48    pub lifetime_subscriptions: u64,
49    /// Number of lifetime favorites.
50    pub lifetime_favorited: u64,
51    /// Number of views.
52    pub views: u64,
53    /// Tags associated with the file.
54    pub tags: Vec<String>,
55    /// Key-value tags.
56    pub kvtags: HashMap<String, String>,
57    /// Vote data.
58    pub vote_data: Option<VoteData>,
59}
60
61/// Vote data for a published file.
62#[derive(Debug, Clone)]
63pub struct VoteData {
64    /// Score (0.0 to 1.0).
65    pub score: f32,
66    /// Number of votes up.
67    pub votes_up: u32,
68    /// Number of votes down.
69    pub votes_down: u32,
70}
71
72impl SteamClient {
73    /// Get details for Workshop/UGC files.
74    ///
75    /// # Arguments
76    /// * `ids` - List of published file IDs to get details for
77    ///
78    /// # Returns
79    /// A map of file ID to file details.
80    pub async fn get_published_file_details(&mut self, ids: Vec<u64>) -> Result<HashMap<u64, PublishedFileDetails>, SteamError> {
81        if !self.is_logged_in() {
82            return Err(SteamError::NotLoggedOn);
83        }
84
85        let msg = steam_protos::CPublishedFileGetDetailsRequest {
86            publishedfileids: ids,
87            includetags: Some(true),
88            includeadditionalpreviews: Some(true),
89            includechildren: Some(true),
90            includekvtags: Some(true),
91            includevotes: Some(true),
92            includeforsaledata: Some(true),
93            includemetadata: Some(true),
94            language: Some(0),
95            ..Default::default()
96        };
97
98        let response: steam_protos::CPublishedFileGetDetailsResponse = self.send_service_method_and_wait("PublishedFile.GetDetails#1", &msg).await?;
99
100        let mut results = HashMap::new();
101
102        for item in response.publishedfiledetails {
103            let publishedfileid = match item.publishedfileid {
104                Some(id) => id,
105                None => continue,
106            };
107
108            let creator = match item.creator {
109                Some(id) if id > 0 => Some(SteamID::from(id)),
110                _ => None,
111            };
112
113            let mut kvtags = HashMap::new();
114            for tag in item.kvtags {
115                if let (Some(k), Some(v)) = (tag.key, tag.value) {
116                    kvtags.insert(k, v);
117                }
118            }
119
120            let tags = item.tags.into_iter().filter_map(|t| t.tag).collect();
121
122            let vote_data = item.vote_data.map(|v| VoteData { score: v.score.unwrap_or(0.0), votes_up: v.votes_up.unwrap_or(0), votes_down: v.votes_down.unwrap_or(0) });
123
124            results.insert(
125                publishedfileid,
126                PublishedFileDetails {
127                    publishedfileid,
128                    creator,
129                    consumer_appid: item.consumer_appid.unwrap_or(0),
130                    creator_appid: item.creator_appid.unwrap_or(0),
131                    title: item.title.unwrap_or_default(),
132                    description: item.file_description.unwrap_or_default(),
133                    filename: item.filename.unwrap_or_default(),
134                    file_size: item.file_size.unwrap_or(0),
135                    preview_url: item.preview_url,
136                    time_created: item.time_created.unwrap_or(0),
137                    time_updated: item.time_updated.unwrap_or(0),
138                    visibility: item.visibility.unwrap_or(0),
139                    banned: item.banned.unwrap_or(false),
140                    ban_reason: item.ban_reason,
141                    subscriptions: item.subscriptions.map(|v| v as u64).unwrap_or(0),
142                    favorited: item.favorited.map(|v| v as u64).unwrap_or(0),
143                    lifetime_subscriptions: item.lifetime_subscriptions.map(|v| v as u64).unwrap_or(0),
144                    lifetime_favorited: item.lifetime_favorited.map(|v| v as u64).unwrap_or(0),
145                    views: item.views.map(|v| v as u64).unwrap_or(0),
146                    tags,
147                    kvtags,
148                    vote_data,
149                },
150            );
151        }
152
153        Ok(results)
154    }
155
156    /// Get details for a single Workshop/UGC file.
157    ///
158    /// # Arguments
159    /// * `id` - Published file ID to get details for
160    pub async fn get_published_file_detail(&mut self, id: u64) -> Result<Option<PublishedFileDetails>, SteamError> {
161        let results = self.get_published_file_details(vec![id]).await?;
162        Ok(results.into_values().next())
163    }
164}