1use serde::{Deserialize, Serialize};
2
3use crate::{AssetTypeId, DateTime, Error, Paging, client::Client};
4
5pub const URL: &str = "https://games.roblox.com/v2";
6
7#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
8#[serde(rename_all = "camelCase")]
9pub struct UniverseMedia {
10 pub asset_type: AssetTypeId,
11 pub asset_type_id: u8,
12
13 pub approved: bool,
14 pub alt_text: Option<String>,
15
16 pub image_id: Option<u64>,
17
18 pub video_id: Option<String>,
19 pub video_hash: Option<String>,
20 pub video_title: Option<String>,
21}
22
23#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
25pub struct GameCreator {
26 pub id: u64,
27 #[serde(rename = "type")]
28 pub kind: String,
29 pub name: Option<String>,
30}
31
32#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
33pub struct GameRootPlace {
34 pub id: u64,
35 #[serde(rename = "type")]
36 pub kind: String,
37}
38
39#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
40#[serde(rename_all = "camelCase")]
41pub struct Game {
42 pub id: u64,
43 pub name: String,
44 pub description: Option<String>,
45
46 pub creator: GameCreator,
47 pub root_place: GameRootPlace,
48
49 pub created: DateTime,
50 pub updated: DateTime,
51
52 pub price: Option<u64>,
53 pub place_visits: u64,
54}
55
56#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
57pub struct GamesResponse {
58 #[serde(rename = "data")]
59 pub games: Vec<Game>,
60 #[serde(rename = "nextPageCursor")]
61 pub next_cursor: Option<String>,
62 #[serde(rename = "previousPageCursor")]
63 pub previous_cursor: Option<String>,
64}
65
66async fn games_generic(
67 client: &mut Client,
68 path: &str,
69 access_filter: u8,
70 paging: Paging<'_>,
71) -> Result<GamesResponse, Error> {
72 let limit = paging.limit.unwrap_or(10).to_string();
73 let sort_order = paging.order.unwrap_or_default().to_string();
74 let cursor = match paging.cursor {
75 Some(cursor) => cursor.to_string(),
76 None => String::new(),
77 };
78
79 let result = client
80 .requestor
81 .client
82 .get(format!("{URL}/{path}"))
83 .query(&[
84 ("accessFilter", access_filter.to_string()),
85 ("limit", limit),
86 ("sortOrder", sort_order),
87 ("cursor", cursor),
88 ])
89 .headers(client.requestor.default_headers.clone())
90 .send()
91 .await;
92
93 let response = client.validate_response(result).await?;
94 client.requestor.parse_json::<GamesResponse>(response).await
95}
96
97pub async fn universe_media(
98 client: &mut Client,
99 id: u64,
100 all_experiences: bool,
101) -> Result<Vec<UniverseMedia>, Error> {
102 let result = client
103 .requestor
104 .client
105 .get(format!("{URL}/games/{id}/media"))
106 .query(&[("fetchAllExperienceRelatedMedia", all_experiences)])
107 .headers(client.requestor.default_headers.clone())
108 .send()
109 .await;
110
111 #[derive(Debug, Deserialize)]
112 struct Response {
113 #[serde(rename = "data")]
114 media: Vec<UniverseMedia>,
115 }
116
117 let response = client.validate_response(result).await?;
118 Ok(client
119 .requestor
120 .parse_json::<Response>(response)
121 .await?
122 .media)
123}
124
125pub async fn group_games(
128 client: &mut Client,
129 id: u64,
130 access_filter: u8,
131 paging: Paging<'_>,
132) -> Result<GamesResponse, Error> {
133 games_generic(client, &format!("groups/{id}/games"), access_filter, paging).await
134}
135
136pub async fn group_games_v2(
138 client: &mut Client,
139 id: u64,
140 access_filter: u8,
141 paging: Paging<'_>,
142) -> Result<GamesResponse, Error> {
143 games_generic(
144 client,
145 &format!("groups/{id}/gamesV2"),
146 access_filter,
147 paging,
148 )
149 .await
150}
151
152pub async fn user_games(
154 client: &mut Client,
155 id: u64,
156 access_filter: u8,
157 paging: Paging<'_>,
158) -> Result<GamesResponse, Error> {
159 games_generic(client, &format!("users/{id}/games"), access_filter, paging).await
160}
161
162pub async fn user_favorited_games(
164 client: &mut Client,
165 id: u64,
166 access_filter: u8,
167 paging: Paging<'_>,
168) -> Result<GamesResponse, Error> {
169 games_generic(
170 client,
171 &format!("users/{id}/favorite/games"),
172 access_filter,
173 paging,
174 )
175 .await
176}