Set Universe Asset Permissions #17
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -1339,7 +1339,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rbx_asset"
|
||||
version = "0.4.5"
|
||||
version = "0.4.6"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"chrono",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rbx_asset"
|
||||
version = "0.4.5"
|
||||
version = "0.4.6"
|
||||
edition = "2021"
|
||||
publish = ["strafesnet"]
|
||||
repository = "https://git.itzana.me/StrafesNET/asset-tool"
|
||||
|
||||
44
rbx_asset/src/body.rs
Normal file
44
rbx_asset/src/body.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use reqwest::Body;
|
||||
|
||||
pub trait ContentType:Into<Body>{
|
||||
fn content_type(&self)->&'static str;
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy,Debug)]
|
||||
pub struct Json<T>(pub(crate)T);
|
||||
impl<T:Into<Body>> From<Json<T>> for Body{
|
||||
fn from(Json(value):Json<T>)->Self{
|
||||
value.into()
|
||||
}
|
||||
}
|
||||
impl<T:Into<Body>> ContentType for Json<T>{
|
||||
fn content_type(&self)->&'static str{
|
||||
"application/json"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy,Debug)]
|
||||
pub struct Text<T>(pub(crate)T);
|
||||
impl<T:Into<Body>> From<Text<T>> for Body{
|
||||
fn from(Text(value):Text<T>)->Self{
|
||||
value.into()
|
||||
}
|
||||
}
|
||||
impl<T:Into<Body>> ContentType for Text<T>{
|
||||
fn content_type(&self)->&'static str{
|
||||
"text/plain"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy,Debug)]
|
||||
pub struct Binary<T>(pub(crate)T);
|
||||
impl<T:Into<Body>> From<Binary<T>> for Body{
|
||||
fn from(Binary(value):Binary<T>)->Self{
|
||||
value.into()
|
||||
}
|
||||
}
|
||||
impl<T:Into<Body>> ContentType for Binary<T>{
|
||||
fn content_type(&self)->&'static str{
|
||||
"application/octet-stream"
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::body::{ContentType,Json};
|
||||
use crate::util::response_ok;
|
||||
use crate::types::{ResponseError,MaybeGzippedBytes};
|
||||
|
||||
@@ -306,6 +307,58 @@ pub struct UserInventoryPageResponse{
|
||||
pub data:Vec<UserInventoryItem>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SetAssetsPermissionsError{
|
||||
Parse(url::ParseError),
|
||||
JSONEncode(serde_json::Error),
|
||||
Patch(PostError),
|
||||
Response(ResponseError),
|
||||
Reqwest(reqwest::Error),
|
||||
}
|
||||
impl std::fmt::Display for SetAssetsPermissionsError{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f,"{self:?}")
|
||||
}
|
||||
}
|
||||
impl std::error::Error for SetAssetsPermissionsError{}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
#[allow(nonstandard_style)]
|
||||
struct AssetPermissions{
|
||||
assetId:u64,
|
||||
grantToDependencies:bool,//true
|
||||
}
|
||||
#[derive(serde::Serialize)]
|
||||
#[allow(nonstandard_style)]
|
||||
struct SetAssetsPermissions<'a>{
|
||||
subjectType:&'a str,// "Universe"
|
||||
subjectId:&'a str,// "4422715291"
|
||||
action:&'a str,// "Use",
|
||||
enableDeepAccessCheck:bool,//true,
|
||||
requests:&'a [AssetPermissions],
|
||||
}
|
||||
pub struct SetAssetsPermissionsRequest<'a>{
|
||||
pub universe_id:u64,
|
||||
pub asset_ids:&'a [u64],
|
||||
}
|
||||
impl SetAssetsPermissionsRequest<'_>{
|
||||
fn serialize(&self)->Result<String,serde_json::Error>{
|
||||
let requests:&Vec<_>=&self.asset_ids.iter().map(|&asset_id|AssetPermissions{
|
||||
assetId:asset_id,
|
||||
grantToDependencies:true,
|
||||
}).collect();
|
||||
let subject_id=&self.universe_id.to_string();
|
||||
let permissions=SetAssetsPermissions{
|
||||
subjectType:"Universe",
|
||||
subjectId:subject_id,
|
||||
action:"Use",
|
||||
enableDeepAccessCheck:true,
|
||||
requests,
|
||||
};
|
||||
serde_json::to_string(&permissions)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Cookie(String);
|
||||
impl Cookie{
|
||||
@@ -356,6 +409,29 @@ impl Context{
|
||||
|
||||
Ok(resp)
|
||||
}
|
||||
async fn patch(&self,url:url::Url,body:impl ContentType+Clone)->Result<reqwest::Response,PostError>{
|
||||
let mut resp=self.client.patch(url.clone())
|
||||
.header("Cookie",self.cookie.as_str())
|
||||
.header("Content-Type",body.content_type())
|
||||
.body(body.clone())
|
||||
.send().await.map_err(PostError::Reqwest)?;
|
||||
|
||||
//This is called a CSRF challenge apparently
|
||||
if resp.status()==reqwest::StatusCode::FORBIDDEN{
|
||||
if let Some(csrf_token)=resp.headers().get("X-CSRF-Token"){
|
||||
resp=self.client.patch(url)
|
||||
.header("X-CSRF-Token",csrf_token)
|
||||
.header("Cookie",self.cookie.as_str())
|
||||
.header("Content-Type",body.content_type())
|
||||
.body(body)
|
||||
.send().await.map_err(PostError::Reqwest)?;
|
||||
}else{
|
||||
Err(PostError::CSRF)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(resp)
|
||||
}
|
||||
pub async fn create(&self,config:CreateRequest,body:impl Into<reqwest::Body>+Clone)->Result<UploadResponse,CreateError>{
|
||||
let mut url=reqwest::Url::parse("https://data.roblox.com/Data/Upload.ashx?json=1&type=Model&genreTypeId=1").map_err(CreateError::ParseError)?;
|
||||
//url borrow scope
|
||||
@@ -560,4 +636,16 @@ impl Context{
|
||||
).await.map_err(PageError::Response)?
|
||||
.json::<UserInventoryPageResponse>().await.map_err(PageError::Reqwest)
|
||||
}
|
||||
/// Used to enable an asset to be loaded onto a group game.
|
||||
pub async fn set_assets_permissions(&self,config:SetAssetsPermissionsRequest<'_>)->Result<(),SetAssetsPermissionsError>{
|
||||
let url=reqwest::Url::parse("https://apis.roblox.com/asset-permissions-api/v1/assets/permissions").map_err(SetAssetsPermissionsError::Parse)?;
|
||||
|
||||
let body=config.serialize().map_err(SetAssetsPermissionsError::JSONEncode)?;
|
||||
|
||||
response_ok(
|
||||
self.patch(url,Json(body)).await.map_err(SetAssetsPermissionsError::Patch)?
|
||||
).await.map_err(SetAssetsPermissionsError::Response)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod cloud;
|
||||
pub mod cookie;
|
||||
pub mod types;
|
||||
mod body;
|
||||
mod util;
|
||||
|
||||
Reference in New Issue
Block a user