v0.4.7 user inventory + git fix #8
@ -114,19 +114,31 @@ impl std::fmt::Display for AssetVersionsPageError{
|
|||||||
}
|
}
|
||||||
impl std::error::Error for AssetVersionsPageError{}
|
impl std::error::Error for AssetVersionsPageError{}
|
||||||
|
|
||||||
pub struct InventoryPageRequest{
|
pub enum Owner{
|
||||||
pub group:u64,
|
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>,
|
pub cursor:Option<String>,
|
||||||
}
|
}
|
||||||
#[derive(serde::Deserialize,serde::Serialize)]
|
#[derive(serde::Deserialize,serde::Serialize)]
|
||||||
#[allow(nonstandard_style,dead_code)]
|
#[allow(nonstandard_style,dead_code)]
|
||||||
pub struct InventoryItem{
|
pub struct CreationsItem{
|
||||||
pub id:u64,
|
pub id:u64,
|
||||||
pub name:String,
|
pub name:String,
|
||||||
}
|
}
|
||||||
#[derive(serde::Deserialize,serde::Serialize)]
|
#[derive(serde::Deserialize,serde::Serialize)]
|
||||||
#[allow(nonstandard_style,dead_code)]
|
#[allow(nonstandard_style,dead_code)]
|
||||||
pub struct InventoryPageResponse{
|
pub struct CreationsPageResponse{
|
||||||
pub totalResults:u64,//up to 50
|
pub totalResults:u64,//up to 50
|
||||||
pub filteredKeyword:Option<String>,//""
|
pub filteredKeyword:Option<String>,//""
|
||||||
pub searchDebugInfo:Option<String>,//null
|
pub searchDebugInfo:Option<String>,//null
|
||||||
@ -135,19 +147,19 @@ pub struct InventoryPageResponse{
|
|||||||
pub imageSearchStatus:Option<String>,//null
|
pub imageSearchStatus:Option<String>,//null
|
||||||
pub previousPageCursor:Option<String>,
|
pub previousPageCursor:Option<String>,
|
||||||
pub nextPageCursor:Option<String>,
|
pub nextPageCursor:Option<String>,
|
||||||
pub data:Vec<InventoryItem>,
|
pub data:Vec<CreationsItem>,
|
||||||
}
|
}
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum InventoryPageError{
|
pub enum CreationsPageError{
|
||||||
ParseError(url::ParseError),
|
ParseError(url::ParseError),
|
||||||
Reqwest(reqwest::Error),
|
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 {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f,"{self:?}")
|
write!(f,"{self:?}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl std::error::Error for InventoryPageError{}
|
impl std::error::Error for CreationsPageError{}
|
||||||
|
|
||||||
//idk how to do this better
|
//idk how to do this better
|
||||||
enum ReaderType<R:std::io::Read>{
|
enum ReaderType<R:std::io::Read>{
|
||||||
@ -305,8 +317,9 @@ impl CookieContext{
|
|||||||
.error_for_status().map_err(AssetVersionsPageError::Reqwest)?
|
.error_for_status().map_err(AssetVersionsPageError::Reqwest)?
|
||||||
.json::<AssetVersionsPageResponse>().await.map_err(AssetVersionsPageError::Reqwest)
|
.json::<AssetVersionsPageResponse>().await.map_err(AssetVersionsPageError::Reqwest)
|
||||||
}
|
}
|
||||||
pub async fn get_inventory_page(&self,config:InventoryPageRequest)->Result<InventoryPageResponse,InventoryPageError>{
|
pub async fn get_creations_page(&self,config:&CreationsPageRequest)->Result<CreationsPageResponse,CreationsPageError>{
|
||||||
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)?;
|
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
|
//url borrow scope
|
||||||
{
|
{
|
||||||
let mut query=url.query_pairs_mut();//borrow here
|
let mut query=url.query_pairs_mut();//borrow here
|
||||||
@ -315,8 +328,8 @@ impl CookieContext{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.get(url).await.map_err(InventoryPageError::Reqwest)?
|
self.get(url).await.map_err(CreationsPageError::Reqwest)?
|
||||||
.error_for_status().map_err(InventoryPageError::Reqwest)?
|
.error_for_status().map_err(CreationsPageError::Reqwest)?
|
||||||
.json::<InventoryPageResponse>().await.map_err(InventoryPageError::Reqwest)
|
.json::<CreationsPageResponse>().await.map_err(CreationsPageError::Reqwest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
44
src/main.rs
44
src/main.rs
@ -3,7 +3,7 @@ use clap::{Args,Parser,Subcommand};
|
|||||||
use anyhow::{anyhow,Result as AResult};
|
use anyhow::{anyhow,Result as AResult};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use rbx_asset::cloud::{ApiKey,CloudContext};
|
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 AssetID=u64;
|
||||||
type AssetIDFileMap=Vec<(AssetID,PathBuf)>;
|
type AssetIDFileMap=Vec<(AssetID,PathBuf)>;
|
||||||
@ -23,7 +23,7 @@ enum Commands{
|
|||||||
DownloadHistory(DownloadHistorySubcommand),
|
DownloadHistory(DownloadHistorySubcommand),
|
||||||
Download(DownloadSubcommand),
|
Download(DownloadSubcommand),
|
||||||
DownloadDecompile(DownloadDecompileSubcommand),
|
DownloadDecompile(DownloadDecompileSubcommand),
|
||||||
DownloadGroupInventoryJson(DownloadGroupInventoryJsonSubcommand),
|
DownloadCreationsJson(DownloadCreationsJsonSubcommand),
|
||||||
CreateAsset(CreateAssetSubcommand),
|
CreateAsset(CreateAssetSubcommand),
|
||||||
CreateAssetMedia(CreateAssetMediaSubcommand),
|
CreateAssetMedia(CreateAssetMediaSubcommand),
|
||||||
CreateAssetMedias(CreateAssetMediasSubcommand),
|
CreateAssetMedias(CreateAssetMediasSubcommand),
|
||||||
@ -72,9 +72,9 @@ struct DownloadSubcommand{
|
|||||||
#[arg(required=true)]
|
#[arg(required=true)]
|
||||||
asset_ids:Vec<AssetID>,
|
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)]
|
#[derive(Args)]
|
||||||
struct DownloadGroupInventoryJsonSubcommand{
|
struct DownloadCreationsJsonSubcommand{
|
||||||
#[arg(long,group="cookie",required=true)]
|
#[arg(long,group="cookie",required=true)]
|
||||||
cookie_literal:Option<String>,
|
cookie_literal:Option<String>,
|
||||||
#[arg(long,group="cookie",required=true)]
|
#[arg(long,group="cookie",required=true)]
|
||||||
@ -83,8 +83,10 @@ struct DownloadGroupInventoryJsonSubcommand{
|
|||||||
cookie_file:Option<PathBuf>,
|
cookie_file:Option<PathBuf>,
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
output_folder:Option<PathBuf>,
|
output_folder:Option<PathBuf>,
|
||||||
#[arg(long)]
|
#[arg(long,group="owner",required=true)]
|
||||||
group:u64,
|
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.
|
/// Upload a (.rbxm, .rbxmx) model file, creating a new asset. Can be any type of model, including modulescripts.
|
||||||
#[derive(Args)]
|
#[derive(Args)]
|
||||||
@ -427,13 +429,16 @@ async fn main()->AResult<()>{
|
|||||||
write_scripts:subcommand.write_scripts.unwrap_or(true),
|
write_scripts:subcommand.write_scripts.unwrap_or(true),
|
||||||
}).await
|
}).await
|
||||||
},
|
},
|
||||||
Commands::DownloadGroupInventoryJson(subcommand)=>download_group_inventory_json(
|
Commands::DownloadCreationsJson(subcommand)=>download_creations_json(
|
||||||
cookie_from_args(
|
cookie_from_args(
|
||||||
subcommand.cookie_literal,
|
subcommand.cookie_literal,
|
||||||
subcommand.cookie_envvar,
|
subcommand.cookie_envvar,
|
||||||
subcommand.cookie_file,
|
subcommand.cookie_file,
|
||||||
).await?,
|
).await?,
|
||||||
subcommand.group,
|
owner_from_args(
|
||||||
|
subcommand.user_id,
|
||||||
|
subcommand.group_id,
|
||||||
|
)?,
|
||||||
subcommand.output_folder.unwrap_or_else(||std::env::current_dir().unwrap()),
|
subcommand.output_folder.unwrap_or_else(||std::env::current_dir().unwrap()),
|
||||||
).await,
|
).await,
|
||||||
Commands::CreateAsset(subcommand)=>create_asset(CreateAssetConfig{
|
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))
|
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{
|
struct CreateAssetConfig{
|
||||||
cookie:Cookie,
|
cookie:Cookie,
|
||||||
@ -890,23 +903,26 @@ async fn download_list(cookie:Cookie,asset_id_file_map:AssetIDFileMap)->AResult<
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_inventory_pages(context:&CookieContext,group:u64)->AResult<Vec<InventoryItem>>{
|
async fn get_creations_pages(context:&CookieContext,owner:rbx_asset::cookie::Owner)->AResult<Vec<CreationsItem>>{
|
||||||
let mut cursor:Option<String>=None;
|
let mut config=rbx_asset::cookie::CreationsPageRequest{
|
||||||
|
owner,
|
||||||
|
cursor:None,
|
||||||
|
};
|
||||||
let mut asset_list=Vec::new();
|
let mut asset_list=Vec::new();
|
||||||
loop{
|
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);
|
asset_list.append(&mut page.data);
|
||||||
if page.nextPageCursor.is_none(){
|
if page.nextPageCursor.is_none(){
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cursor=page.nextPageCursor;
|
config.cursor=page.nextPageCursor;
|
||||||
}
|
}
|
||||||
Ok(asset_list)
|
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 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();
|
let mut path=output_folder.clone();
|
||||||
path.set_file_name("versions.json");
|
path.set_file_name("versions.json");
|
||||||
|
Loading…
Reference in New Issue
Block a user