Add 'upload' command to publish places to Roblox for you

This commit is contained in:
Lucien Greathouse
2019-01-07 14:01:53 -08:00
parent 1992ce1cfb
commit 99ea374fc5
3 changed files with 133 additions and 1 deletions

View File

@@ -45,6 +45,13 @@ fn main() {
(@arg PROJECT: "Path to the project to serve. Defaults to the current directory.")
(@arg output: --output +takes_value +required "Where to output the result.")
)
(@subcommand upload =>
(about: "Generates a place file out of the project and uploads it to Roblox.")
(@arg PROJECT: "Path to the project to upload. Defaults to the current directory.")
(@arg cookie: --cookie +takes_value +required "Security cookie to authenticate with.")
(@arg place_id: --place_id +takes_value +required "Place ID to upload to.")
)
);
// `get_matches` consumes self for some reason.
@@ -109,6 +116,40 @@ fn main() {
},
}
},
("upload", Some(sub_matches)) => {
let fuzzy_project_path = match sub_matches.value_of("PROJECT") {
Some(v) => make_path_absolute(Path::new(v)),
None => std::env::current_dir().unwrap(),
};
let security_cookie = sub_matches.value_of("cookie").unwrap();
let place_id: u64 = {
let arg = sub_matches.value_of("place_id").unwrap();
match arg.parse() {
Ok(v) => v,
Err(_) => {
error!("Invalid place ID {}", arg);
process::exit(1);
},
}
};
let options = commands::UploadOptions {
fuzzy_project_path,
security_cookie: security_cookie.to_string(),
place_id,
};
match commands::upload(&options) {
Ok(_) => {},
Err(e) => {
error!("{}", e);
process::exit(1);
},
}
},
_ => {
app.print_help().expect("Could not print help text to stdout!");
},

View File

@@ -1,7 +1,9 @@
mod serve;
mod init;
mod build;
mod upload;
pub use self::serve::*;
pub use self::init::*;
pub use self::build::*;
pub use self::build::*;
pub use self::upload::*;

View File

@@ -0,0 +1,89 @@
use std::{
path::PathBuf,
io,
};
use failure::Fail;
use reqwest::header::{USER_AGENT, CONTENT_TYPE, COOKIE};
use crate::{
rbx_session::construct_oneoff_tree,
project::{Project, ProjectLoadFuzzyError},
imfs::Imfs,
};
#[derive(Debug, Fail)]
pub enum UploadError {
#[fail(display = "Roblox API Error: {}", _0)]
RobloxApiError(String),
#[fail(display = "Project load error: {}", _0)]
ProjectLoadError(#[fail(cause)] ProjectLoadFuzzyError),
#[fail(display = "IO error: {}", _0)]
IoError(#[fail(cause)] io::Error),
#[fail(display = "HTTP error: {}", _0)]
HttpError(#[fail(cause)] reqwest::Error),
}
impl From<ProjectLoadFuzzyError> for UploadError {
fn from(error: ProjectLoadFuzzyError) -> UploadError {
UploadError::ProjectLoadError(error)
}
}
impl From<io::Error> for UploadError {
fn from(error: io::Error) -> UploadError {
UploadError::IoError(error)
}
}
impl From<reqwest::Error> for UploadError {
fn from(error: reqwest::Error) -> UploadError {
UploadError::HttpError(error)
}
}
#[derive(Debug)]
pub struct UploadOptions {
pub fuzzy_project_path: PathBuf,
pub security_cookie: String,
pub place_id: u64,
}
pub fn upload(options: &UploadOptions) -> Result<(), UploadError> {
info!("Looking for project at {}", options.fuzzy_project_path.display());
let project = Project::load_fuzzy(&options.fuzzy_project_path)?;
info!("Found project at {}", project.file_location.display());
info!("Using project {:#?}", project);
let mut imfs = Imfs::new();
imfs.add_roots_from_project(&project)?;
let tree = construct_oneoff_tree(&project, &imfs);
let root_id = tree.get_root_id();
let top_level_ids = tree.get_instance(root_id).unwrap().get_children_ids();
let mut contents = Vec::new();
rbx_xml::encode(&tree, top_level_ids, &mut contents);
let url = format!("https://data.roblox.com/Data/Upload.ashx?json=1&type=Place&assetid={}", options.place_id);
let client = reqwest::Client::new();
let mut response = client.post(&url)
.header(COOKIE, format!(".ROBLOSECURITY={}", &options.security_cookie))
.header(USER_AGENT, "Roblox/WinInet")
.header("Requester", "Client")
.header(CONTENT_TYPE, "application/xml")
.body(contents)
.send()?;
if !response.status().is_success() {
return Err(UploadError::RobloxApiError(response.text()?));
}
Ok(())
}