Skip to content

Commit 3315e73

Browse files
authored
Inventory API (#73)
* Start inventory impl * Collectible details * Doc stub * Inventory client * Inventory CLI stub * Inventory CLI impl * Inventory CLI docs * Serial number optional * Remove print * Clean up warnings
1 parent 42335ad commit 3315e73

File tree

11 files changed

+313
-9
lines changed

11 files changed

+313
-9
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Possible use-cases:
3030
| :white_check_mark: | Places |
3131
| :x: | Instances |
3232
| :white_check_mark: | Subscriptions |
33-
| :x: | Inventory |
33+
| :white_check_mark: | Inventory |
3434
| :white_check_mark: | User Notifications |
3535
| :white_check_mark: | User |
3636
| :x: | Creator Store |

docs/cli/cli-inventory.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Inventory API
2+
3+
## List
4+
List inventory items for a given user.
5+
6+
```
7+
Usage: rbxcloud inventory list [OPTIONS] --user-id <USER_ID> --api-key <API_KEY>
8+
9+
Options:
10+
-u, --user-id <USER_ID> Roblox user ID
11+
-p, --pretty Pretty-print the JSON response
12+
-m, --max-page-size <MAX_PAGE_SIZE> Max page size
13+
-n, --page-token <PAGE_TOKEN> Next page token
14+
-f, --filter <FILTER> Filter string
15+
-a, --api-key <API_KEY> Roblox Open Cloud API Key [env: RBXCLOUD_API_KEY=]
16+
-h, --help Print help
17+
```

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ nav:
1313
- DataStore: cli/cli-datastore.md
1414
- Experience: cli/cli-experience.md
1515
- Group: cli/cli-group.md
16+
- Inventory: cli/cli-inventory.md
1617
- Luau Execution: cli/cli-luau-execution.md
1718
- Messaging: cli/cli-messaging.md
1819
- Notification: cli/cli-notification.md

src/cli/inventory_cli.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use clap::{Args, Subcommand};
2+
use rbxcloud::rbx::{types::RobloxUserId, v2::Client};
3+
4+
#[derive(Debug, Subcommand)]
5+
pub enum InventoryCommands {
6+
/// List inventory items for a given user
7+
List {
8+
/// Roblox user ID
9+
#[clap(short, long, value_parser)]
10+
user_id: u64,
11+
12+
/// Pretty-print the JSON response
13+
#[clap(short, long, value_parser, default_value_t = false)]
14+
pretty: bool,
15+
16+
/// Max page size
17+
#[clap(short, long, value_parser)]
18+
max_page_size: Option<u32>,
19+
20+
/// Next page token
21+
#[clap(short = 'n', long, value_parser)]
22+
page_token: Option<String>,
23+
24+
/// Filter string
25+
#[clap(short, long, value_parser)]
26+
filter: Option<String>,
27+
28+
/// Roblox Open Cloud API Key
29+
#[clap(short, long, value_parser, env = "RBXCLOUD_API_KEY")]
30+
api_key: String,
31+
},
32+
}
33+
34+
#[derive(Debug, Args)]
35+
pub struct Inventory {
36+
#[clap(subcommand)]
37+
command: InventoryCommands,
38+
}
39+
40+
impl Inventory {
41+
pub async fn run(self) -> anyhow::Result<Option<String>> {
42+
match self.command {
43+
InventoryCommands::List {
44+
user_id,
45+
pretty,
46+
max_page_size,
47+
page_token,
48+
filter,
49+
api_key,
50+
} => {
51+
let client = Client::new(&api_key);
52+
53+
let inventory = client.inventory();
54+
55+
let res = inventory
56+
.list_inventory_items(RobloxUserId(user_id), max_page_size, page_token, filter)
57+
.await;
58+
59+
match res {
60+
Ok(data) => {
61+
let r = if pretty {
62+
serde_json::to_string_pretty(&data)?
63+
} else {
64+
serde_json::to_string(&data)?
65+
};
66+
Ok(Some(r))
67+
}
68+
Err(err) => Err(anyhow::anyhow!(err)),
69+
}
70+
}
71+
}
72+
}
73+
}

src/cli/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod assets_cli;
22
mod datastore_cli;
33
mod experience_cli;
44
mod group_cli;
5+
mod inventory_cli;
56
mod luau_execution_cli;
67
mod messaging_cli;
78
mod notification_cli;
@@ -12,6 +13,7 @@ mod universe_cli;
1213
mod user_cli;
1314

1415
use clap::{Parser, Subcommand};
16+
use inventory_cli::Inventory;
1517
use luau_execution_cli::Luau;
1618
use universe_cli::Universe;
1719
use user_cli::User;
@@ -46,6 +48,9 @@ pub enum Command {
4648
/// Access the Roblox OrderedDataStore API
4749
OrderedDatastore(OrderedDataStore),
4850

51+
/// Access the Roblox user inventory API
52+
Inventory(Inventory),
53+
4954
/// Access the Roblox Group API
5055
Group(Group),
5156

@@ -76,6 +81,7 @@ impl Cli {
7681
Command::Datastore(command) => command.run().await,
7782
Command::OrderedDatastore(command) => command.run().await,
7883
Command::Group(command) => command.run().await,
84+
Command::Inventory(command) => command.run().await,
7985
Command::Luau(command) => command.run().await,
8086
Command::Subscription(command) => command.run().await,
8187
Command::Notification(command) => command.run().await,

src/rbx/types.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ pub struct PlaceId(pub u64);
1313
pub struct ReturnLimit(pub u64);
1414

1515
/// Represents a Roblox user's ID.
16-
#[derive(Debug, Clone, Copy)]
16+
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1717
pub struct RobloxUserId(pub u64);
1818

1919
#[derive(Debug, Clone, Copy)]

src/rbx/v2/inventory.rs

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
use serde::{Deserialize, Serialize};
2+
3+
use crate::rbx::{error::Error, types::RobloxUserId, util::QueryString};
4+
5+
use super::http_err::handle_http_err;
6+
7+
#[derive(Deserialize, Serialize, Debug)]
8+
#[serde(rename_all = "camelCase")]
9+
pub struct ListInventoryItemsParams {
10+
pub api_key: String,
11+
pub user_id: RobloxUserId,
12+
pub max_page_size: Option<u32>,
13+
pub page_token: Option<String>,
14+
pub filter: Option<String>,
15+
}
16+
17+
#[derive(Deserialize, Serialize, Debug)]
18+
#[serde(rename_all = "camelCase")]
19+
pub struct InventoryItems {
20+
inventory_items: Vec<InventoryItem>,
21+
next_page_token: String,
22+
}
23+
24+
#[derive(Deserialize, Serialize, Debug)]
25+
#[serde(rename_all = "camelCase")]
26+
pub struct InventoryItem {
27+
path: String,
28+
#[serde(skip_serializing_if = "Option::is_none")]
29+
asset_details: Option<InventoryItemAssetDetails>,
30+
#[serde(skip_serializing_if = "Option::is_none")]
31+
badge_details: Option<InventoryItemBadgeDetails>,
32+
#[serde(skip_serializing_if = "Option::is_none")]
33+
game_pass_details: Option<InventoryItemGamePassDetails>,
34+
#[serde(skip_serializing_if = "Option::is_none")]
35+
private_server_details: Option<InventoryItemPrivateServerDetails>,
36+
#[serde(skip_serializing_if = "Option::is_none")]
37+
add_time: Option<String>,
38+
}
39+
40+
#[derive(Deserialize, Serialize, Debug)]
41+
#[serde(rename_all = "camelCase")]
42+
pub struct InventoryItemAssetDetails {
43+
asset_id: String,
44+
instance_id: String,
45+
inventory_item_asset_type: InventoryItemAssetType,
46+
#[serde(skip_serializing_if = "Option::is_none")]
47+
collectible_details: Option<InventoryItemCollectibleDetails>,
48+
}
49+
50+
#[derive(Deserialize, Serialize, Debug)]
51+
#[serde(rename_all = "camelCase")]
52+
pub struct InventoryItemBadgeDetails {
53+
badge_id: String,
54+
}
55+
56+
#[derive(Deserialize, Serialize, Debug)]
57+
#[serde(rename_all = "camelCase")]
58+
pub struct InventoryItemGamePassDetails {
59+
game_pass_id: String,
60+
}
61+
62+
#[derive(Deserialize, Serialize, Debug)]
63+
#[serde(rename_all = "camelCase")]
64+
pub struct InventoryItemPrivateServerDetails {
65+
private_server_id: String,
66+
}
67+
68+
#[derive(Deserialize, Serialize, Debug)]
69+
#[serde(rename_all = "camelCase")]
70+
pub struct InventoryItemCollectibleDetails {
71+
item_id: String,
72+
instance_id: String,
73+
instance_state: InventoryItemInstanceState,
74+
#[serde(skip_serializing_if = "Option::is_none")]
75+
serial_number: Option<u64>,
76+
}
77+
78+
#[derive(Deserialize, Serialize, Debug)]
79+
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
80+
pub enum InventoryItemInstanceState {
81+
CollectibleItemInstanceStateUnspecified,
82+
Available,
83+
Hold,
84+
}
85+
86+
#[derive(Deserialize, Serialize, Debug)]
87+
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
88+
pub enum InventoryItemAssetType {
89+
ClassicTshirt,
90+
Audio,
91+
Hat,
92+
Model,
93+
ClassicShirt,
94+
ClassicPants,
95+
Decal,
96+
ClassicHead,
97+
Face,
98+
Gear,
99+
Animation,
100+
Torso,
101+
RightArm,
102+
LeftArm,
103+
LeftLeg,
104+
RightLeg,
105+
Package,
106+
Plugin,
107+
MeshPart,
108+
HairAccessory,
109+
FaceAccessory,
110+
NeckAccessory,
111+
ShoulderAccessory,
112+
FrontAccessory,
113+
BackAccessory,
114+
WaistAccessory,
115+
ClimbAnimation,
116+
DeathAnimation,
117+
FallAnimation,
118+
IdleAnimation,
119+
JumpAnimation,
120+
RunAnimation,
121+
SwimAnimation,
122+
WalkAnimation,
123+
PoseAnimation,
124+
EmoteAnimation,
125+
Video,
126+
TshirtAccessory,
127+
ShirtAccessory,
128+
PantsAccessory,
129+
JacketAccessory,
130+
SweaterAccessory,
131+
ShortsAccessory,
132+
LeftShoeAccessory,
133+
RightShoeAccessory,
134+
DressSkirtAccessory,
135+
EyebrowAccessory,
136+
EyelashAccessory,
137+
MoodAnimation,
138+
DynamicHead,
139+
CreatedPlace,
140+
PurchasedPlace,
141+
}
142+
143+
pub async fn list_inventory_items(
144+
params: &ListInventoryItemsParams,
145+
) -> Result<InventoryItems, Error> {
146+
let client = reqwest::Client::new();
147+
148+
let url = format!(
149+
"https://apis.roblox.com/cloud/v2/users/{userId}/inventory-items",
150+
userId = params.user_id,
151+
);
152+
153+
let mut query: QueryString = vec![];
154+
if let Some(max_page_size) = params.max_page_size {
155+
query.push(("maxPageSize", format!("{max_page_size}")));
156+
}
157+
if let Some(page_token) = &params.page_token {
158+
query.push(("pageToken", page_token.clone()));
159+
}
160+
if let Some(filter) = &params.filter {
161+
query.push(("filter", filter.clone()));
162+
}
163+
164+
let res = client
165+
.get(url)
166+
.header("x-api-key", &params.api_key)
167+
.query(&query)
168+
.send()
169+
.await?;
170+
171+
let status = res.status();
172+
173+
if !status.is_success() {
174+
let code = status.as_u16();
175+
return handle_http_err(code);
176+
}
177+
178+
let body = res.json::<InventoryItems>().await?;
179+
Ok(body)
180+
}

src/rbx/v2/luau_execution.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,6 @@ pub async fn get_luau_execution_task_logs(
258258
.send()
259259
.await?;
260260

261-
println!("URL SENT: {}", res.url());
262-
263261
let status = res.status();
264262

265263
if !status.is_success() {

0 commit comments

Comments
 (0)