From 85a55375671ddafae74f37f72d22e09a9fa272fb Mon Sep 17 00:00:00 2001 From: Chloe-Woahie <68732833+Chloe-Woahie@users.noreply.github.com> Date: Wed, 5 Apr 2023 22:58:49 -0400 Subject: [PATCH 1/9] style: prefer serde rename_all on structs --- src/catalog/avatar_catalog/mod.rs | 12 ++++-------- src/economy/mod.rs | 8 ++++---- src/economy/reqwest_types.rs | 27 +++++++++++---------------- src/trades/reqwest_types.rs | 7 +++---- src/users/mod.rs | 13 +++++-------- src/users/reqwest_types.rs | 14 ++++---------- 6 files changed, 31 insertions(+), 50 deletions(-) diff --git a/src/catalog/avatar_catalog/mod.rs b/src/catalog/avatar_catalog/mod.rs index 195aaa3..1da872e 100644 --- a/src/catalog/avatar_catalog/mod.rs +++ b/src/catalog/avatar_catalog/mod.rs @@ -144,11 +144,9 @@ pub enum CreatorType { pub enum PriceStatus { #[default] Free, - #[serde(rename(deserialize = "Off Sale"))] - #[serde(rename(deserialize = "Offsale"))] + #[serde(alias = "Off Sale")] Offsale, - #[serde(rename(deserialize = "No Resellers"))] - #[serde(rename(deserialize = "NoResellers"))] + #[serde(alias = "No Resellers")] NoResellers, } @@ -247,12 +245,10 @@ pub enum Subcategory { )] pub struct PremiumPricing { /// The discount percentage in the form of a value from 0-100. - #[serde(rename(deserialize = "premiumDiscountPercentage"))] - #[serde(rename(deserialize = "premium_discount_percentage"))] + #[serde(alias = "premiumDiscountPercentage")] pub premium_discount_percentage: u64, /// The price of the item for premium users. - #[serde(rename(deserialize = "premiumPriceInRobux"))] - #[serde(rename(deserialize = "premium_price_in_robux"))] + #[serde(alias = "premiumPriceInRobux")] pub premium_price_in_robux: u64, } diff --git a/src/economy/mod.rs b/src/economy/mod.rs index a3fe8dd..0aeee1d 100644 --- a/src/economy/mod.rs +++ b/src/economy/mod.rs @@ -260,13 +260,13 @@ impl Client { let mut sales = Vec::new(); for raw_sale in raw.data { - let sale_id = raw_sale.sale_id; + let sale_id = raw_sale.id; let asset_id = raw_sale.details.id; let robux_received = raw_sale.currency.amount; let is_pending = raw_sale.is_pending; - let user_id = raw_sale.user.id; - let user_display_name = raw_sale.user.user_display_name; - let asset_name = raw_sale.details.item_name; + let user_id = raw_sale.agent.id; + let user_display_name = raw_sale.agent.name; + let asset_name = raw_sale.details.name; let sale = UserSale { sale_id, diff --git a/src/economy/reqwest_types.rs b/src/economy/reqwest_types.rs index 7764900..d66b485 100644 --- a/src/economy/reqwest_types.rs +++ b/src/economy/reqwest_types.rs @@ -6,27 +6,25 @@ pub(super) struct CurrencyResponse { } #[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub(super) struct ResellersResponse { - #[serde(rename = "previousPageCursor")] pub previous_page_cursor: Option, - #[serde(rename = "nextPageCursor")] pub next_page_cursor: Option, pub data: Vec, } #[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub(super) struct ListingRaw { - #[serde(rename = "userAssetId")] pub user_asset_id: u64, pub seller: ResellerRaw, pub price: u64, - #[serde(rename = "serialNumber")] pub serial_number: Option, } #[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub(super) struct ResellerRaw { - #[serde(rename = "hasVerifiedBadge")] pub has_verified_badge: bool, pub id: u64, #[serde(rename = "type")] @@ -35,22 +33,19 @@ pub(super) struct ResellerRaw { } #[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub(super) struct UserSalesResponse { - #[serde(rename = "previousPageCursor")] pub previous_page_cursor: Option, - #[serde(rename = "nextPageCursor")] pub next_page_cursor: Option, pub data: Vec, } #[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub(super) struct SaleRaw { - #[serde(rename = "id")] - pub sale_id: u64, - #[serde(rename = "isPending")] + pub id: u64, pub is_pending: bool, - #[serde(rename = "agent")] - pub user: UserRaw, + pub agent: UserRaw, pub details: DetailsRaw, pub currency: CurrencyRaw, } @@ -59,15 +54,15 @@ pub(super) struct SaleRaw { #[derive(Serialize, Deserialize)] pub(super) struct UserRaw { pub id: u64, - #[serde(rename = "name")] - pub user_display_name: String, + /// This is the user's display name. + pub name: String, } #[derive(Serialize, Deserialize)] pub(super) struct DetailsRaw { pub id: u64, - #[serde(rename = "name")] - pub item_name: String, + /// The name of the item. + pub name: String, } #[derive(Serialize, Deserialize)] diff --git a/src/trades/reqwest_types.rs b/src/trades/reqwest_types.rs index bcfb7ec..bc6a622 100644 --- a/src/trades/reqwest_types.rs +++ b/src/trades/reqwest_types.rs @@ -2,29 +2,28 @@ use super::TradeStatus; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub(super) struct InboundTradesResponse { - #[serde(rename = "previousPageCursor")] pub previous_page_cursor: Option, - #[serde(rename = "nextPageCursor")] pub next_page_cursor: Option, pub data: Vec, } #[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub(super) struct TradeRaw { pub id: i64, pub user: TradeUserRaw, pub created: String, pub expiration: String, - #[serde(rename = "isActive")] pub is_active: bool, pub status: TradeStatus, } #[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub(super) struct TradeUserRaw { pub id: i64, pub name: String, - #[serde(rename = "displayName")] pub display_name: String, } diff --git a/src/users/mod.rs b/src/users/mod.rs index 6f6677f..c878721 100644 --- a/src/users/mod.rs +++ b/src/users/mod.rs @@ -12,14 +12,11 @@ const USERS_SEARCH_API: &str = "https://users.roblox.com/v1/users/search"; #[allow(missing_docs)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)] pub(crate) struct ClientUserInformation { - #[serde(rename(deserialize = "id"))] - #[serde(rename(deserialize = "user_id"))] + #[serde(alias = "id")] pub user_id: u64, - #[serde(rename(deserialize = "name"))] - #[serde(rename(deserialize = "username"))] + #[serde(alias = "name")] pub username: String, - #[serde(rename(deserialize = "displayName"))] - #[serde(rename(deserialize = "display_name"))] + #[serde(alias = "displayName")] pub display_name: String, } @@ -120,8 +117,8 @@ impl Client { for user in raw.data { let user_data = User { - user_id: user.user_id, - username: user.username, + user_id: user.id, + username: user.name, display_name: user.display_name, has_verified_badge: user.has_verified_badge, previous_usernames: user.previous_usernames, diff --git a/src/users/reqwest_types.rs b/src/users/reqwest_types.rs index b7b9698..e017200 100644 --- a/src/users/reqwest_types.rs +++ b/src/users/reqwest_types.rs @@ -1,25 +1,19 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub(super) struct UserSearchResponse { - #[serde(rename(deserialize = "previousPageCursor"))] pub previous_page_cursor: Option, - #[serde(rename(deserialize = "nextPageCursor"))] pub next_page_cursor: String, - #[serde(rename(deserialize = "data"))] pub data: Vec, } #[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub(super) struct UserSearchUserInformationRaw { - #[serde(rename(deserialize = "id"))] - pub user_id: u64, - #[serde(rename(deserialize = "name"))] - pub username: String, - #[serde(rename(deserialize = "hasVerifiedBadge"))] + pub id: u64, + pub name: String, pub has_verified_badge: bool, - #[serde(rename(deserialize = "previousUsernames"))] pub previous_usernames: Vec, - #[serde(rename(deserialize = "displayName"))] pub display_name: String, } From 6c87200ff461ee10a77c9f4586f6f755b2d5d827 Mon Sep 17 00:00:00 2001 From: Chloe-Woahie <68732833+Chloe-Woahie@users.noreply.github.com> Date: Wed, 5 Apr 2023 23:03:47 -0400 Subject: [PATCH 2/9] docs: add docs for user info methods --- src/client.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/client.rs b/src/client.rs index 5a34aa1..b25e4cb 100644 --- a/src/client.rs +++ b/src/client.rs @@ -69,6 +69,9 @@ pub struct ClientBuilder { impl Client { /// Returns the user id of the user. If the user id is not cached, it will be fetched from Roblox first. + /// + /// The user id should be the only thing used to differentiate between accounts as + /// username and display name can change. pub async fn user_id(&self) -> Result { let guard = self.user_id.read().await; let user_id_opt = *guard; @@ -86,6 +89,8 @@ impl Client { } /// Returns the username of the user. If the username is not cached, it will be fetched from Roblox first. + /// + /// Username can change (although rarely). For this reason only user id should be used for differentiating accounts. pub async fn username(&self) -> Result { let guard = self.username.read().await; let username_opt = guard.clone(); @@ -103,6 +108,8 @@ impl Client { } /// Returns the display name of the user. If the display name is not cached, it will be fetched from Roblox first. + /// + /// Display name can change. For this reason only user id should be used for differentiating accounts. pub async fn display_name(&self) -> Result { let guard = self.display_name.read().await; let display_name_opt = guard.clone(); From d6f778eccc9460da4f140ede0b89a0451befa88a Mon Sep 17 00:00:00 2001 From: Chloe-Woahie <68732833+Chloe-Woahie@users.noreply.github.com> Date: Wed, 5 Apr 2023 23:03:50 -0400 Subject: [PATCH 3/9] chore: remove old todos --- src/lib.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2b670b7..9207fe5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -136,22 +136,14 @@ mod validation; // todo: endpoints that require premium/robux to test: recent trades, send trade, buy limited item, buy non-limited item // todo: inventory api, groups api, follow api // todo: add usage to readme -// todo: make enums copy -// todo: how to construct a Client isn't clear. // todo: every type should have an explanation of the typical means by which the user will construct or fetch it, if the answer isn't “this is a struct literal with public methods”. // todo: https://docs.rs/roboat/0.8.1/roboat/enum.Limit.html This is a weird type — why does it exist, why not an integer? If it is part of the API requirements then say so. Maybe make it #[non_exhaustive]. // todo: A couple of client methods say “The default limit is Limit::Ten.” but the Limit isn't actually optional -// todo: make it so roblosecurity cant be changed -// todo: make it so users know that username and display name are cached and only id should be used for differentiating stuff. -// todo: try refactoring with cognitive complexity extension -// todo: try serde alias // todo: figure out authtickets // todo: censor roblosecurity in client debug impl with auth_value.set_sensitive(true) // todo: make it so roblosecurity and reqwest client can be set on creation // todo: test an rwlock instead of mutex // todo: add ugc limited buying -// todo: use #[serde(rename_all = "camelCase")] instead of #[serde(rename = "x")] -// todo: add a force refresh method on the client builder use serde::{Deserialize, Serialize}; From 0e6d2baf858264ef96e260aefd5c0c9ceee0530b Mon Sep 17 00:00:00 2001 From: Chloe-Woahie <68732833+Chloe-Woahie@users.noreply.github.com> Date: Wed, 5 Apr 2023 23:28:28 -0400 Subject: [PATCH 4/9] chore: use ClientUserInformation in Client --- src/client.rs | 67 +++++++++++++++++++----------------------------- src/users/mod.rs | 5 +--- 2 files changed, 27 insertions(+), 45 deletions(-) diff --git a/src/client.rs b/src/client.rs index b25e4cb..c999bb9 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,3 +1,4 @@ +use crate::users::ClientUserInformation; use crate::RoboatError; // We use tokio's version of rwlock so that readers to not starve writers on linux. use tokio::sync::RwLock; @@ -50,12 +51,8 @@ pub struct Client { pub(crate) roblosecurity: Option, /// The field holding the value for the X-CSRF-TOKEN header used in and returned by endpoints. pub(crate) xcsrf: RwLock, - /// The user id of the user. Not modifiable by user. - pub(crate) user_id: RwLock>, - /// The username of the user. Not modifiable by user. - pub(crate) username: RwLock>, - /// The display name of the user. Not modifiable by user. - pub(crate) display_name: RwLock>, + /// Holds the user id, username, and display name of the user. + pub(crate) user_information: RwLock>, /// A Reqwest HTTP client used to send web requests. pub(crate) reqwest_client: reqwest::Client, } @@ -73,15 +70,15 @@ impl Client { /// The user id should be the only thing used to differentiate between accounts as /// username and display name can change. pub async fn user_id(&self) -> Result { - let guard = self.user_id.read().await; - let user_id_opt = *guard; + let guard = self.user_information.read().await; + let user_information_opt = &*guard; - // Drop the read lock in case this thread grabs the writer lock later in the function. - drop(guard); - - match user_id_opt { - Some(user_id) => Ok(user_id), + match user_information_opt { + Some(user_information) => Ok(user_information.user_id), None => { + // Drop the read lock as the writer lock will be requested. + drop(guard); + let user_info = self.user_information_internal().await?; Ok(user_info.user_id) } @@ -92,15 +89,15 @@ impl Client { /// /// Username can change (although rarely). For this reason only user id should be used for differentiating accounts. pub async fn username(&self) -> Result { - let guard = self.username.read().await; - let username_opt = guard.clone(); + let guard = self.user_information.read().await; + let user_information_opt = &*guard; - // Drop the read lock in case this thread grabs the writer lock later in the function. - drop(guard); - - match username_opt { - Some(username) => Ok(username), + match user_information_opt { + Some(user_information) => Ok(user_information.username.clone()), None => { + // Drop the read lock as the writer lock will be requested. + drop(guard); + let user_info = self.user_information_internal().await?; Ok(user_info.username) } @@ -111,15 +108,15 @@ impl Client { /// /// Display name can change. For this reason only user id should be used for differentiating accounts. pub async fn display_name(&self) -> Result { - let guard = self.display_name.read().await; - let display_name_opt = guard.clone(); + let guard = self.user_information.read().await; + let user_information_opt = &*guard; - // Drop the read lock in case this thread grabs the writer lock later in the function. - drop(guard); - - match display_name_opt { - Some(display_name) => Ok(display_name), + match user_information_opt { + Some(user_information) => Ok(user_information.display_name.clone()), None => { + // Drop the read lock as the writer lock will be requested. + drop(guard); + let user_info = self.user_information_internal().await?; Ok(user_info.display_name) } @@ -128,20 +125,8 @@ impl Client { /// Used in [`Client::user_information_internal`]. This is implemented in the client /// module as we do not want other modules to have to interact with the rwlock directly. - pub(crate) async fn set_user_id(&self, user_id: u64) { - *self.user_id.write().await = Some(user_id); - } - - /// Used in [`Client::user_information_internal`]. This is implemented in the client - /// module as we do not want other modules to have to interact with the rwlock directly. - pub(crate) async fn set_username(&self, username: String) { - *self.username.write().await = Some(username); - } - - /// Used in [`Client::user_information_internal`]. This is implemented in the client - /// module as we do not want other modules to have to interact with the rwlock directly. - pub(crate) async fn set_display_name(&self, display_name: String) { - *self.display_name.write().await = Some(display_name); + pub(crate) async fn set_user_information(&self, user_information: ClientUserInformation) { + *self.user_information.write().await = Some(user_information); } /// Sets the xcsrf token of the client. Remember to .await this method. diff --git a/src/users/mod.rs b/src/users/mod.rs index c878721..6c3382d 100644 --- a/src/users/mod.rs +++ b/src/users/mod.rs @@ -55,10 +55,7 @@ impl Client { let user_information = Self::parse_to_raw::(response).await?; // Cache results. - self.set_user_id(user_information.user_id as u64).await; - self.set_username(user_information.username.clone()).await; - self.set_display_name(user_information.display_name.clone()) - .await; + self.set_user_information(user_information.clone()).await; Ok(user_information) } From 0c54313f7ccaf90193ee52fb893e7fc6f6610181 Mon Sep 17 00:00:00 2001 From: Chloe-Woahie <68732833+Chloe-Woahie@users.noreply.github.com> Date: Wed, 5 Apr 2023 23:28:41 -0400 Subject: [PATCH 5/9] chore: remove todos --- src/lib.rs | 2 -- src/users/mod.rs | 4 ---- 2 files changed, 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9207fe5..029d827 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -141,8 +141,6 @@ mod validation; // todo: A couple of client methods say “The default limit is Limit::Ten.” but the Limit isn't actually optional // todo: figure out authtickets // todo: censor roblosecurity in client debug impl with auth_value.set_sensitive(true) -// todo: make it so roblosecurity and reqwest client can be set on creation -// todo: test an rwlock instead of mutex // todo: add ugc limited buying use serde::{Deserialize, Serialize}; diff --git a/src/users/mod.rs b/src/users/mod.rs index 6c3382d..bbcbc1a 100644 --- a/src/users/mod.rs +++ b/src/users/mod.rs @@ -60,10 +60,6 @@ impl Client { Ok(user_information) } - // todo: make external example - // todo: make it use roblosecurity if available - // todo: write docs with doc example - // todo: note the previous todos are for this one shark guy and should be resolved within a couple of days (or ill handle it) /// Searches for a user using . /// /// # Notes From 48675b7048274d049fcd8fb774eff2affde6e791 Mon Sep 17 00:00:00 2001 From: Chloe-Woahie <68732833+Chloe-Woahie@users.noreply.github.com> Date: Wed, 5 Apr 2023 23:44:05 -0400 Subject: [PATCH 6/9] docs: fix docs --- src/economy/mod.rs | 2 -- src/lib.rs | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/economy/mod.rs b/src/economy/mod.rs index 0aeee1d..4061648 100644 --- a/src/economy/mod.rs +++ b/src/economy/mod.rs @@ -110,7 +110,6 @@ impl Client { /// /// # Argument Notes /// * The cursor is used to get the a certain page of results. If you want the starting page, use `None`. - /// * The default `limit` is [`Limit::Ten`]. /// /// # Return Value Notes /// * The first value is a vector of reseller listings. @@ -191,7 +190,6 @@ impl Client { /// /// # Argument Notes /// * The cursor is used to get the a certain page of results. If you want the starting page, use `None`. - /// * The default `limit` is [`Limit::Hundred`]. /// /// # Return Value Notes /// * The first value is a vector of user sales. diff --git a/src/lib.rs b/src/lib.rs index 029d827..14974ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -137,8 +137,6 @@ mod validation; // todo: inventory api, groups api, follow api // todo: add usage to readme // todo: every type should have an explanation of the typical means by which the user will construct or fetch it, if the answer isn't “this is a struct literal with public methods”. -// todo: https://docs.rs/roboat/0.8.1/roboat/enum.Limit.html This is a weird type — why does it exist, why not an integer? If it is part of the API requirements then say so. Maybe make it #[non_exhaustive]. -// todo: A couple of client methods say “The default limit is Limit::Ten.” but the Limit isn't actually optional // todo: figure out authtickets // todo: censor roblosecurity in client debug impl with auth_value.set_sensitive(true) // todo: add ugc limited buying @@ -148,7 +146,9 @@ use serde::{Deserialize, Serialize}; // Used in reqwest header keys. const XCSRF_HEADER: &str = "x-csrf-token"; -/// The maximum amount of instances to return from an endpoint. +/// The maximum amount of instances to return from an endpoint. Used as a parameter in various methods that call +/// endpoints. This is an enum instead of an integer as these are the only values that are accepted by Roblox +/// for the limit parameter. #[allow(missing_docs)] #[derive( Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize, Copy, From 73acc74db3883d236087998f09b2de2901250a09 Mon Sep 17 00:00:00 2001 From: Chloe-Woahie <68732833+Chloe-Woahie@users.noreply.github.com> Date: Wed, 5 Apr 2023 23:47:48 -0400 Subject: [PATCH 7/9] chore: add #[non_exhaustive] to RoboatError Not breaking change as it's unlikely anyone was handling all variants --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 14974ec..58ba144 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -173,6 +173,7 @@ impl Limit { } /// The universal error used in this crate. +#[non_exhaustive] #[derive(thiserror::Error, Debug, Default)] pub enum RoboatError { /// Used when an endpoint returns status code 429. From d98060d622597c766f1c74e6651e0a6fd27b629f Mon Sep 17 00:00:00 2001 From: Chloe-Woahie <68732833+Chloe-Woahie@users.noreply.github.com> Date: Thu, 6 Apr 2023 00:17:22 -0400 Subject: [PATCH 8/9] chore: hide roblosecurity in debug impl --- src/client.rs | 31 ++++++++++++++++++++++--------- src/economy/mod.rs | 10 +++++----- src/presence/mod.rs | 2 +- src/trades/mod.rs | 4 ++-- src/users/mod.rs | 9 +++++---- 5 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/client.rs b/src/client.rs index c999bb9..6464492 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,5 +1,6 @@ use crate::users::ClientUserInformation; use crate::RoboatError; +use reqwest::header::HeaderValue; // We use tokio's version of rwlock so that readers to not starve writers on linux. use tokio::sync::RwLock; @@ -47,8 +48,8 @@ use tokio::sync::RwLock; /// ``` #[derive(Debug, Default)] pub struct Client { - /// The cookie used for authentication. - pub(crate) roblosecurity: Option, + /// The full cookie that includes the roblosecurity token. + pub(crate) cookie_string: Option, /// The field holding the value for the X-CSRF-TOKEN header used in and returned by endpoints. pub(crate) xcsrf: RwLock, /// Holds the user id, username, and display name of the user. @@ -139,14 +140,13 @@ impl Client { self.xcsrf.read().await.clone() } - /// Creates a string for the cookie header using the roblosecurity. + /// Returns a copy of the cookie string stored in the client. /// If the roblosecurity has not been set, [`RoboatError::RoblosecurityNotSet`] is returned. - pub(crate) fn create_cookie_string(&self) -> Result { - // We can continue to keep the reader lock as this function will never request a write lock. - let roblosecurity_opt = &self.roblosecurity; + pub(crate) fn cookie_string(&self) -> Result { + let cookie_string_opt = &self.cookie_string; - match roblosecurity_opt { - Some(roblosecurity) => Ok(format!(".ROBLOSECURITY={}", roblosecurity)), + match cookie_string_opt { + Some(cookie) => Ok(cookie.clone()), None => Err(RoboatError::RoblosecurityNotSet), } } @@ -197,9 +197,22 @@ impl ClientBuilder { /// ``` pub fn build(self) -> Client { Client { - roblosecurity: self.roblosecurity, + cookie_string: self + .roblosecurity + .as_ref() + .map(|x| create_cookie_string_header(x)), reqwest_client: self.reqwest_client.unwrap_or_default(), ..Default::default() } } } + +fn create_cookie_string_header(roblosecurity: &str) -> HeaderValue { + // We panic here because I really really really hope that nobody is using invalid characters in their roblosecurity. + let mut header = HeaderValue::from_str(&format!(".ROBLOSECURITY={}", roblosecurity)) + .expect("Invalid roblosecurity characters."); + + header.set_sensitive(true); + + header +} diff --git a/src/economy/mod.rs b/src/economy/mod.rs index 4061648..f44b2ee 100644 --- a/src/economy/mod.rs +++ b/src/economy/mod.rs @@ -86,7 +86,7 @@ impl Client { pub async fn robux(&self) -> Result { let user_id = self.user_id().await?; let formatted_url = format!("{}{}{}", ROBUX_API_PART_1, user_id, ROBUX_API_PART_2); - let cookie = self.create_cookie_string()?; + let cookie = self.cookie_string()?; let request_result = self .reqwest_client @@ -143,7 +143,7 @@ impl Client { ) -> Result<(Vec, Option), RoboatError> { let limit = limit.to_u64(); let cursor = cursor.unwrap_or_default(); - let cookie = self.create_cookie_string()?; + let cookie = self.cookie_string()?; let formatted_url = format!( "{}{}{}?cursor={}&limit={}", @@ -241,7 +241,7 @@ impl Client { USER_SALES_TRANSACTION_TYPE ); - let cookie = self.create_cookie_string()?; + let cookie = self.cookie_string()?; let request_result = self .reqwest_client @@ -396,7 +396,7 @@ mod internal { TOGGLE_SALE_API_PART_1, item_id, TOGGLE_SALE_API_PART_2, uaid ); - let cookie = self.create_cookie_string()?; + let cookie = self.cookie_string()?; let json = serde_json::json!({ "price": price, @@ -428,7 +428,7 @@ mod internal { TOGGLE_SALE_API_PART_1, item_id, TOGGLE_SALE_API_PART_2, uaid ); - let cookie = self.create_cookie_string()?; + let cookie = self.cookie_string()?; let json = serde_json::json!({}); diff --git a/src/presence/mod.rs b/src/presence/mod.rs index 8c59bfb..7366857 100644 --- a/src/presence/mod.rs +++ b/src/presence/mod.rs @@ -54,7 +54,7 @@ mod internal { impl Client { pub(super) async fn register_presence_internal(&self) -> Result<(), RoboatError> { - let cookie = self.create_cookie_string()?; + let cookie = self.cookie_string()?; let json = serde_json::json!({ "location": "Home", diff --git a/src/trades/mod.rs b/src/trades/mod.rs index f677ab7..8d10923 100644 --- a/src/trades/mod.rs +++ b/src/trades/mod.rs @@ -98,7 +98,7 @@ impl Client { let limit = limit.to_u64(); let cursor = cursor.unwrap_or_default(); - let roblosecurity = self.create_cookie_string()?; + let cookie_string = self.cookie_string()?; let trade_type_str = match trade_type { TradeType::Inbound => "inbound", @@ -115,7 +115,7 @@ impl Client { let request_result = self .reqwest_client .get(&formatted_url) - .header(header::COOKIE, roblosecurity) + .header(header::COOKIE, cookie_string) .send() .await; diff --git a/src/users/mod.rs b/src/users/mod.rs index bbcbc1a..3dcd58a 100644 --- a/src/users/mod.rs +++ b/src/users/mod.rs @@ -1,5 +1,5 @@ use crate::{Client, RoboatError}; -use reqwest::header; +use reqwest::header::{self, HeaderValue}; use serde::{Deserialize, Serialize}; mod reqwest_types; @@ -42,7 +42,7 @@ impl Client { pub(crate) async fn user_information_internal( &self, ) -> Result { - let cookie = self.create_cookie_string()?; + let cookie = self.cookie_string()?; let request_result = self .reqwest_client @@ -94,12 +94,13 @@ impl Client { /// ``` pub async fn user_search(&self, keyword: String) -> Result, RoboatError> { let formatted_url = format!("{}?keyword={}", USERS_SEARCH_API, keyword); - let roblosecurity = self.create_cookie_string().unwrap_or_default(); + + let cookie_string = self.cookie_string().unwrap_or(HeaderValue::from_static("")); let request_result = self .reqwest_client .get(formatted_url) - .header(header::COOKIE, roblosecurity) + .header(header::COOKIE, cookie_string) .send() .await; From fa4b062b527a24c305696b2e712d6261b41b9893 Mon Sep 17 00:00:00 2001 From: Chloe-Woahie <68732833+Chloe-Woahie@users.noreply.github.com> Date: Thu, 6 Apr 2023 00:17:31 -0400 Subject: [PATCH 9/9] chore: increase crate version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index fcf87e2..0041cb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT" name = "roboat" readme = "README.md" repository = "https://github.com/Chloe-Woahie/roboat" -version = "0.10.0" +version = "0.10.1" [dependencies] reqwest = { version = "0.11.14", default-features=false, features = ["rustls-tls", "json"] }