roblox_api/api/games/
v2.rs

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// TODO: use CreatorType instead
24#[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
125/// Apparently this api only works on owned groups, use v2 instead
126/// Set `access_filter` to 1, if you want a valid response
127pub 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
136/// Set `access_filter` to 1, if you want a valid response
137pub 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
152/// Set `access_filter` to 2, if you want a valid response
153pub 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
162/// Set `access_filter` to 2, if you want a valid response
163pub 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}