diff --git a/rbx_asset/src/cookie.rs b/rbx_asset/src/cookie.rs index b89cd32..d5b5ceb 100644 --- a/rbx_asset/src/cookie.rs +++ b/rbx_asset/src/cookie.rs @@ -79,7 +79,7 @@ impl std::fmt::Display for DownloadError{ } impl std::error::Error for DownloadError{} -pub struct HistoryPageRequest{ +pub struct AssetVersionsPageRequest{ pub asset_id:u64, pub cursor:Option, } @@ -97,22 +97,22 @@ pub struct AssetVersion{ } #[derive(serde::Deserialize)] #[allow(nonstandard_style,dead_code)] -pub struct HistoryPageResponse{ +pub struct AssetVersionsPageResponse{ pub previousPageCursor:Option, pub nextPageCursor:Option, pub data:Vec, } #[derive(Debug)] -pub enum HistoryPageError{ +pub enum AssetVersionsPageError{ ParseError(url::ParseError), Reqwest(reqwest::Error), } -impl std::fmt::Display for HistoryPageError{ +impl std::fmt::Display for AssetVersionsPageError{ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f,"{self:?}") } } -impl std::error::Error for HistoryPageError{} +impl std::error::Error for AssetVersionsPageError{} pub struct InventoryPageRequest{ pub group:u64, @@ -289,8 +289,8 @@ impl CookieContext{ Err(e)=>Err(e), }.map_err(DownloadError::IO) } - pub async fn history_page(&self,config:HistoryPageRequest)->Result{ - let mut url=reqwest::Url::parse(format!("https://develop.roblox.com/v1/assets/{}/saved-versions",config.asset_id).as_str()).map_err(HistoryPageError::ParseError)?; + pub async fn get_asset_versions_page(&self,config:AssetVersionsPageRequest)->Result{ + let mut url=reqwest::Url::parse(format!("https://develop.roblox.com/v1/assets/{}/saved-versions",config.asset_id).as_str()).map_err(AssetVersionsPageError::ParseError)?; //url borrow scope { let mut query=url.query_pairs_mut();//borrow here @@ -302,8 +302,8 @@ impl CookieContext{ } } - Ok(self.get(url).await.map_err(HistoryPageError::Reqwest)? - .json::().await.map_err(HistoryPageError::Reqwest)?) + Ok(self.get(url).await.map_err(AssetVersionsPageError::Reqwest)? + .json::().await.map_err(AssetVersionsPageError::Reqwest)?) } pub async fn inventory_page(&self,config:InventoryPageRequest)->Result{ let mut url=reqwest::Url::parse(format!("https://apis.roblox.com/toolbox-service/v1/creations/group/{}/10?limit=50",config.group).as_str()).map_err(InventoryPageError::ParseError)?; diff --git a/src/main.rs b/src/main.rs index eff4e17..4aa886c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,8 @@ use std::{io::Read,path::PathBuf}; use clap::{Args,Parser,Subcommand}; use anyhow::Result as AResult; use futures::StreamExt; -use rbx_asset::cloud::{ApiKey,AssetVersion,CloudContext,InventoryItem}; +use rbx_asset::cloud::{ApiKey,CloudContext,InventoryItem}; +use rbx_asset::cookie::{Cookie,CookieContext,AssetVersion}; type AssetID=u64; type AssetIDFileMap=Vec<(AssetID,PathBuf)>; @@ -38,12 +39,12 @@ enum Commands{ struct DownloadHistorySubcommand{ #[arg(long)] asset_id:AssetID, - #[arg(long,group="api_key",required=true)] - api_key_literal:Option, - #[arg(long,group="api_key",required=true)] - api_key_envvar:Option, - #[arg(long,group="api_key",required=true)] - api_key_file:Option, + #[arg(long,group="cookie",required=true)] + cookie_literal:Option, + #[arg(long,group="cookie",required=true)] + cookie_envvar:Option, + #[arg(long,group="cookie",required=true)] + cookie_file:Option, #[arg(long)] output_folder:Option, #[arg(long)] @@ -68,12 +69,12 @@ struct DownloadSubcommand{ } #[derive(Args)] struct DownloadGroupInventoryJsonSubcommand{ - #[arg(long,group="api_key",required=true)] - api_key_literal:Option, - #[arg(long,group="api_key",required=true)] - api_key_envvar:Option, - #[arg(long,group="api_key",required=true)] - api_key_file:Option, + #[arg(long,group="cookie",required=true)] + cookie_literal:Option, + #[arg(long,group="cookie",required=true)] + cookie_envvar:Option, + #[arg(long,group="cookie",required=true)] + cookie_file:Option, #[arg(long)] output_folder:Option, #[arg(long)] @@ -283,10 +284,10 @@ async fn main()->AResult<()>{ end_version:subcommand.end_version, start_version:subcommand.start_version.unwrap_or(0), output_folder:subcommand.output_folder.unwrap_or_else(||std::env::current_dir().unwrap()), - api_key:api_key_from_args( - subcommand.api_key_literal, - subcommand.api_key_envvar, - subcommand.api_key_file, + cookie:cookie_from_args( + subcommand.cookie_literal, + subcommand.cookie_envvar, + subcommand.cookie_file, ).await?, asset_id:subcommand.asset_id, }).await, @@ -321,10 +322,10 @@ async fn main()->AResult<()>{ }).await }, Commands::DownloadGroupInventoryJson(subcommand)=>download_group_inventory_json( - api_key_from_args( - subcommand.api_key_literal, - subcommand.api_key_envvar, - subcommand.api_key_file, + cookie_from_args( + subcommand.cookie_literal, + subcommand.cookie_envvar, + subcommand.cookie_file, ).await?, subcommand.group, subcommand.output_folder.unwrap_or_else(||std::env::current_dir().unwrap()), @@ -425,6 +426,15 @@ async fn main()->AResult<()>{ } } +async fn cookie_from_args(literal:Option,environment:Option,file:Option)->AResult{ + let cookie=match (literal,environment,file){ + (Some(cookie_literal),None,None)=>cookie_literal, + (None,Some(cookie_environment),None)=>std::env::var(cookie_environment)?, + (None,None,Some(cookie_file))=>tokio::fs::read_to_string(cookie_file).await?, + _=>Err(anyhow::Error::msg("Illegal api key argument triple"))?, + }; + Ok(Cookie::new(cookie)) +} async fn api_key_from_args(literal:Option,environment:Option,file:Option)->AResult{ let api_key=match (literal,environment,file){ (Some(api_key_literal),None,None)=>api_key_literal, @@ -517,11 +527,11 @@ async fn download_list(api_key:ApiKey,asset_id_file_map:AssetIDFileMap)->AResult Ok(()) } -async fn get_inventory_pages(context:&CloudContext,group:u64)->AResult>{ +async fn get_inventory_pages(context:&CookieContext,group:u64)->AResult>{ let mut cursor:Option=None; let mut asset_list=Vec::new(); loop{ - let mut page=context.inventory_page(rbx_asset::cloud::InventoryPageRequest{group,cursor}).await?; + let mut page=context.inventory_page(rbx_asset::cookie::InventoryPageRequest{group,cursor}).await?; asset_list.append(&mut page.data); if page.nextPageCursor.is_none(){ break; @@ -531,8 +541,8 @@ async fn get_inventory_pages(context:&CloudContext,group:u64)->AResultAResult<()>{ - let context=CloudContext::new(api_key); +async fn download_group_inventory_json(cookie:Cookie,group:u64,output_folder:PathBuf)->AResult<()>{ + let context=CookieContext::new(cookie); let item_list=get_inventory_pages(&context,group).await?; let mut path=output_folder.clone(); @@ -562,7 +572,7 @@ struct DownloadHistoryConfig{ end_version:Option, start_version:u64, output_folder:PathBuf, - api_key:ApiKey, + cookie:Cookie, asset_id:AssetID, } @@ -603,7 +613,7 @@ async fn download_history(mut config:DownloadHistoryConfig)->AResult<()>{ None=>Err(anyhow::Error::msg("Cannot continue from versions.json - there are no previous versions"))?, } } - let context=CloudContext::new(config.api_key); + let context=CookieContext::new(config.cookie); //limit concurrent downloads let mut join_set=tokio::task::JoinSet::new(); @@ -611,7 +621,7 @@ async fn download_history(mut config:DownloadHistoryConfig)->AResult<()>{ //poll paged list of all asset versions let mut cursor:Option=None; loop{ - let mut page=context.get_asset_versions(rbx_asset::cloud::AssetVersionsRequest{asset_id:config.asset_id,cursor}).await?; + let mut page=context.get_asset_versions_page(rbx_asset::cookie::AssetVersionsPageRequest{asset_id:config.asset_id,cursor}).await?; let context=&context; let output_folder=config.output_folder.clone(); let data=&page.data;