v0.4.7 user inventory + git fix #8

Merged
Quaternions merged 9 commits from staging into master 2024-10-01 20:10:30 +00:00
2 changed files with 56 additions and 27 deletions
Showing only changes of commit ac674778f6 - Show all commits

View File

@ -114,19 +114,31 @@ impl std::fmt::Display for AssetVersionsPageError{
}
impl std::error::Error for AssetVersionsPageError{}
pub struct InventoryPageRequest{
pub group:u64,
pub enum Owner{
User(u64),
Group(u64),
}
impl Owner{
fn get_url_info(&self)->(&str,u64){
match self{
&Owner::User(id)=>("user",id),
&Owner::Group(id)=>("group",id),
}
}
}
pub struct CreationsPageRequest{
pub owner:Owner,
pub cursor:Option<String>,
}
#[derive(serde::Deserialize,serde::Serialize)]
#[allow(nonstandard_style,dead_code)]
pub struct InventoryItem{
pub struct CreationsItem{
pub id:u64,
pub name:String,
}
#[derive(serde::Deserialize,serde::Serialize)]
#[allow(nonstandard_style,dead_code)]
pub struct InventoryPageResponse{
pub struct CreationsPageResponse{
pub totalResults:u64,//up to 50
pub filteredKeyword:Option<String>,//""
pub searchDebugInfo:Option<String>,//null
@ -135,19 +147,19 @@ pub struct InventoryPageResponse{
pub imageSearchStatus:Option<String>,//null
pub previousPageCursor:Option<String>,
pub nextPageCursor:Option<String>,
pub data:Vec<InventoryItem>,
pub data:Vec<CreationsItem>,
}
#[derive(Debug)]
pub enum InventoryPageError{
pub enum CreationsPageError{
ParseError(url::ParseError),
Reqwest(reqwest::Error),
}
impl std::fmt::Display for InventoryPageError{
impl std::fmt::Display for CreationsPageError{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f,"{self:?}")
}
}
impl std::error::Error for InventoryPageError{}
impl std::error::Error for CreationsPageError{}
//idk how to do this better
enum ReaderType<R:std::io::Read>{
@ -305,8 +317,9 @@ impl CookieContext{
.error_for_status().map_err(AssetVersionsPageError::Reqwest)?
.json::<AssetVersionsPageResponse>().await.map_err(AssetVersionsPageError::Reqwest)
}
pub async fn get_inventory_page(&self,config:InventoryPageRequest)->Result<InventoryPageResponse,InventoryPageError>{
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)?;
pub async fn get_creations_page(&self,config:&CreationsPageRequest)->Result<CreationsPageResponse,CreationsPageError>{
let (owner,id)=config.owner.get_url_info();
let mut url=reqwest::Url::parse(format!("https://apis.roblox.com/toolbox-service/v1/creations/{}/{}/10?limit=50",owner,id).as_str()).map_err(CreationsPageError::ParseError)?;
//url borrow scope
{
let mut query=url.query_pairs_mut();//borrow here
@ -315,8 +328,8 @@ impl CookieContext{
}
}
self.get(url).await.map_err(InventoryPageError::Reqwest)?
.error_for_status().map_err(InventoryPageError::Reqwest)?
.json::<InventoryPageResponse>().await.map_err(InventoryPageError::Reqwest)
self.get(url).await.map_err(CreationsPageError::Reqwest)?
.error_for_status().map_err(CreationsPageError::Reqwest)?
.json::<CreationsPageResponse>().await.map_err(CreationsPageError::Reqwest)
}
}

View File

@ -3,7 +3,7 @@ use clap::{Args,Parser,Subcommand};
use anyhow::{anyhow,Result as AResult};
use futures::StreamExt;
use rbx_asset::cloud::{ApiKey,CloudContext};
use rbx_asset::cookie::{Cookie,CookieContext,AssetVersion,InventoryItem};
use rbx_asset::cookie::{Cookie,CookieContext,AssetVersion,CreationsItem};
type AssetID=u64;
type AssetIDFileMap=Vec<(AssetID,PathBuf)>;
@ -23,7 +23,7 @@ enum Commands{
DownloadHistory(DownloadHistorySubcommand),
Download(DownloadSubcommand),
DownloadDecompile(DownloadDecompileSubcommand),
DownloadGroupInventoryJson(DownloadGroupInventoryJsonSubcommand),
DownloadCreationsJson(DownloadCreationsJsonSubcommand),
CreateAsset(CreateAssetSubcommand),
CreateAssetMedia(CreateAssetMediaSubcommand),
CreateAssetMedias(CreateAssetMediasSubcommand),
@ -72,9 +72,9 @@ struct DownloadSubcommand{
#[arg(required=true)]
asset_ids:Vec<AssetID>,
}
/// Download the list of asset ids (not the assets themselves) in a group inventory. The output is written to `output_folder/versions.json`
/// Download the list of asset ids (not the assets themselves) created by a group or user. The output is written to `output_folder/versions.json`
#[derive(Args)]
struct DownloadGroupInventoryJsonSubcommand{
struct DownloadCreationsJsonSubcommand{
#[arg(long,group="cookie",required=true)]
cookie_literal:Option<String>,
#[arg(long,group="cookie",required=true)]
@ -83,8 +83,10 @@ struct DownloadGroupInventoryJsonSubcommand{
cookie_file:Option<PathBuf>,
#[arg(long)]
output_folder:Option<PathBuf>,
#[arg(long)]
group:u64,
#[arg(long,group="owner",required=true)]
group_id:Option<u64>,
#[arg(long,group="owner",required=true)]
user_id:Option<u64>,
}
/// Upload a (.rbxm, .rbxmx) model file, creating a new asset. Can be any type of model, including modulescripts.
#[derive(Args)]
@ -427,13 +429,16 @@ async fn main()->AResult<()>{
write_scripts:subcommand.write_scripts.unwrap_or(true),
}).await
},
Commands::DownloadGroupInventoryJson(subcommand)=>download_group_inventory_json(
Commands::DownloadCreationsJson(subcommand)=>download_creations_json(
cookie_from_args(
subcommand.cookie_literal,
subcommand.cookie_envvar,
subcommand.cookie_file,
).await?,
subcommand.group,
owner_from_args(
subcommand.user_id,
subcommand.group_id,
)?,
subcommand.output_folder.unwrap_or_else(||std::env::current_dir().unwrap()),
).await,
Commands::CreateAsset(subcommand)=>create_asset(CreateAssetConfig{
@ -603,6 +608,14 @@ async fn api_key_from_args(literal:Option<String>,environment:Option<String>,fil
};
Ok(ApiKey::new(api_key))
}
fn owner_from_args(user_id:Option<u64>,group_id:Option<u64>)->AResult<rbx_asset::cookie::Owner>{
let owner=match (user_id,group_id){
(Some(id),None)=>rbx_asset::cookie::Owner::User(id),
(None,Some(id))=>rbx_asset::cookie::Owner::Group(id),
_=>Err(anyhow::Error::msg("Illegal owner argument pair"))?,
};
Ok(owner)
}
struct CreateAssetConfig{
cookie:Cookie,
@ -890,23 +903,26 @@ async fn download_list(cookie:Cookie,asset_id_file_map:AssetIDFileMap)->AResult<
Ok(())
}
async fn get_inventory_pages(context:&CookieContext,group:u64)->AResult<Vec<InventoryItem>>{
let mut cursor:Option<String>=None;
async fn get_creations_pages(context:&CookieContext,owner:rbx_asset::cookie::Owner)->AResult<Vec<CreationsItem>>{
let mut config=rbx_asset::cookie::CreationsPageRequest{
owner,
cursor:None,
};
let mut asset_list=Vec::new();
loop{
let mut page=context.get_inventory_page(rbx_asset::cookie::InventoryPageRequest{group,cursor}).await?;
let mut page=context.get_creations_page(&config).await?;
asset_list.append(&mut page.data);
if page.nextPageCursor.is_none(){
break;
}
cursor=page.nextPageCursor;
config.cursor=page.nextPageCursor;
}
Ok(asset_list)
}
async fn download_group_inventory_json(cookie:Cookie,group:u64,output_folder:PathBuf)->AResult<()>{
async fn download_creations_json(cookie:Cookie,owner:rbx_asset::cookie::Owner,output_folder:PathBuf)->AResult<()>{
let context=CookieContext::new(cookie);
let item_list=get_inventory_pages(&context,group).await?;
let item_list=get_creations_pages(&context,owner).await?;
let mut path=output_folder.clone();
path.set_file_name("versions.json");