Skip to content

Commit

Permalink
Merge pull request #51 from Chloe-Woahie/dev-main
Browse files Browse the repository at this point in the history
v0.24.0
  • Loading branch information
fekie authored May 19, 2023
2 parents bb0e506 + 20c7c5c commit ae2fdf1
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 56 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ license = "MIT"
name = "roboat"
readme = "README.md"
repository = "https://github.com/Chloe-Woahie/roboat"
version = "0.23.1"
version = "0.24.0"

[dependencies]
reqwest = { version = "0.11.14", default-features=false, features = ["rustls-tls", "json"] }
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ Documentation can be found [here](https://docs.rs/roboat/).
- Register Presence - [`Client::register_presence`](https://docs.rs/roboat/latest/roboat/struct.Client.html#method.register_presence)
* Private Messages API - [`privatemessages.roblox.com/*`]
- Fetch Messages - [`Client::messages`](https://docs.rs/roboat/latest/roboat/struct.Client.html#method.messages)
* Thumbnails API - [`thumbnails.roblox.com/*`]
- Fetch Asset Thumbnail URL Bulk - [`Client::asset_thumbnail_url_bulk`](https://docs.rs/roboat/latest/roboat/struct.Client.html#method.asset_thumbnail_url_bulk)
* Trades API - [`trades.roblox.com/*`]
- Fetch Trades List - [`Client::trades`](https://docs.rs/roboat/latest/roboat/struct.Client.html#method.trades)
* Users API - [`users.roblox.com/*`]
Expand All @@ -72,7 +74,7 @@ Alternatively, you can add a specific version of roboat to your project by addin

```toml
[dependencies]
roboat = "0.23.1"
roboat = "0.24.0"
```

# Quick Start Examples
Expand Down
79 changes: 25 additions & 54 deletions examples/fetch_item_details.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,64 +39,35 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
id: 21070789,
};

let items = vec![item_1, item_2, item_3, item_4, item_5, item_6];

let client = ClientBuilder::new().build();
let details = client.item_details(items).await?;

println!(
"Bundle Name: {} / Bundle Price: {}",
details[0].name,
details[0]
.price
.map(|x| x.to_string())
.unwrap_or_else(|| "*No Resellers*".to_owned())
);

println!(
"Item Name: {} / Item Price: {}",
details[1].name,
details[1]
.price
.map(|x| x.to_string())
.unwrap_or_else(|| "*No Resellers*".to_owned())
);
// ugc limited
let item_7 = Item {
item_type: ItemType::Asset,
id: 13464465797,
};

println!(
"Item Name: {} / Item Price: {}",
details[2].name,
details[2]
.price
.map(|x| x.to_string())
.unwrap_or_else(|| "*No Resellers*".to_owned())
);
// off sale ugc (may be out of date in the future)
let item_8 = Item {
item_type: ItemType::Asset,
id: 13450447846,
};

println!(
"Item Name: {} / Item Price: {}",
details[3].name,
details[3]
.price
.map(|x| x.to_string())
.unwrap_or_else(|| "*No Resellers*".to_owned())
);
let items = vec![
item_1, item_2, item_3, item_4, item_5, item_6, item_7, item_8,
];

println!(
"Item Name: {} / Item Price: {}",
details[4].name,
details[4]
.price
.map(|x| x.to_string())
.unwrap_or_else(|| "*No Resellers*".to_owned())
);
let client = ClientBuilder::new().build();
let all_details = client.item_details(items).await?;

println!(
"Item Name: {} / Item Price: {}",
details[5].name,
details[5]
.price
.map(|x| x.to_string())
.unwrap_or_else(|| "*No Resellers*".to_owned())
);
for details in all_details {
println!(
"Bundle Name: {} / Bundle Price: {}",
details.name,
details
.price
.map(|x| x.to_string())
.unwrap_or_else(|| "*No Resellers*".to_owned())
);
}

Ok(())
}
20 changes: 20 additions & 0 deletions examples/fetch_item_thumbnails.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use roboat::thumbnails::ThumbnailAssetSize;
use roboat::ClientBuilder;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let asset_id_1 = 20418400;
let asset_id_2 = 12660007639;

let size = ThumbnailAssetSize::S420x420;

let client = ClientBuilder::new().build();
let urls = client
.asset_thumbnail_url_bulk(vec![asset_id_1, asset_id_2], size)
.await?;

println!("Asset {} thumbnail url: {}", asset_id_1, urls[0]);
println!("Asset {} thumbnail url: {}", asset_id_2, urls[1]);

Ok(())
}
5 changes: 5 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
//! - Register Presence - [`Client::register_presence`]
//! * Private Messages API
//! - Fetch Messages - [`Client::messages`]
//! * Thumbnails API
//! - Fetch Asset Thumbnail URL Bulk - [`Client::asset_thumbnail_url_bulk`]
//! * Trades API
//! - Fetch Trades List - [`Client::trades`]
//! * Users API
Expand Down Expand Up @@ -210,6 +212,8 @@ pub mod groups;
mod presence;
/// A module for endpoints prefixed with <https://privatemessages.roblox.com/*>.
pub mod private_messages;
/// A module for endpoints prefixed with <https://thumbnails.roblox.com/*>.
pub mod thumbnails;
/// A module for endpoints prefixed with <https://trades.roblox.com/*>.
pub mod trades;
/// A module for endpoints prefixed with <https://users.roblox.com/*>.
Expand All @@ -235,6 +239,7 @@ mod validation;
// todo: maybe add stronger types for stuff like cursors? stuff that can be returned basically
// todo: add doc example and example count somewhere
// todo: add message apis
// todo: show on readme and crate docs which endpoints are wrappers for other endpoints

use serde::{Deserialize, Serialize};

Expand Down
166 changes: 166 additions & 0 deletions src/thumbnails/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
use crate::{Client, RoboatError};
use serde::{Deserialize, Serialize};
use std::fmt;

mod request_types;

const THUMBNAIL_API_URL: &str = "https://thumbnails.roblox.com/v1/batch";

/// A size for an asset thumbnail.
///
/// Sizes are taken from <https://thumbnails.roblox.com/docs/index.html#operations-Assets-get_v1_assets>.
#[allow(missing_docs)]
#[derive(
Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize, Copy,
)]
pub enum ThumbnailAssetSize {
S30x30,
S42x42,
S50x50,
S60x62,
S75x75,
S110x110,
S140x140,
S150x150,
S160x100,
S160x600,
S250x250,
S256x144,
S300x250,
S304x166,
S384x216,
S396x216,
#[default]
S420x420,
S480x270,
S512x512,
S576x324,
S700x700,
S728x90,
S768x432,
S1200x80,
}

impl fmt::Display for ThumbnailAssetSize {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::S30x30 => write!(f, "30x30"),
Self::S42x42 => write!(f, "42x42"),
Self::S50x50 => write!(f, "50x50"),
Self::S60x62 => write!(f, "60x62"),
Self::S75x75 => write!(f, "75x75"),
Self::S110x110 => write!(f, "110x110"),
Self::S140x140 => write!(f, "140x140"),
Self::S150x150 => write!(f, "150x150"),
Self::S160x100 => write!(f, "160x100"),
Self::S160x600 => write!(f, "160x600"),
Self::S250x250 => write!(f, "250x250"),
Self::S256x144 => write!(f, "256x144"),
Self::S300x250 => write!(f, "300x250"),
Self::S304x166 => write!(f, "304x166"),
Self::S384x216 => write!(f, "384x216"),
Self::S396x216 => write!(f, "396x216"),
Self::S420x420 => write!(f, "420x420"),
Self::S480x270 => write!(f, "480x270"),
Self::S512x512 => write!(f, "512x512"),
Self::S576x324 => write!(f, "576x324"),
Self::S700x700 => write!(f, "700x700"),
Self::S728x90 => write!(f, "728x90"),
Self::S768x432 => write!(f, "768x432"),
Self::S1200x80 => write!(f, "1200x80"),
}
}
}

impl Client {
/// Fetches multiple asset thumbnails of a specified size using <https://users.roblox.com/v1/users/search>.
///
/// # Notes
/// * Does not require a valid roblosecurity.
///
/// # Errors
/// * All errors under [Standard Errors](#standard-errors).
///
/// # Example
///
/// ```no_run
/// use roboat::ClientBuilder;
/// use roboat::thumbnails::ThumbnailAssetSize;
///
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let client = ClientBuilder::new().build();
///
/// let size = ThumbnailAssetSize::S420x420;
/// let asset_id_1 = 20418400;
/// let asset_id_2 = 12660007639;
///
/// let urls = client
/// .asset_thumbnail_url_bulk(vec![asset_id_1, asset_id_2], size)
/// .await?;
///
/// println!("Asset {} thumbnail url: {}", asset_id_1, urls[0]);
/// println!("Asset {} thumbnail url: {}", asset_id_2, urls[1]);
///
/// # Ok(())
/// # }
/// ```
pub async fn asset_thumbnail_url_bulk(
&self,
asset_ids: Vec<u64>,
size: ThumbnailAssetSize,
) -> Result<Vec<String>, RoboatError> {
let url = THUMBNAIL_API_URL;

let mut json_item_requests = Vec::new();

for asset_id in &asset_ids {
json_item_requests.push(serde_json::json!({
"requestId": format!("{}::Asset:{}:png:regular", asset_id, size),
"type": "Asset",
"targetId": asset_id,
"token": "",
"format": "png",
"size": size.to_string(),
}));
}

let body = serde_json::json!(json_item_requests);

let request_result = self.reqwest_client.post(url).json(&body).send().await;

let response = Self::validate_request_result(request_result).await?;
let mut raw =
Self::parse_to_raw::<request_types::AssetThumbnailUrlResponse>(response).await?;

sort_url_datas_by_argument_order(&mut raw.data, &asset_ids);

let mut urls = Vec::new();

for data in raw.data {
urls.push(data.image_url);
}

Ok(urls)
}
}

/// Makes sure that the url datas are in the same order as the arguments.
fn sort_url_datas_by_argument_order(
url_datas: &mut [request_types::AssetThumbnailUrlDataRaw],
arguments: &[u64],
) {
url_datas.sort_by(|a, b| {
let a_index = arguments
.iter()
.position(|id| *id == a.target_id as u64)
.unwrap_or(usize::MAX);

let b_index = arguments
.iter()
.position(|id| *id == b.target_id as u64)
.unwrap_or(usize::MAX);

a_index.cmp(&b_index)
});
}
21 changes: 21 additions & 0 deletions src/thumbnails/request_types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
pub(super) struct AssetThumbnailUrlResponse {
pub data: Vec<AssetThumbnailUrlDataRaw>,
}

#[derive(Serialize, Deserialize, Debug)]
pub(super) struct AssetThumbnailUrlDataRaw {
#[serde(rename = "requestId")]
pub request_id: String,
#[serde(rename = "errorCode")]
pub error_code: i64,
#[serde(rename = "errorMessage")]
pub error_message: String,
#[serde(rename = "targetId")]
pub target_id: i64,
pub state: String,
#[serde(rename = "imageUrl")]
pub image_url: String,
}

0 comments on commit ae2fdf1

Please sign in to comment.