15 Commits

Author SHA1 Message Date
7ba16464c4 handle file variations correctly
All checks were successful
continuous-integration/drone/push Build is passing
2025-08-25 19:47:44 -07:00
66230d031c do not redownload 2025-08-25 19:47:44 -07:00
f6aa44ffc5 return list verbatim if no cursor 2025-08-25 19:47:44 -07:00
ae166d8509 do not error on remove 2025-08-25 19:47:44 -07:00
a4ae552169 fix cursor bug 2025-08-25 19:47:44 -07:00
23d687e072 explicit error path 2025-08-25 19:47:44 -07:00
71bbfa0128 fix stack overflow 2025-08-25 19:47:44 -07:00
89da9108c2 allow the versions to not exist 2025-08-25 19:47:44 -07:00
04d5592aaf delete cursor file if completed 2025-08-25 19:47:44 -07:00
bd3605ab87 allow the cursor to not exist 2025-08-25 19:47:44 -07:00
13cff42bbc fix error path 2025-08-25 19:47:44 -07:00
60ba5511ad plumb api key through DownloadCreationsHistory
All checks were successful
continuous-integration/drone/push Build is passing
2025-08-25 17:54:52 -07:00
cf67ad510b allow resume from files
All checks were successful
continuous-integration/drone/push Build is passing
2025-08-25 17:42:04 -07:00
e6a548a1a1 get_asset_v2 2025-08-25 17:42:04 -07:00
d2bee93fbb DownloadCreationsHistory 2025-08-25 17:42:04 -07:00
11 changed files with 552 additions and 572 deletions

View File

@@ -7,17 +7,6 @@ platform:
arch: amd64
steps:
- name: build
image: clux/muslrust:1.89.0-stable
commands:
- cargo build --release --target x86_64-unknown-linux-musl
when:
branch:
- master
event:
- push
- pull_request
- name: image
image: plugins/docker
settings:
@@ -30,15 +19,6 @@ steps:
password:
from_secret: GIT_PASS
dockerfile: Containerfile
depends_on:
- build
when:
branch:
- master
event:
- push
---
kind: signature
hmac: 52507904dfaada892c05a61422dc5e147c1438419ed841d0f1e3e3ec2b193540
...
- master

950
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
workspace = { members = ["rbx_asset", "rox_compiler"] }
[package]
name = "asset-tool"
version = "0.5.1"
version = "0.4.12"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -10,20 +10,17 @@ edition = "2021"
anyhow = "1.0.75"
clap = { version = "4.4.2", features = ["derive"] }
futures = "0.3.30"
git2 = { version = "0.20.0", optional = true }
rbx_asset = { path = "rbx_asset", features = ["gzip", "rustls-tls"], default-features = false }
rbx_binary = "2.0.0"
rbx_dom_weak = "4.0.0"
rbx_reflection_database = "2.0.1"
rbx_xml = "2.0.0"
git2 = "0.20.0"
lazy-regex = "3.1.0"
rbx_asset = { path = "rbx_asset" }
rbx_binary = "1.0.0"
rbx_dom_weak = "3.0.0"
rbx_reflection_database = "1.0.3"
rbx_xml = "1.0.0"
rox_compiler = { path = "rox_compiler" }
serde_json = "1.0.111"
tokio = { version = "1.35.1", features = ["macros", "rt-multi-thread", "fs"] }
[features]
default = []
git = ["dep:git2"]
[profile.release]
#lto = true
strip = true

View File

@@ -1,3 +1,23 @@
FROM alpine:3.22 AS runtime
COPY /target/x86_64-unknown-linux-musl/release/asset-tool /usr/local/bin/
ENTRYPOINT ["/usr/local/bin/asset-tool"]
# Using the `rust-musl-builder` as base image, instead of
# the official Rust toolchain
FROM docker.io/clux/muslrust:stable AS chef
USER root
RUN cargo install cargo-chef
WORKDIR /app
FROM chef AS planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json
FROM chef AS builder
COPY --from=planner /app/recipe.json recipe.json
# Notice that we are specifying the --target flag!
RUN cargo chef cook --release --target x86_64-unknown-linux-musl --recipe-path recipe.json
COPY . .
RUN cargo build --release --target x86_64-unknown-linux-musl --bin asset-tool
FROM docker.io/alpine:latest AS runtime
RUN addgroup -S myuser && adduser -S myuser -G myuser
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/asset-tool /usr/local/bin/
USER myuser
ENTRYPOINT ["/usr/local/bin/asset-tool"]

View File

@@ -1,6 +1,6 @@
[package]
name = "rbx_asset"
version = "0.5.0"
version = "0.4.10"
edition = "2021"
publish = ["strafesnet"]
repository = "https://git.itzana.me/StrafesNET/asset-tool"

View File

@@ -3,13 +3,14 @@ use crate::util::{serialize_u64,deserialize_u64,response_ok};
use crate::types::{ResponseError,MaybeGzippedBytes};
#[derive(Debug,serde::Deserialize,serde::Serialize)]
#[allow(nonstandard_style,dead_code)]
pub enum AssetType{
Audio,
Decal,
Model,
}
#[derive(Debug,serde::Deserialize,serde::Serialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct CreateAssetRequest{
pub assetType:AssetType,
pub creationContext:CreationContext,
@@ -55,7 +56,7 @@ impl std::fmt::Display for CreateError{
impl std::error::Error for CreateError{}
#[derive(Debug,serde::Deserialize,serde::Serialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct UpdateAssetRequest{
pub assetId:u64,
pub displayName:Option<String>,
@@ -64,41 +65,42 @@ pub struct UpdateAssetRequest{
//woo nested roblox stuff
#[derive(Clone,Debug,serde::Deserialize,serde::Serialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub enum Creator{
userId(#[serde(deserialize_with="deserialize_u64",serialize_with="serialize_u64")]u64),
groupId(#[serde(deserialize_with="deserialize_u64",serialize_with="serialize_u64")]u64),
}
#[derive(Debug,serde::Deserialize,serde::Serialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct CreationContext{
pub creator:Creator,
pub expectedPrice:Option<u64>,
}
#[derive(Debug,serde::Deserialize,serde::Serialize)]
#[allow(nonstandard_style,dead_code)]
pub enum ModerationState{
Reviewing,
Rejected,
Approved,
}
#[derive(Debug,serde::Deserialize,serde::Serialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct ModerationResult{
pub moderationState:ModerationState,
}
#[derive(Debug,serde::Deserialize,serde::Serialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct Preview{
pub asset:String,
pub altText:String,
}
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct UpdatePlaceRequest{
pub universeId:u64,
pub placeId:u64,
}
#[derive(Debug,serde::Deserialize,serde::Serialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct UpdatePlaceResponse{
pub versionNumber:u64,
}
@@ -144,7 +146,7 @@ pub struct GetAssetLatestRequest{
}
*/
#[derive(Debug,serde::Deserialize,serde::Serialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct AssetResponse{
//u64 wrapped in quotes wohoo!!
#[serde(deserialize_with="deserialize_u64")]
@@ -164,6 +166,7 @@ pub struct AssetResponse{
#[serde(default)]
pub previews:Vec<Preview>,
}
#[allow(nonstandard_style,dead_code)]
pub struct GetAssetVersionRequest{
pub asset_id:u64,
pub version:u64,
@@ -194,13 +197,13 @@ impl AssetLocation{
}
#[derive(Debug,serde::Deserialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct AssetMetadata{
pub metadataType:u32,
pub value:String,
}
#[derive(Debug,serde::Deserialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct AssetLocationInfo{
pub location:Option<AssetLocation>,
pub requestId:String,
@@ -216,7 +219,7 @@ pub struct AssetVersionsRequest{
pub cursor:Option<String>,
}
#[derive(Debug,serde::Deserialize,serde::Serialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct AssetVersion{
pub Id:u64,
pub assetId:u64,
@@ -228,7 +231,7 @@ pub struct AssetVersion{
pub isPublished:bool,
}
#[derive(Debug,serde::Deserialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct AssetVersionsResponse{
pub previousPageCursor:Option<String>,
pub nextPageCursor:Option<String>,
@@ -252,12 +255,13 @@ pub struct InventoryPageRequest{
pub cursor:Option<String>,
}
#[derive(Debug,serde::Deserialize,serde::Serialize)]
#[allow(nonstandard_style,dead_code)]
pub struct InventoryItem{
pub id:u64,
pub name:String,
}
#[derive(Debug,serde::Deserialize,serde::Serialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct InventoryPageResponse{
pub totalResults:u64,//up to 50
pub filteredKeyword:Option<String>,//""
@@ -295,7 +299,7 @@ impl std::fmt::Display for OperationError{
}
impl std::error::Error for OperationError{}
#[derive(Debug,serde::Deserialize,serde::Serialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
struct RobloxOperation{
pub path:Option<String>,
pub metadata:Option<String>,

View File

@@ -15,7 +15,7 @@ impl std::fmt::Display for PostError{
impl std::error::Error for PostError{}
#[derive(Debug,serde::Deserialize,serde::Serialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct CreateRequest{
pub name:String,
pub description:String,
@@ -43,7 +43,7 @@ impl std::fmt::Display for CreateError{
}
impl std::error::Error for CreateError{}
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct UploadRequest{
pub assetid:u64,
pub name:Option<String>,
@@ -73,15 +73,17 @@ impl std::fmt::Display for UploadError{
}
impl std::error::Error for UploadError{}
#[derive(Debug,serde::Deserialize,serde::Serialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct UploadResponse{
pub AssetId:u64,
pub AssetVersion:u64,
}
#[allow(nonstandard_style,dead_code)]
pub struct GetAssetDetailsRequest{
pub asset_id:u64,
}
#[allow(nonstandard_style,dead_code)]
pub struct GetAssetRequest{
pub asset_id:u64,
pub version:Option<u64>,
@@ -116,13 +118,13 @@ impl std::fmt::Display for GetAssetV2Error{
impl std::error::Error for GetAssetV2Error{}
#[derive(serde::Deserialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct GetAssetV2AssetMetadata{
pub metadataType:u32,
pub value:String,
}
#[derive(serde::Deserialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct GetAssetV2Location{
pub assetFormat:String,// "source"
location:String,// this value is private so users cannot mutate it
@@ -135,7 +137,7 @@ impl GetAssetV2Location{
}
}
#[derive(serde::Deserialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct GetAssetV2Info{
pub locations:Vec<GetAssetV2Location>,
pub requestId:String,
@@ -160,7 +162,7 @@ pub enum CreatorType{
#[derive(Debug)]
#[derive(serde::Deserialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct Creator{
pub Id:u64,
pub Name:String,
@@ -171,7 +173,7 @@ pub struct Creator{
#[derive(Debug)]
#[derive(serde::Deserialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct AssetDetails{
pub TargetId:u64,
pub ProductType:Option<String>,
@@ -207,7 +209,7 @@ pub struct AssetVersionsPageRequest{
pub cursor:Option<String>,
}
#[derive(serde::Deserialize,serde::Serialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct AssetVersion{
pub Id:u64,
pub assetId:u64,
@@ -219,7 +221,7 @@ pub struct AssetVersion{
pub isPublished:bool,
}
#[derive(serde::Deserialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct AssetVersionsPageResponse{
pub previousPageCursor:Option<String>,
pub nextPageCursor:Option<String>,
@@ -255,12 +257,13 @@ pub struct CreationsPageRequest{
pub cursor:Option<String>,
}
#[derive(serde::Deserialize,serde::Serialize)]
#[allow(nonstandard_style,dead_code)]
pub struct CreationsItem{
pub id:u64,
pub name:String,
}
#[derive(serde::Deserialize,serde::Serialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct CreationsPageResponse{
pub totalResults:u64,//up to 50
pub filteredKeyword:Option<String>,//""
@@ -279,14 +282,14 @@ pub struct UserInventoryPageRequest{
}
#[derive(serde::Deserialize,serde::Serialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct UserInventoryItemOwner{
pub userId:u64,
pub username:String,
pub buildersClubMembershipType:String,
}
#[derive(serde::Deserialize,serde::Serialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct UserInventoryItem{
pub userAssetId:u64,
pub assetId:u64,
@@ -298,7 +301,7 @@ pub struct UserInventoryItem{
pub updated:chrono::DateTime<chrono::Utc>,
}
#[derive(serde::Deserialize,serde::Serialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style,dead_code)]
pub struct UserInventoryPageResponse{
pub previousPageCursor:Option<String>,
pub nextPageCursor:Option<String>,
@@ -321,13 +324,13 @@ impl std::fmt::Display for SetAssetsPermissionsError{
impl std::error::Error for SetAssetsPermissionsError{}
#[derive(serde::Serialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style)]
struct AssetPermissions{
assetId:u64,
grantToDependencies:bool,//true
}
#[derive(serde::Serialize)]
#[expect(nonstandard_style)]
#[allow(nonstandard_style)]
struct SetAssetsPermissions<'a>{
subjectType:&'a str,// "Universe"
subjectId:&'a str,// "4422715291"

View File

@@ -10,8 +10,8 @@ authors = ["Rhys Lloyd <krakow20@gmail.com>"]
[dependencies]
futures = "0.3.30"
regex = { version = "1.11.3", default-features = false, features = ["unicode-perl"] }
lazy-regex = "3.1.0"
rayon = "1.8.0"
rbx_dom_weak = "4.0.0"
rbx_xml = "2.0.0"
rbx_dom_weak = "3.0.0"
rbx_xml = "1.0.0"
tokio = { version = "1.35.1", features = ["fs"] }

View File

@@ -28,16 +28,6 @@ impl std::fmt::Display for PropertiesOverride{
}
}
#[macro_export]
macro_rules! lazy_regex{
($r:literal)=>{{
use regex::Regex;
use std::sync::LazyLock;
static RE:LazyLock<Regex>=LazyLock::new(||Regex::new($r).unwrap());
&RE
}};
}
pub(crate) fn sanitize(s:&str)->std::borrow::Cow<'_,str>{
lazy_regex!(r"[^A-Za-z0-9.-]").replace_all(s,"_")
lazy_regex::regex!(r"[^A-Za-z0-9.-]").replace_all(s,"_")
}

View File

@@ -2,7 +2,6 @@ use std::path::{Path,PathBuf};
use futures::{StreamExt, TryStreamExt};
use tokio::io::AsyncReadExt;
use crate::lazy_regex;
use crate::common::{sanitize,Style,PropertiesOverride};
//holy smokes what am I doing lmao
@@ -203,7 +202,7 @@ impl ScriptWithOverrides{
let mut count=0;
for line in source.lines(){
//only string type properties are supported atm
if let Some(captures)=lazy_regex!(r#"^\-\-\s*Properties\.([A-Za-z]\w*)\s*\=\s*"(\w+)"$"#)
if let Some(captures)=lazy_regex::regex!(r#"^\-\-\s*Properties\.([A-Za-z]\w*)\s*\=\s*"(\w+)"$"#)
.captures(line){
count+=line.len();
match &captures[1]{
@@ -340,7 +339,7 @@ impl CompileNode{
//reject goobers
let is_goober=matches!(style,Some(Style::Rojo));
let (ext_len,file_discernment)={
if let Some(captures)=lazy_regex!(r"^.*(\.module\.lua|\.client\.lua|\.server\.lua)$")
if let Some(captures)=lazy_regex::regex!(r"^.*(\.module\.lua|\.client\.lua|\.server\.lua)$")
.captures(file_name.as_str()){
let ext=&captures[1];
(ext.len(),match ext{
@@ -354,7 +353,7 @@ impl CompileNode{
".server.lua"=>FileDiscernment::Script(ScriptHint::Script),
_=>panic!("Regex failed"),
})
}else if let Some(captures)=lazy_regex!(r"^.*(\.rbxmx|\.lua)$")
}else if let Some(captures)=lazy_regex::regex!(r"^.*(\.rbxmx|\.lua)$")
.captures(file_name.as_str()){
let ext=&captures[1];
(ext.len(),match ext{

View File

@@ -8,7 +8,6 @@ use rbx_asset::cookie::{Cookie,Context as CookieContext,AssetVersion,CreationsIt
type AssetID=u64;
type AssetIDFileMap=Vec<(AssetID,PathBuf)>;
#[cfg(feature="git")]
const CONCURRENT_DECODE:usize=8;
const CONCURRENT_REQUESTS:usize=32;
const CONCURRENT_FS:usize=64;
@@ -44,9 +43,7 @@ enum Commands{
CompileUploadAsset(CompileUploadAssetSubcommand),
CompileUploadPlace(CompileUploadPlaceSubcommand),
Decompile(DecompileSubcommand),
#[cfg(feature="git")]
DecompileHistoryIntoGit(DecompileHistoryIntoGitSubcommand),
#[cfg(feature="git")]
DownloadAndDecompileHistoryIntoGit(DownloadAndDecompileHistoryIntoGitSubcommand),
RunLuau(RunLuauSubcommand),
}
@@ -742,7 +739,6 @@ async fn main()->AResult<()>{
write_models:subcommand.write_models.unwrap_or(false),
write_scripts:subcommand.write_scripts.unwrap_or(true),
}).await,
#[cfg(feature="git")]
Commands::DecompileHistoryIntoGit(subcommand)=>decompile_history_into_git(DecompileHistoryConfig{
git_committer_name:subcommand.git_committer_name,
git_committer_email:subcommand.git_committer_email,
@@ -753,7 +749,6 @@ async fn main()->AResult<()>{
write_models:subcommand.write_models.unwrap_or(false),
write_scripts:subcommand.write_scripts.unwrap_or(true),
}).await,
#[cfg(feature="git")]
Commands::DownloadAndDecompileHistoryIntoGit(subcommand)=>download_and_decompile_history_into_git(DownloadAndDecompileHistoryConfig{
git_committer_name:subcommand.git_committer_name,
git_committer_email:subcommand.git_committer_email,
@@ -1717,7 +1712,6 @@ async fn download_decompile(config:DownloadDecompileConfig)->AResult<()>{
Ok(())
}
#[cfg(feature="git")]
struct WriteCommitConfig{
git_committer_name:String,
git_committer_email:String,
@@ -1728,7 +1722,6 @@ struct WriteCommitConfig{
write_scripts:bool,
}
#[cfg(feature="git")]
async fn write_commit(config:WriteCommitConfig,b:Result<AResult<(AssetVersion,rox_compiler::DecompiledContext)>,tokio::task::JoinError>,repo:&git2::Repository)->AResult<()>{
let (asset_version,context)=b??;
println!("writing files for version {}",asset_version.assetVersionNumber);
@@ -1810,7 +1803,6 @@ async fn write_commit(config:WriteCommitConfig,b:Result<AResult<(AssetVersion,ro
Ok(())
}
#[cfg(feature="git")]
struct DecompileHistoryConfig{
git_committer_name:String,
git_committer_email:String,
@@ -1822,7 +1814,6 @@ struct DecompileHistoryConfig{
write_scripts:bool,
}
#[cfg(feature="git")]
async fn decompile_history_into_git(config:DecompileHistoryConfig)->AResult<()>{
//use prexisting versions list
let mut versions_path=config.input_folder.clone();
@@ -1861,7 +1852,6 @@ async fn decompile_history_into_git(config:DecompileHistoryConfig)->AResult<()>{
Ok(())
}
#[cfg(feature="git")]
struct DownloadAndDecompileHistoryConfig{
cookie:Cookie,
asset_id:AssetID,
@@ -1874,7 +1864,6 @@ struct DownloadAndDecompileHistoryConfig{
write_scripts:bool,
}
#[cfg(feature="git")]
async fn download_and_decompile_history_into_git(config:DownloadAndDecompileHistoryConfig)->AResult<()>{
let context=CookieContext::new(config.cookie);