roblox_api/api/games/
v1.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{DateTime, Error, Paging, client::Client};
4
5pub const URL: &str = "https://games.roblox.com/v1";
6
7#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
8#[serde(rename_all = "camelCase")]
9pub struct PlaceDetails {
10    #[serde(rename = "placeId")]
11    pub id: u64,
12    pub name: String,
13    pub description: String,
14
15    pub source_name: String,
16    pub source_description: String,
17
18    pub universe_id: u64,
19    pub universe_root_place_id: u64,
20
21    pub url: String,
22    pub image_token: String,
23    pub reason_prohibited: String,
24
25    // cast to Creator
26    pub builder: String,
27    pub builder_id: u64,
28
29    pub price: u64,
30
31    pub is_playable: bool,
32    #[serde(rename = "hasVerifiedBadge")]
33    pub is_verified: bool,
34}
35
36#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
37pub struct PrivateServerInfoGameRootPlace {
38    pub id: u64,
39    pub name: String,
40}
41
42#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
43#[serde(rename_all = "camelCase")]
44pub struct PrivateServerInfoGame {
45    pub id: u64,
46    pub name: String,
47    pub root_place: PrivateServerInfoGameRootPlace,
48}
49
50#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
51#[serde(rename_all = "camelCase")]
52pub struct PrivateServerInfoSubscription {
53    pub price: u64,
54
55    pub active: bool,
56    pub expired: bool,
57    pub can_renew: bool,
58    pub has_price_changed: bool,
59    pub has_recurring_profile: bool,
60    pub has_insufficient_funds: bool,
61
62    pub expiration_date: DateTime,
63}
64
65#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
66#[serde(rename_all = "camelCase")]
67pub struct PrivateServerInfoPermissions {
68    // TODO: find out what it holds
69    //pub users: Vec<>
70
71    // Assuming u64, might be String
72    pub enemy_clan_id: Option<u64>,
73
74    pub clan_allowed: bool,
75    pub friends_allowed: bool,
76}
77
78#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
79pub struct PrivateServerInfoVoiceSettings {
80    pub enabled: bool,
81}
82
83#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
84#[serde(rename_all = "camelCase")]
85pub struct PrivateServerInfo {
86    pub id: u64,
87    pub name: String,
88
89    pub active: bool,
90
91    #[serde(rename = "link")]
92    pub link_url: String,
93    pub join_code: String,
94
95    pub game: PrivateServerInfoGame,
96    pub subscription: PrivateServerInfoSubscription,
97    pub permissions: PrivateServerInfoPermissions,
98    pub voice_settings: PrivateServerInfoVoiceSettings,
99}
100
101#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
102#[serde(rename_all = "camelCase")]
103pub struct PrivateServerOwner {
104    pub id: u64,
105    pub name: String,
106    pub display_name: String,
107    #[serde(rename = "hasVerifiedBadge")]
108    pub is_verified: bool,
109}
110
111#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
112#[serde(rename_all = "camelCase")]
113pub struct PrivateServer {
114    pub name: String,
115    #[serde(rename = "vipServerId")]
116    pub id: u64,
117    pub max_players: u16,
118
119    pub owner: PrivateServerOwner,
120
121    //pub players: Vec<()>, // Seems to be an empty vec?
122    pub player_tokens: Vec<String>,
123}
124
125#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
126pub struct PrivateServersResponse {
127    #[serde(rename = "data")]
128    pub servers: Vec<PrivateServer>,
129    #[serde(rename = "gameJoinRestricted")]
130    pub join_restricted: bool,
131    #[serde(rename = "nextPageCursor")]
132    pub next_cursor: Option<String>,
133    #[serde(rename = "previousPageCursor")]
134    pub previous_cursor: Option<String>,
135}
136
137#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
138#[serde(rename_all = "camelCase")]
139pub struct Server {
140    #[serde(rename = "id")]
141    pub job_id: String,
142
143    pub playing: u16,
144    pub max_players: u16,
145    pub fps: f32,
146    pub ping: Option<u16>,
147
148    //pub players: Vec<()>, // Seems to be an empty vec?
149    pub player_tokens: Vec<String>,
150}
151
152#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
153pub struct ServersResponse {
154    #[serde(rename = "data")]
155    pub servers: Vec<Server>,
156    #[serde(rename = "nextPageCursor")]
157    pub next_cursor: Option<String>,
158    #[serde(rename = "previousPageCursor")]
159    pub previous_cursor: Option<String>,
160}
161
162#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
163pub struct UniverseVotes {
164    pub id: u64,
165    #[serde(rename = "upVotes")]
166    pub likes: u32,
167    #[serde(rename = "downVotes")]
168    pub dislikes: u32,
169}
170
171#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
172#[serde(rename_all = "camelCase")]
173pub struct UniverseGamepass {
174    pub id: u64,
175    pub name: String,
176    pub display_name: String,
177
178    pub price: Option<u64>,
179    pub product_id: Option<u64>,
180
181    #[serde(rename = "isOwned")]
182    pub owned: bool,
183
184    pub seller_id: Option<u64>,
185    pub seller_name: String,
186}
187
188#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
189pub struct UniverseGamepassesResponse {
190    #[serde(rename = "data")]
191    pub gamepasses: Vec<UniverseGamepass>,
192    #[serde(rename = "nextPageCursor")]
193    pub next_cursor: Option<String>,
194    #[serde(rename = "previousPageCursor")]
195    pub previous_cursor: Option<String>,
196}
197
198pub async fn batch_place_details(
199    client: &mut Client,
200    ids: &[u64],
201) -> Result<Vec<PlaceDetails>, Error> {
202    let ids = ids
203        .iter()
204        .map(|x| x.to_string())
205        .collect::<Vec<String>>()
206        .join(",");
207
208    let result = client
209        .requestor
210        .client
211        .get(format!("{URL}/games/multiget-place-details"))
212        .query(&[("placeIds", ids)])
213        .headers(client.requestor.default_headers.clone())
214        .send()
215        .await;
216
217    let response = client.validate_response(result).await?;
218    client
219        .requestor
220        .parse_json::<Vec<PlaceDetails>>(response)
221        .await
222}
223
224/// Set server_kind to 0, if you want a valid response
225pub async fn servers(
226    client: &mut Client,
227    id: u64,
228    server_kind: u8,
229    exclude_full_games: bool,
230    paging: Paging<'_>,
231) -> Result<ServersResponse, Error> {
232    let limit = paging.limit.unwrap_or(10).to_string();
233    let sort_order = paging.order.unwrap_or_default().to_string();
234    let cursor = match paging.cursor {
235        Some(cursor) => cursor.to_string(),
236        None => String::new(),
237    };
238
239    let result = client
240        .requestor
241        .client
242        .get(format!("{URL}/games/{id}/servers/{server_kind}"))
243        .query(&[
244            ("excludeFullGames", exclude_full_games.to_string()),
245            ("limit", limit),
246            ("sortOrder", sort_order),
247            ("cursor", cursor),
248        ])
249        .headers(client.requestor.default_headers.clone())
250        .send()
251        .await;
252
253    let response = client.validate_response(result).await?;
254    client
255        .requestor
256        .parse_json::<ServersResponse>(response)
257        .await
258}
259
260pub async fn private_servers(
261    client: &mut Client,
262    id: u64,
263    exclude_friend_servers: bool,
264    paging: Paging<'_>,
265) -> Result<PrivateServersResponse, Error> {
266    let limit = paging.limit.unwrap_or(10).to_string();
267    let sort_order = paging.order.unwrap_or_default().to_string();
268    let cursor = match paging.cursor {
269        Some(cursor) => cursor.to_string(),
270        None => String::new(),
271    };
272
273    let result = client
274        .requestor
275        .client
276        .get(format!("{URL}/games/{id}/private-servers"))
277        .query(&[
278            ("excludeFriendServers", exclude_friend_servers.to_string()),
279            ("limit", limit),
280            ("sortOrder", sort_order),
281            ("cursor", cursor),
282        ])
283        .headers(client.requestor.default_headers.clone())
284        .send()
285        .await;
286
287    let response = client.validate_response(result).await?;
288    client
289        .requestor
290        .parse_json::<PrivateServersResponse>(response)
291        .await
292}
293
294pub async fn private_server_info(client: &mut Client, id: u64) -> Result<PrivateServerInfo, Error> {
295    let result = client
296        .requestor
297        .client
298        .get(format!("{URL}/vip-servers/{id}"))
299        .headers(client.requestor.default_headers.clone())
300        .send()
301        .await;
302
303    let response = client.validate_response(result).await?;
304    client
305        .requestor
306        .parse_json::<PrivateServerInfo>(response)
307        .await
308}
309
310pub async fn universe_favorite_count(client: &mut Client, id: u64) -> Result<u64, Error> {
311    let result = client
312        .requestor
313        .client
314        .get(format!("{URL}/games/{id}/favorites/count"))
315        .headers(client.requestor.default_headers.clone())
316        .send()
317        .await;
318
319    #[derive(Debug, Deserialize)]
320    struct Response {
321        #[serde(rename = "favoritesCount")]
322        favorites: u64,
323    }
324
325    let response = client.validate_response(result).await?;
326    Ok(client
327        .requestor
328        .parse_json::<Response>(response)
329        .await?
330        .favorites)
331}
332
333pub async fn universe_votes(client: &mut Client, ids: &[u64]) -> Result<Vec<UniverseVotes>, Error> {
334    let ids = ids
335        .iter()
336        .map(|x| x.to_string())
337        .collect::<Vec<String>>()
338        .join(",");
339
340    let result = client
341        .requestor
342        .client
343        .get(format!("{URL}/games/votes"))
344        .query(&[("universeIds", ids)])
345        .headers(client.requestor.default_headers.clone())
346        .send()
347        .await;
348
349    #[derive(Debug, Deserialize)]
350    struct Response {
351        #[serde(rename = "data")]
352        votes: Vec<UniverseVotes>,
353    }
354
355    let response = client.validate_response(result).await?;
356    Ok(client
357        .requestor
358        .parse_json::<Response>(response)
359        .await?
360        .votes)
361}
362
363pub async fn universe_gamepasses(
364    client: &mut Client,
365    id: u64,
366    paging: Paging<'_>,
367) -> Result<UniverseGamepassesResponse, Error> {
368    let limit = paging.limit.unwrap_or(10).to_string();
369    let sort_order = paging.order.unwrap_or_default().to_string();
370    let cursor = match paging.cursor {
371        Some(cursor) => cursor.to_string(),
372        None => String::new(),
373    };
374
375    let result = client
376        .requestor
377        .client
378        .get(format!("{URL}/games/{id}/game-passes"))
379        .query(&[
380            ("limit", limit),
381            ("sortOrder", sort_order),
382            ("cursor", cursor),
383        ])
384        .headers(client.requestor.default_headers.clone())
385        .send()
386        .await;
387
388    let response = client.validate_response(result).await?;
389    client
390        .requestor
391        .parse_json::<UniverseGamepassesResponse>(response)
392        .await
393}