Merge pull request 'v0.2.0' (#2) from v0.2.0 into master
Reviewed-on: https://git.etztech.xyz/ZeroHD/Albatross/pulls/2
This commit is contained in:
		
						commit
						cc85d287ef
					
				
							
								
								
									
										42
									
								
								.drone.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								.drone.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | |||||||
|  | --- | ||||||
|  | kind: pipeline | ||||||
|  | name: compliance | ||||||
|  | type: docker | ||||||
|  | 
 | ||||||
|  | trigger: | ||||||
|  |   event: | ||||||
|  |     - pull_request | ||||||
|  | 
 | ||||||
|  | steps: | ||||||
|  |   - name: build | ||||||
|  |     pull: always | ||||||
|  |     image: rust:1.46.0 | ||||||
|  |     commands: | ||||||
|  |       - cargo build --verbose | ||||||
|  | 
 | ||||||
|  | --- | ||||||
|  | kind: pipeline | ||||||
|  | name: release | ||||||
|  | type: docker | ||||||
|  | 
 | ||||||
|  | trigger: | ||||||
|  |   branch: | ||||||
|  |     - master | ||||||
|  |   event: | ||||||
|  |     - push | ||||||
|  | 
 | ||||||
|  | steps: | ||||||
|  |   - name: build | ||||||
|  |     pull: always | ||||||
|  |     image: rust:1.46.0 | ||||||
|  |     commands: | ||||||
|  |       - cargo build --verbose --release | ||||||
|  |   - name: gitea-release | ||||||
|  |     pull: always | ||||||
|  |     image: jolheiser/drone-gitea-main:latest | ||||||
|  |     settings: | ||||||
|  |       token: | ||||||
|  |         from_secret: gitea_token | ||||||
|  |       base: https://git.etztech.xyz | ||||||
|  |       files: | ||||||
|  |         - "target/release/albatross" | ||||||
							
								
								
									
										241
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										241
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -1,5 +1,20 @@ | |||||||
| # This file is automatically @generated by Cargo. | # This file is automatically @generated by Cargo. | ||||||
| # It is not intended for manual editing. | # It is not intended for manual editing. | ||||||
|  | [[package]] | ||||||
|  | name = "addr2line" | ||||||
|  | version = "0.13.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072" | ||||||
|  | dependencies = [ | ||||||
|  |  "gimli", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "adler" | ||||||
|  | version = "0.2.3" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "adler32" | name = "adler32" | ||||||
| version = "1.0.4" | version = "1.0.4" | ||||||
| @ -17,17 +32,17 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "albatross" | name = "albatross" | ||||||
| version = "0.1.0" | version = "0.2.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "chrono", |  "chrono", | ||||||
|  "clap", |  | ||||||
|  "config", |  "config", | ||||||
|  |  "discord-hooks-rs", | ||||||
|  "flate2", |  "flate2", | ||||||
|  "log", |  "log", | ||||||
|  "regex", |  "regex", | ||||||
|  "reqwest", |  "reqwest", | ||||||
|  "serde 1.0.111", |  "serde 1.0.117", | ||||||
|  "serde_derive", |  "structopt", | ||||||
|  "tar", |  "tar", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| @ -57,6 +72,20 @@ version = "1.0.0" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" | checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "backtrace" | ||||||
|  | version = "0.3.53" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "707b586e0e2f247cbde68cdd2c3ce69ea7b7be43e1c5b426e37c9319c4b9838e" | ||||||
|  | dependencies = [ | ||||||
|  |  "addr2line", | ||||||
|  |  "cfg-if 1.0.0", | ||||||
|  |  "libc", | ||||||
|  |  "miniz_oxide 0.4.3", | ||||||
|  |  "object", | ||||||
|  |  "rustc-demangle", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "base64" | name = "base64" | ||||||
| version = "0.12.1" | version = "0.12.1" | ||||||
| @ -93,6 +122,12 @@ version = "0.1.10" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "cfg-if" | ||||||
|  | version = "1.0.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "chrono" | name = "chrono" | ||||||
| version = "0.4.11" | version = "0.4.11" | ||||||
| @ -128,7 +163,7 @@ dependencies = [ | |||||||
|  "lazy_static 1.4.0", |  "lazy_static 1.4.0", | ||||||
|  "nom", |  "nom", | ||||||
|  "rust-ini", |  "rust-ini", | ||||||
|  "serde 1.0.111", |  "serde 1.0.117", | ||||||
|  "serde-hjson", |  "serde-hjson", | ||||||
|  "serde_json", |  "serde_json", | ||||||
|  "toml", |  "toml", | ||||||
| @ -157,7 +192,17 @@ version = "1.2.0" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" | checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "cfg-if", |  "cfg-if 0.1.10", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "discord-hooks-rs" | ||||||
|  | version = "0.1.0" | ||||||
|  | source = "git+https://github.com/joeyahines/discord-hooks-rs#70307a7a5d00b8c48c0dc10402c7d8325836539a" | ||||||
|  | dependencies = [ | ||||||
|  |  "failure", | ||||||
|  |  "serde 1.0.117", | ||||||
|  |  "serde_json", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| @ -172,7 +217,29 @@ version = "0.8.23" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "e8ac63f94732332f44fe654443c46f6375d1939684c17b0afb6cb56b0456e171" | checksum = "e8ac63f94732332f44fe654443c46f6375d1939684c17b0afb6cb56b0456e171" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "cfg-if", |  "cfg-if 0.1.10", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "failure" | ||||||
|  | version = "0.1.8" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" | ||||||
|  | dependencies = [ | ||||||
|  |  "backtrace", | ||||||
|  |  "failure_derive", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "failure_derive" | ||||||
|  | version = "0.1.8" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" | ||||||
|  | dependencies = [ | ||||||
|  |  "proc-macro2", | ||||||
|  |  "quote", | ||||||
|  |  "syn", | ||||||
|  |  "synstructure", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| @ -181,7 +248,7 @@ version = "0.2.10" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "affc17579b132fc2461adf7c575cc6e8b134ebca52c51f5411388965227dc695" | checksum = "affc17579b132fc2461adf7c575cc6e8b134ebca52c51f5411388965227dc695" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "cfg-if", |  "cfg-if 0.1.10", | ||||||
|  "libc", |  "libc", | ||||||
|  "redox_syscall", |  "redox_syscall", | ||||||
|  "winapi 0.3.8", |  "winapi 0.3.8", | ||||||
| @ -193,10 +260,10 @@ version = "1.0.14" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42" | checksum = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "cfg-if", |  "cfg-if 0.1.10", | ||||||
|  "crc32fast", |  "crc32fast", | ||||||
|  "libc", |  "libc", | ||||||
|  "miniz_oxide", |  "miniz_oxide 0.3.6", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| @ -293,11 +360,17 @@ version = "0.1.14" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" | checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "cfg-if", |  "cfg-if 0.1.10", | ||||||
|  "libc", |  "libc", | ||||||
|  "wasi", |  "wasi", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "gimli" | ||||||
|  | version = "0.22.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "h2" | name = "h2" | ||||||
| version = "0.2.5" | version = "0.2.5" | ||||||
| @ -317,6 +390,15 @@ dependencies = [ | |||||||
|  "tokio-util", |  "tokio-util", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "heck" | ||||||
|  | version = "0.3.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" | ||||||
|  | dependencies = [ | ||||||
|  |  "unicode-segmentation", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "hermit-abi" | name = "hermit-abi" | ||||||
| version = "0.1.13" | version = "0.1.13" | ||||||
| @ -484,7 +566,7 @@ version = "0.4.8" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" | checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "cfg-if", |  "cfg-if 0.1.10", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| @ -524,13 +606,23 @@ dependencies = [ | |||||||
|  "adler32", |  "adler32", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "miniz_oxide" | ||||||
|  | version = "0.4.3" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" | ||||||
|  | dependencies = [ | ||||||
|  |  "adler", | ||||||
|  |  "autocfg", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "mio" | name = "mio" | ||||||
| version = "0.6.22" | version = "0.6.22" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" | checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "cfg-if", |  "cfg-if 0.1.10", | ||||||
|  "fuchsia-zircon", |  "fuchsia-zircon", | ||||||
|  "fuchsia-zircon-sys", |  "fuchsia-zircon-sys", | ||||||
|  "iovec", |  "iovec", | ||||||
| @ -579,7 +671,7 @@ version = "0.2.34" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" | checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "cfg-if", |  "cfg-if 0.1.10", | ||||||
|  "libc", |  "libc", | ||||||
|  "winapi 0.3.8", |  "winapi 0.3.8", | ||||||
| ] | ] | ||||||
| @ -632,6 +724,12 @@ dependencies = [ | |||||||
|  "libc", |  "libc", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "object" | ||||||
|  | version = "0.21.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "37fd5004feb2ce328a52b0b3d01dbf4ffff72583493900ed15f22d4111c51693" | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "once_cell" | name = "once_cell" | ||||||
| version = "1.4.0" | version = "1.4.0" | ||||||
| @ -645,7 +743,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||||||
| checksum = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd" | checksum = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "bitflags", |  "bitflags", | ||||||
|  "cfg-if", |  "cfg-if 0.1.10", | ||||||
|  "foreign-types", |  "foreign-types", | ||||||
|  "lazy_static 1.4.0", |  "lazy_static 1.4.0", | ||||||
|  "libc", |  "libc", | ||||||
| @ -722,10 +820,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||||||
| checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" | checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "proc-macro2" | name = "proc-macro-error" | ||||||
| version = "1.0.18" | version = "1.0.4" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" | ||||||
|  | dependencies = [ | ||||||
|  |  "proc-macro-error-attr", | ||||||
|  |  "proc-macro2", | ||||||
|  |  "quote", | ||||||
|  |  "syn", | ||||||
|  |  "version_check 0.9.2", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "proc-macro-error-attr" | ||||||
|  | version = "1.0.4" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" | ||||||
|  | dependencies = [ | ||||||
|  |  "proc-macro2", | ||||||
|  |  "quote", | ||||||
|  |  "version_check 0.9.2", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "proc-macro2" | ||||||
|  | version = "1.0.24" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "unicode-xid", |  "unicode-xid", | ||||||
| ] | ] | ||||||
| @ -836,7 +958,7 @@ dependencies = [ | |||||||
|  "native-tls", |  "native-tls", | ||||||
|  "percent-encoding", |  "percent-encoding", | ||||||
|  "pin-project-lite", |  "pin-project-lite", | ||||||
|  "serde 1.0.111", |  "serde 1.0.117", | ||||||
|  "serde_json", |  "serde_json", | ||||||
|  "serde_urlencoded", |  "serde_urlencoded", | ||||||
|  "tokio", |  "tokio", | ||||||
| @ -854,6 +976,12 @@ version = "0.13.0" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" | checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "rustc-demangle" | ||||||
|  | version = "0.1.18" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ryu" | name = "ryu" | ||||||
| version = "1.0.5" | version = "1.0.5" | ||||||
| @ -901,9 +1029,12 @@ checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "serde" | name = "serde" | ||||||
| version = "1.0.111" | version = "1.0.117" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d" | checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" | ||||||
|  | dependencies = [ | ||||||
|  |  "serde_derive", | ||||||
|  | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "serde-hjson" | name = "serde-hjson" | ||||||
| @ -920,9 +1051,9 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "serde_derive" | name = "serde_derive" | ||||||
| version = "1.0.111" | version = "1.0.117" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250" | checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2", |  "proc-macro2", | ||||||
|  "quote", |  "quote", | ||||||
| @ -937,7 +1068,7 @@ checksum = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" | |||||||
| dependencies = [ | dependencies = [ | ||||||
|  "itoa", |  "itoa", | ||||||
|  "ryu", |  "ryu", | ||||||
|  "serde 1.0.111", |  "serde 1.0.117", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| @ -957,7 +1088,7 @@ checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" | |||||||
| dependencies = [ | dependencies = [ | ||||||
|  "dtoa", |  "dtoa", | ||||||
|  "itoa", |  "itoa", | ||||||
|  "serde 1.0.111", |  "serde 1.0.117", | ||||||
|  "url", |  "url", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| @ -979,7 +1110,7 @@ version = "0.3.12" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" | checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "cfg-if", |  "cfg-if 0.1.10", | ||||||
|  "libc", |  "libc", | ||||||
|  "redox_syscall", |  "redox_syscall", | ||||||
|  "winapi 0.3.8", |  "winapi 0.3.8", | ||||||
| @ -992,16 +1123,52 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||||||
| checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "syn" | name = "structopt" | ||||||
| version = "1.0.30" | version = "0.3.20" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2" | checksum = "126d630294ec449fae0b16f964e35bf3c74f940da9dca17ee9b905f7b3112eb8" | ||||||
|  | dependencies = [ | ||||||
|  |  "clap", | ||||||
|  |  "lazy_static 1.4.0", | ||||||
|  |  "structopt-derive", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "structopt-derive" | ||||||
|  | version = "0.4.13" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "65e51c492f9e23a220534971ff5afc14037289de430e3c83f9daf6a1b6ae91e8" | ||||||
|  | dependencies = [ | ||||||
|  |  "heck", | ||||||
|  |  "proc-macro-error", | ||||||
|  |  "proc-macro2", | ||||||
|  |  "quote", | ||||||
|  |  "syn", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "syn" | ||||||
|  | version = "1.0.46" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "5ad5de3220ea04da322618ded2c42233d02baca219d6f160a3e9c87cda16c942" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2", |  "proc-macro2", | ||||||
|  "quote", |  "quote", | ||||||
|  "unicode-xid", |  "unicode-xid", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "synstructure" | ||||||
|  | version = "0.12.4" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" | ||||||
|  | dependencies = [ | ||||||
|  |  "proc-macro2", | ||||||
|  |  "quote", | ||||||
|  |  "syn", | ||||||
|  |  "unicode-xid", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "tar" | name = "tar" | ||||||
| version = "0.4.28" | version = "0.4.28" | ||||||
| @ -1020,7 +1187,7 @@ version = "3.1.0" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" | checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "cfg-if", |  "cfg-if 0.1.10", | ||||||
|  "libc", |  "libc", | ||||||
|  "rand", |  "rand", | ||||||
|  "redox_syscall", |  "redox_syscall", | ||||||
| @ -1104,7 +1271,7 @@ version = "0.4.10" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" | checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "serde 1.0.111", |  "serde 1.0.117", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| @ -1146,6 +1313,12 @@ dependencies = [ | |||||||
|  "smallvec", |  "smallvec", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "unicode-segmentation" | ||||||
|  | version = "1.6.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "unicode-width" | name = "unicode-width" | ||||||
| version = "0.1.7" | version = "0.1.7" | ||||||
| @ -1215,8 +1388,8 @@ version = "0.2.63" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "4c2dc4aa152834bc334f506c1a06b866416a8b6697d5c9f75b9a689c8486def0" | checksum = "4c2dc4aa152834bc334f506c1a06b866416a8b6697d5c9f75b9a689c8486def0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "cfg-if", |  "cfg-if 0.1.10", | ||||||
|  "serde 1.0.111", |  "serde 1.0.117", | ||||||
|  "serde_json", |  "serde_json", | ||||||
|  "wasm-bindgen-macro", |  "wasm-bindgen-macro", | ||||||
| ] | ] | ||||||
| @ -1242,7 +1415,7 @@ version = "0.4.13" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "64487204d863f109eb77e8462189d111f27cb5712cc9fdb3461297a76963a2f6" | checksum = "64487204d863f109eb77e8462189d111f27cb5712cc9fdb3461297a76963a2f6" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "cfg-if", |  "cfg-if 0.1.10", | ||||||
|  "js-sys", |  "js-sys", | ||||||
|  "wasm-bindgen", |  "wasm-bindgen", | ||||||
|  "web-sys", |  "web-sys", | ||||||
|  | |||||||
| @ -1,15 +1,14 @@ | |||||||
| [package] | [package] | ||||||
| name = "albatross" | name = "albatross" | ||||||
| version = "0.1.0" | version = "0.2.0" | ||||||
| authors = ["Joey Hines <joey@ahines.net>"] | authors = ["Joey Hines <joey@ahines.net>"] | ||||||
| edition = "2018" | edition = "2018" | ||||||
| 
 | 
 | ||||||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
| clap = "2.33.0" | structopt = "0.3.20" | ||||||
| serde = "1.0.106" | serde = { version="1.0.116", features=["derive"] } | ||||||
| serde_derive = "1.0.104" |  | ||||||
| config = "0.9" | config = "0.9" | ||||||
| log = "0.4.8" | log = "0.4.8" | ||||||
| chrono = "0.4" | chrono = "0.4" | ||||||
| @ -17,3 +16,4 @@ regex = "1.3.9" | |||||||
| flate2 = "1.0.14" | flate2 = "1.0.14" | ||||||
| tar = "0.4.28" | tar = "0.4.28" | ||||||
| reqwest = { version = "0.10", features = ["blocking", "json"] } | reqwest = { version = "0.10", features = ["blocking", "json"] } | ||||||
|  | discord-hooks-rs = { git = "https://github.com/joeyahines/discord-hooks-rs" } | ||||||
|  | |||||||
							
								
								
									
										24
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								README.md
									
									
									
									
									
								
							| @ -4,6 +4,30 @@ Back up what you care about in your Minecraft worlds. | |||||||
| Albatross backs up player files and region files within a certain configurable radius. It can also send Discord  | Albatross backs up player files and region files within a certain configurable radius. It can also send Discord  | ||||||
| webhooks. Backups are compressed and stored as `tar.gz` archives.  | webhooks. Backups are compressed and stored as `tar.gz` archives.  | ||||||
| 
 | 
 | ||||||
|  | ## Help | ||||||
|  | ``` | ||||||
|  | albatross 0.2.0 | ||||||
|  | Backup your Minecraft Server! | ||||||
|  | 
 | ||||||
|  | USAGE: | ||||||
|  |     albatross --config-path <config-path> <SUBCOMMAND> | ||||||
|  | 
 | ||||||
|  | FLAGS: | ||||||
|  |     -h, --help       Prints help information | ||||||
|  |     -V, --version    Prints version information | ||||||
|  | 
 | ||||||
|  | OPTIONS: | ||||||
|  |     -c, --config-path <config-path>    Path to the Albatross config [env: ALBATROSS_CONFIG=] | ||||||
|  | 
 | ||||||
|  | SUBCOMMANDS: | ||||||
|  |     backup    Backup a server | ||||||
|  |     export    Export a backup as a single player world | ||||||
|  |     help      Prints this message or the help of the given subcommand(s) | ||||||
|  | 
 | ||||||
|  | Process finished with exit code 1 | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
| ## Config | ## Config | ||||||
| ```toml | ```toml | ||||||
| [backup] | [backup] | ||||||
|  | |||||||
							
								
								
									
										380
									
								
								src/backup.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										380
									
								
								src/backup.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,380 @@ | |||||||
|  | use crate::backup; | ||||||
|  | use crate::config::{AlbatrossConfig, WorldConfig, WorldType}; | ||||||
|  | use crate::discord::send_webhook; | ||||||
|  | use crate::region::Region; | ||||||
|  | use chrono::{NaiveDateTime, Utc}; | ||||||
|  | use flate2::read::GzDecoder; | ||||||
|  | use flate2::write::GzEncoder; | ||||||
|  | use flate2::Compression; | ||||||
|  | use std::convert::TryFrom; | ||||||
|  | use std::fs::{ | ||||||
|  |     copy, create_dir, create_dir_all, remove_dir_all, remove_file, rename, DirEntry, File, | ||||||
|  | }; | ||||||
|  | use std::path::PathBuf; | ||||||
|  | use std::time::Instant; | ||||||
|  | use tar::Archive; | ||||||
|  | 
 | ||||||
|  | /// Backup a file
 | ||||||
|  | ///
 | ||||||
|  | /// # Param
 | ||||||
|  | /// * `file_name` - file name
 | ||||||
|  | /// * `world_path` - path to the world folder
 | ||||||
|  | /// * `backup_path` - path to the backup folder
 | ||||||
|  | pub fn backup_file( | ||||||
|  |     file_name: &str, | ||||||
|  |     mut world_path: PathBuf, | ||||||
|  |     mut backup_path: PathBuf, | ||||||
|  | ) -> Result<u64, std::io::Error> { | ||||||
|  |     world_path.push(file_name); | ||||||
|  |     backup_path.push(file_name); | ||||||
|  | 
 | ||||||
|  |     copy(world_path, backup_path) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Backup a directory
 | ||||||
|  | ///
 | ||||||
|  | /// # Param
 | ||||||
|  | /// * `dir_name` - directory name
 | ||||||
|  | /// * `world_path` - path to the world folder
 | ||||||
|  | /// * `backup_path` - path to the backup folder
 | ||||||
|  | pub fn backup_dir( | ||||||
|  |     dir_name: &str, | ||||||
|  |     world_path: &PathBuf, | ||||||
|  |     backup_path: &PathBuf, | ||||||
|  | ) -> Result<u64, std::io::Error> { | ||||||
|  |     let mut src_dir = world_path.clone(); | ||||||
|  |     src_dir.push(dir_name); | ||||||
|  |     let mut backup_dir = backup_path.clone(); | ||||||
|  |     backup_dir.push(dir_name); | ||||||
|  |     create_dir(&backup_dir)?; | ||||||
|  | 
 | ||||||
|  |     let mut file_count = 0; | ||||||
|  |     for entry in src_dir.read_dir()? { | ||||||
|  |         let entry = entry?; | ||||||
|  |         let mut target = backup_dir.clone(); | ||||||
|  |         target.push(entry.file_name()); | ||||||
|  | 
 | ||||||
|  |         copy(entry.path(), target)?; | ||||||
|  |         file_count += 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Ok(file_count) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Backup the regions
 | ||||||
|  | ///
 | ||||||
|  | /// # Param
 | ||||||
|  | /// * `dir_name` - name of the backup folder
 | ||||||
|  | /// * `save_radius` - block radius to save
 | ||||||
|  | /// * `world_path` - path to the world folder
 | ||||||
|  | /// * `backup_path` - path to the backup folder
 | ||||||
|  | pub fn backup_region( | ||||||
|  |     dir_name: &str, | ||||||
|  |     save_radius: u64, | ||||||
|  |     world_path: &PathBuf, | ||||||
|  |     backup_path: &PathBuf, | ||||||
|  | ) -> Result<u64, std::io::Error> { | ||||||
|  |     let mut count: u64 = 0; | ||||||
|  |     let mut src_dir = world_path.clone(); | ||||||
|  |     src_dir.push(dir_name); | ||||||
|  |     let mut backup_dir = backup_path.clone(); | ||||||
|  |     backup_dir.push(dir_name); | ||||||
|  |     create_dir(&backup_dir)?; | ||||||
|  | 
 | ||||||
|  |     let save_radius = (save_radius as f64 / 512.0).ceil() as i64; | ||||||
|  | 
 | ||||||
|  |     for entry in src_dir.read_dir()? { | ||||||
|  |         let entry = entry?; | ||||||
|  |         let file_name = entry.file_name().to_str().unwrap().to_string(); | ||||||
|  | 
 | ||||||
|  |         if let Ok(region) = Region::try_from(file_name) { | ||||||
|  |             if region.x.abs() <= save_radius && region.y.abs() <= save_radius { | ||||||
|  |                 let mut target = backup_dir.clone(); | ||||||
|  |                 target.push(entry.file_name()); | ||||||
|  | 
 | ||||||
|  |                 copy(entry.path(), target)?; | ||||||
|  |                 count += 1; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Ok(count) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Backup a world
 | ||||||
|  | ///
 | ||||||
|  | /// # Param
 | ||||||
|  | /// * `world_path` - path to the world folder
 | ||||||
|  | /// * `backup_path` - path to the backup folder
 | ||||||
|  | /// * `world_config` - world config options
 | ||||||
|  | pub fn backup_world( | ||||||
|  |     world_path: PathBuf, | ||||||
|  |     mut backup_path: PathBuf, | ||||||
|  |     world_config: &WorldConfig, | ||||||
|  | ) -> Result<u64, std::io::Error> { | ||||||
|  |     let region_count; | ||||||
|  |     backup_path.push(&world_config.world_name); | ||||||
|  |     create_dir(backup_path.as_path())?; | ||||||
|  | 
 | ||||||
|  |     backup_region("poi", world_config.save_radius, &world_path, &backup_path)?; | ||||||
|  |     region_count = backup_region( | ||||||
|  |         "region", | ||||||
|  |         world_config.save_radius, | ||||||
|  |         &world_path, | ||||||
|  |         &backup_path, | ||||||
|  |     )?; | ||||||
|  |     Ok(region_count) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Backup the overworld
 | ||||||
|  | ///
 | ||||||
|  | /// # Param
 | ||||||
|  | /// * `world_path` - path to the world folder
 | ||||||
|  | /// * `backup_path` - path to the backup folder
 | ||||||
|  | /// * `world_config` - world config options
 | ||||||
|  | pub fn backup_overworld( | ||||||
|  |     world_path: PathBuf, | ||||||
|  |     backup_path: PathBuf, | ||||||
|  |     world_config: &WorldConfig, | ||||||
|  | ) -> Result<(u64, u64), std::io::Error> { | ||||||
|  |     backup_dir("data", &world_path, &backup_path)?; | ||||||
|  |     backup_dir("stats", &world_path, &backup_path)?; | ||||||
|  | 
 | ||||||
|  |     backup_file("level.dat", world_path.clone(), backup_path.clone())?; | ||||||
|  |     backup_file("level.dat_old", world_path.clone(), backup_path.clone())?; | ||||||
|  |     backup_file("session.lock", world_path.clone(), backup_path.clone())?; | ||||||
|  |     backup_file("uid.dat", world_path.clone(), backup_path.clone())?; | ||||||
|  | 
 | ||||||
|  |     let player_count = backup_dir("playerdata", &world_path, &backup_path)?; | ||||||
|  |     let region_count = backup_world(world_path, backup_path, world_config)?; | ||||||
|  | 
 | ||||||
|  |     Ok((region_count, player_count)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Backup the nether
 | ||||||
|  | ///
 | ||||||
|  | /// # Param
 | ||||||
|  | /// * `world_path` - path to the world folder
 | ||||||
|  | /// * `backup_path` - path to the backup folder
 | ||||||
|  | /// * `world_config` - world config options
 | ||||||
|  | pub fn backup_nether( | ||||||
|  |     world_path: PathBuf, | ||||||
|  |     backup_path: PathBuf, | ||||||
|  |     world_config: &WorldConfig, | ||||||
|  | ) -> Result<u64, std::io::Error> { | ||||||
|  |     let mut nether_path = world_path; | ||||||
|  |     nether_path.push("DIM-1"); | ||||||
|  | 
 | ||||||
|  |     backup_world(nether_path, backup_path, world_config) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Backup the end
 | ||||||
|  | ///
 | ||||||
|  | /// # Param
 | ||||||
|  | /// * `world_path` - path to the world folder
 | ||||||
|  | /// * `backup_path` - path to the backup folder
 | ||||||
|  | /// * `world_config` - world config options
 | ||||||
|  | pub fn backup_end( | ||||||
|  |     world_path: PathBuf, | ||||||
|  |     backup_path: PathBuf, | ||||||
|  |     world_config: &WorldConfig, | ||||||
|  | ) -> Result<u64, std::io::Error> { | ||||||
|  |     let mut end_path = world_path; | ||||||
|  |     end_path.push("DIM1"); | ||||||
|  | 
 | ||||||
|  |     backup_world(end_path, backup_path, world_config) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Compress the backup after the files have been copied
 | ||||||
|  | ///
 | ||||||
|  | /// # Param
 | ||||||
|  | /// * `tmp_dir`: tmp directory with the backed up files
 | ||||||
|  | /// * `output_file`: output archive
 | ||||||
|  | pub fn compress_backup(tmp_dir: &PathBuf, output_file: &PathBuf) -> Result<(), std::io::Error> { | ||||||
|  |     let archive = File::create(output_file)?; | ||||||
|  |     let enc = GzEncoder::new(archive, Compression::default()); | ||||||
|  |     let mut tar_builder = tar::Builder::new(enc); | ||||||
|  |     tar_builder.append_dir_all(".", tmp_dir)?; | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Takes an existing backup and converts it to a singleplayer world
 | ||||||
|  | ///
 | ||||||
|  | /// # Param
 | ||||||
|  | /// * config - Albatross config
 | ||||||
|  | /// * backup - path of the backup to convert
 | ||||||
|  | /// * output - output path
 | ||||||
|  | pub fn convert_backup_to_sp( | ||||||
|  |     config: &AlbatrossConfig, | ||||||
|  |     backup: &PathBuf, | ||||||
|  |     output: &PathBuf, | ||||||
|  | ) -> Result<(), std::io::Error> { | ||||||
|  |     let backup_file = File::open(backup)?; | ||||||
|  |     let dec = GzDecoder::new(backup_file); | ||||||
|  |     let mut extract = Archive::new(dec); | ||||||
|  |     let extract_path = PathBuf::from("tmp"); | ||||||
|  |     extract.unpack(&extract_path)?; | ||||||
|  | 
 | ||||||
|  |     if let Some(worlds) = &config.world_config { | ||||||
|  |         for world in worlds { | ||||||
|  |             let world_type = match world.world_type.clone() { | ||||||
|  |                 Some(world_type) => world_type, | ||||||
|  |                 None => WorldType::OVERWORLD, | ||||||
|  |             }; | ||||||
|  |             let src = PathBuf::from(&extract_path).join(&world.world_name); | ||||||
|  |             let dest = PathBuf::from(&extract_path); | ||||||
|  |             match world_type { | ||||||
|  |                 WorldType::OVERWORLD => { | ||||||
|  |                     rename(src.clone().join("poi"), dest.clone().join("poi"))?; | ||||||
|  |                     rename(src.clone().join("region"), dest.clone().join("region"))?; | ||||||
|  |                 } | ||||||
|  |                 WorldType::NETHER => { | ||||||
|  |                     rename(src, dest.clone().join("DIM-1"))?; | ||||||
|  |                 } | ||||||
|  |                 WorldType::END => { | ||||||
|  |                     rename(src, dest.clone().join("DIM1"))?; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     compress_backup(&extract_path, output)?; | ||||||
|  |     remove_dir_all(&extract_path)?; | ||||||
|  | 
 | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Get the time of the backup from a file name
 | ||||||
|  | ///
 | ||||||
|  | /// # Param
 | ||||||
|  | /// * `archive_entry`: archive entry
 | ||||||
|  | fn get_time_from_file_name( | ||||||
|  |     archive_entry: &DirEntry, | ||||||
|  | ) -> Result<Option<NaiveDateTime>, std::io::Error> { | ||||||
|  |     let file_name = archive_entry.file_name().to_str().unwrap().to_string(); | ||||||
|  |     let name: Vec<&str> = file_name.split('_').collect(); | ||||||
|  | 
 | ||||||
|  |     Ok(chrono::NaiveDateTime::parse_from_str(name[0], "%d-%m-%y_%H.%M.%S").ok()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Removes the old backups from the ouput directory
 | ||||||
|  | ///
 | ||||||
|  | /// # Params
 | ||||||
|  | /// * `output_dir` - output directory containing
 | ||||||
|  | /// * `keep` - number of backups to keep
 | ||||||
|  | fn remove_old_backups(output_dir: &PathBuf, keep: u64) -> Result<usize, std::io::Error> { | ||||||
|  |     let mut backups = vec![]; | ||||||
|  |     let mut num_of_removed_backups: usize = 0; | ||||||
|  | 
 | ||||||
|  |     for entry in output_dir.read_dir()? { | ||||||
|  |         let entry = entry?; | ||||||
|  | 
 | ||||||
|  |         if let Some(ext) = entry.path().extension() { | ||||||
|  |             if ext == "gz" { | ||||||
|  |                 backups.push(entry); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if backups.len() > keep as usize { | ||||||
|  |         backups.sort_by(|a, b| { | ||||||
|  |             let a_time = get_time_from_file_name(a).unwrap().unwrap(); | ||||||
|  |             let b_time = get_time_from_file_name(b).unwrap().unwrap(); | ||||||
|  | 
 | ||||||
|  |             b_time.cmp(&a_time) | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         num_of_removed_backups = backups.len() - keep as usize; | ||||||
|  | 
 | ||||||
|  |         for _i in 0..num_of_removed_backups { | ||||||
|  |             let oldest = backups.pop().unwrap(); | ||||||
|  |             remove_file(oldest.path())?; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Ok(num_of_removed_backups) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Backup the configured worlds from a minecraft server
 | ||||||
|  | ///
 | ||||||
|  | /// # Params
 | ||||||
|  | /// * `cfg` - config file
 | ||||||
|  | pub fn do_backup(cfg: AlbatrossConfig, output: Option<PathBuf>) -> Result<(), std::io::Error> { | ||||||
|  |     let server_base_dir = cfg.backup.minecraft_dir.clone(); | ||||||
|  |     let worlds = cfg.world_config.clone().expect("No worlds configured"); | ||||||
|  |     let time_str = Utc::now().format("%d-%m-%y_%H.%M.%S").to_string(); | ||||||
|  |     let backup_name = format!("{}_backup.tar.gz", time_str); | ||||||
|  |     let mut output_archive = match output { | ||||||
|  |         Some(out_path) => out_path, | ||||||
|  |         None => cfg.backup.output_dir.clone(), | ||||||
|  |     }; | ||||||
|  |     output_archive.push(backup_name); | ||||||
|  |     let mut tmp_dir = cfg.backup.output_dir.clone(); | ||||||
|  |     tmp_dir.push("tmp"); | ||||||
|  |     remove_dir_all(&tmp_dir).ok(); | ||||||
|  | 
 | ||||||
|  |     create_dir_all(tmp_dir.clone()).unwrap(); | ||||||
|  | 
 | ||||||
|  |     send_webhook("**Albatross is swooping in to backup your worlds!**", &cfg); | ||||||
|  |     let timer = Instant::now(); | ||||||
|  |     for world in worlds { | ||||||
|  |         let mut world_dir = server_base_dir.clone(); | ||||||
|  |         let world_name = world.world_name.clone(); | ||||||
|  |         let world_type = match world.world_type.clone() { | ||||||
|  |             Some(world_type) => world_type, | ||||||
|  |             None => WorldType::OVERWORLD, | ||||||
|  |         }; | ||||||
|  |         world_dir.push(world_name.clone()); | ||||||
|  | 
 | ||||||
|  |         if world_dir.exists() && world_dir.is_dir() { | ||||||
|  |             send_webhook( | ||||||
|  |                 format!("Starting backup of **{}**", world_name).as_str(), | ||||||
|  |                 &cfg, | ||||||
|  |             ); | ||||||
|  |             let webhook_msg = match world_type { | ||||||
|  |                 WorldType::OVERWORLD => { | ||||||
|  |                     let (region_count, player_count) = | ||||||
|  |                         backup_overworld(world_dir.clone(), tmp_dir.clone(), &world)?; | ||||||
|  |                     format!( | ||||||
|  |                         "{} regions and {} player files backed up.", | ||||||
|  |                         region_count, player_count | ||||||
|  |                     ) | ||||||
|  |                 } | ||||||
|  |                 WorldType::NETHER => { | ||||||
|  |                     let region_count = backup_nether(world_dir, tmp_dir.clone(), &world)?; | ||||||
|  |                     format!("{} regions backed up.", region_count) | ||||||
|  |                 } | ||||||
|  |                 WorldType::END => { | ||||||
|  |                     let region_count = backup_end(world_dir, tmp_dir.clone(), &world)?; | ||||||
|  |                     format!("{} regions backed up.", region_count) | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             send_webhook(&webhook_msg, &cfg); | ||||||
|  |         } else { | ||||||
|  |             send_webhook(format!("Error: {} not found.", world_name).as_str(), &cfg); | ||||||
|  |             println!("World \"{}\" not found", world_name.clone()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     backup::compress_backup(&tmp_dir, &output_archive)?; | ||||||
|  | 
 | ||||||
|  |     remove_dir_all(&tmp_dir)?; | ||||||
|  | 
 | ||||||
|  |     let backups_removed = remove_old_backups(&cfg.backup.output_dir, cfg.backup.backups_to_keep)?; | ||||||
|  | 
 | ||||||
|  |     if backups_removed > 0 { | ||||||
|  |         let msg = format!( | ||||||
|  |             "Albatross mistook **{}** of your old backups for some french fries and ate them!! SKRAWWWW", | ||||||
|  |             backups_removed | ||||||
|  |         ); | ||||||
|  |         send_webhook(msg.as_str(), &cfg); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     let secs = timer.elapsed().as_secs(); | ||||||
|  |     send_webhook( | ||||||
|  |         format!("**Full backup completed in {}s**! *SKREEEEEEEEEE*", secs).as_str(), | ||||||
|  |         &cfg, | ||||||
|  |     ); | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
| @ -1,4 +1,5 @@ | |||||||
| use config::{Config, ConfigError, File}; | use config::{Config, ConfigError, File}; | ||||||
|  | use serde::Deserialize; | ||||||
| use std::path::PathBuf; | use std::path::PathBuf; | ||||||
| 
 | 
 | ||||||
| /// World types supported
 | /// World types supported
 | ||||||
							
								
								
									
										16
									
								
								src/discord.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/discord.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | |||||||
|  | use crate::config::AlbatrossConfig; | ||||||
|  | use discord_hooks_rs::DiscordWebhook; | ||||||
|  | 
 | ||||||
|  | /// Sends a webhook to Discord if its configured
 | ||||||
|  | ///
 | ||||||
|  | /// # Params
 | ||||||
|  | /// * `msg` - Message to send to discord
 | ||||||
|  | /// * `cfg` - Albatross config
 | ||||||
|  | pub fn send_webhook(msg: &str, cfg: &AlbatrossConfig) { | ||||||
|  |     if let Some(webhook) = &cfg.backup.discord_webhook { | ||||||
|  |         let json = DiscordWebhook::new().content(msg); | ||||||
|  | 
 | ||||||
|  |         let client = reqwest::blocking::Client::new(); | ||||||
|  |         client.post(webhook).json(&json).send().ok(); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										425
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										425
									
								
								src/main.rs
									
									
									
									
									
								
							| @ -1,391 +1,72 @@ | |||||||
| extern crate serde; |  | ||||||
| 
 |  | ||||||
| #[macro_use] |  | ||||||
| extern crate serde_derive; |  | ||||||
| 
 |  | ||||||
| use chrono::{NaiveDateTime, Utc}; |  | ||||||
| use clap::{App, Arg}; |  | ||||||
| use flate2::write::GzEncoder; |  | ||||||
| use flate2::Compression; |  | ||||||
| use regex::Regex; |  | ||||||
| use std::fs::{copy, create_dir, create_dir_all, remove_dir_all, remove_file, DirEntry, File}; |  | ||||||
| use std::path::PathBuf; | use std::path::PathBuf; | ||||||
| use std::time::Instant; | use structopt::StructOpt; | ||||||
| 
 | 
 | ||||||
| mod albatross_config; | mod backup; | ||||||
|  | mod config; | ||||||
|  | mod discord; | ||||||
|  | mod region; | ||||||
| 
 | 
 | ||||||
| use albatross_config::{AlbatrossConfig, WorldConfig, WorldType}; | use crate::backup::{convert_backup_to_sp, do_backup}; | ||||||
| use std::collections::HashMap; | use crate::config::AlbatrossConfig; | ||||||
| 
 | 
 | ||||||
| /// Struct to store information about the region
 | #[derive(Debug, StructOpt)] | ||||||
| struct Region { | #[structopt(about = "Backup your Minecraft Server!")] | ||||||
|     /// x position of the region
 | struct Albatross { | ||||||
|     x: i64, |     /// Path to the Albatross config
 | ||||||
|     /// y position of the region
 |     #[structopt(short, long, env = "ALBATROSS_CONFIG", parse(from_os_str))] | ||||||
|     y: i64, |     config_path: PathBuf, | ||||||
|  |     /// Subcommand
 | ||||||
|  |     #[structopt(subcommand)] | ||||||
|  |     sub_command: SubCommand, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Region { | #[derive(Debug, StructOpt)] | ||||||
|     fn from_string(string: String) -> Option<Self> { | enum SubCommand { | ||||||
|         let re = Regex::new(r"r\.(?P<x>-?[0-9]*)+\.(?P<y>-?[0-9]*)").unwrap(); |     /// Backup a server
 | ||||||
|         if re.is_match(string.as_str()) { |     Backup { | ||||||
|             let captures = re.captures(string.as_str()).unwrap(); |         /// Output location override
 | ||||||
|  |         #[structopt(short = "o", long = "ouptut", parse(from_os_str))] | ||||||
|  |         output: Option<PathBuf>, | ||||||
|  |     }, | ||||||
|  |     /// Export a backup as a single player world
 | ||||||
|  |     Export { | ||||||
|  |         /// Convert backup to singleplayer world
 | ||||||
|  |         #[structopt(parse(from_os_str))] | ||||||
|  |         input_backup: PathBuf, | ||||||
| 
 | 
 | ||||||
|             return Some(Region { |         /// Output location override
 | ||||||
|                 x: captures["x"].parse::<i64>().unwrap(), |         #[structopt(parse(from_os_str))] | ||||||
|                 y: captures["y"].parse::<i64>().unwrap(), |         output: PathBuf, | ||||||
|             }); |     }, | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         None |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Backup a directory
 |  | ||||||
| ///
 |  | ||||||
| /// # Param
 |  | ||||||
| /// * `world_path` - path to the world folder
 |  | ||||||
| /// * `backup_path` - path to the backup folder
 |  | ||||||
| fn backup_dir( |  | ||||||
|     dir_name: &str, |  | ||||||
|     world_path: &PathBuf, |  | ||||||
|     backup_path: &PathBuf, |  | ||||||
| ) -> Result<u64, std::io::Error> { |  | ||||||
|     let mut src_dir = world_path.clone(); |  | ||||||
|     src_dir.push(dir_name); |  | ||||||
|     let mut backup_dir = backup_path.clone(); |  | ||||||
|     backup_dir.push(dir_name); |  | ||||||
|     create_dir(&backup_dir)?; |  | ||||||
| 
 |  | ||||||
|     let mut file_count = 0; |  | ||||||
|     for entry in src_dir.read_dir()? { |  | ||||||
|         let entry = entry?; |  | ||||||
|         let mut target = backup_dir.clone(); |  | ||||||
|         target.push(entry.file_name()); |  | ||||||
| 
 |  | ||||||
|         copy(entry.path(), target)?; |  | ||||||
|         file_count += 1; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     Ok(file_count) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Backup the regions
 |  | ||||||
| ///
 |  | ||||||
| /// # Param
 |  | ||||||
| /// * `dir_name` - name of the backup folder
 |  | ||||||
| /// * `save_radius` - block radius to save
 |  | ||||||
| /// * `world_path` - path to the world folder
 |  | ||||||
| /// * `backup_path` - path to the backup folder
 |  | ||||||
| fn backup_region( |  | ||||||
|     dir_name: &str, |  | ||||||
|     save_radius: u64, |  | ||||||
|     world_path: &PathBuf, |  | ||||||
|     backup_path: &PathBuf, |  | ||||||
| ) -> Result<u64, std::io::Error> { |  | ||||||
|     let mut count: u64 = 0; |  | ||||||
|     let mut src_dir = world_path.clone(); |  | ||||||
|     src_dir.push(dir_name); |  | ||||||
|     let mut backup_dir = backup_path.clone(); |  | ||||||
|     backup_dir.push(dir_name); |  | ||||||
|     create_dir(&backup_dir)?; |  | ||||||
| 
 |  | ||||||
|     let save_radius = (save_radius as f64 / 512.0).ceil() as i64; |  | ||||||
| 
 |  | ||||||
|     for entry in src_dir.read_dir()? { |  | ||||||
|         let entry = entry?; |  | ||||||
|         let file_name = entry.file_name().to_str().unwrap().to_string(); |  | ||||||
| 
 |  | ||||||
|         if let Some(region) = Region::from_string(file_name) { |  | ||||||
|             if region.x.abs() <= save_radius && region.y.abs() <= save_radius { |  | ||||||
|                 let mut target = backup_dir.clone(); |  | ||||||
|                 target.push(entry.file_name()); |  | ||||||
| 
 |  | ||||||
|                 copy(entry.path(), target)?; |  | ||||||
|                 count += 1; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     Ok(count) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Backup a world
 |  | ||||||
| ///
 |  | ||||||
| /// # Param
 |  | ||||||
| /// * `world_path` - path to the world folder
 |  | ||||||
| /// * `backup_path` - path to the backup folder
 |  | ||||||
| /// * `world_config` - world config options
 |  | ||||||
| fn backup_world( |  | ||||||
|     world_path: PathBuf, |  | ||||||
|     backup_path: PathBuf, |  | ||||||
|     world_config: &WorldConfig, |  | ||||||
| ) -> Result<u64, std::io::Error> { |  | ||||||
|     let mut backup_path = backup_path.clone(); |  | ||||||
|     let region_count; |  | ||||||
|     backup_path.push(&world_config.world_name); |  | ||||||
|     create_dir(backup_path.as_path())?; |  | ||||||
| 
 |  | ||||||
|     backup_region("poi", world_config.save_radius, &world_path, &backup_path)?; |  | ||||||
|     region_count = backup_region( |  | ||||||
|         "region", |  | ||||||
|         world_config.save_radius, |  | ||||||
|         &world_path, |  | ||||||
|         &backup_path, |  | ||||||
|     )?; |  | ||||||
|     backup_dir("data", &world_path, &backup_path)?; |  | ||||||
| 
 |  | ||||||
|     Ok(region_count) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Backup the overworld
 |  | ||||||
| ///
 |  | ||||||
| /// # Param
 |  | ||||||
| /// * `world_path` - path to the world folder
 |  | ||||||
| /// * `backup_path` - path to the backup folder
 |  | ||||||
| /// * `world_config` - world config options
 |  | ||||||
| fn backup_overworld( |  | ||||||
|     world_path: PathBuf, |  | ||||||
|     backup_path: PathBuf, |  | ||||||
|     world_config: &WorldConfig, |  | ||||||
| ) -> Result<u64, std::io::Error> { |  | ||||||
|     backup_world(world_path, backup_path, world_config) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Backup the nether
 |  | ||||||
| ///
 |  | ||||||
| /// # Param
 |  | ||||||
| /// * `world_path` - path to the world folder
 |  | ||||||
| /// * `backup_path` - path to the backup folder
 |  | ||||||
| /// * `world_config` - world config options
 |  | ||||||
| fn backup_nether( |  | ||||||
|     world_path: PathBuf, |  | ||||||
|     backup_path: PathBuf, |  | ||||||
|     world_config: &WorldConfig, |  | ||||||
| ) -> Result<u64, std::io::Error> { |  | ||||||
|     let mut nether_path = world_path.clone(); |  | ||||||
|     nether_path.push("DIM-1"); |  | ||||||
| 
 |  | ||||||
|     backup_world(nether_path, backup_path, world_config) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Backup the end
 |  | ||||||
| ///
 |  | ||||||
| /// # Param
 |  | ||||||
| /// * `world_path` - path to the world folder
 |  | ||||||
| /// * `backup_path` - path to the backup folder
 |  | ||||||
| /// * `world_config` - world config options
 |  | ||||||
| fn backup_end( |  | ||||||
|     world_path: PathBuf, |  | ||||||
|     backup_path: PathBuf, |  | ||||||
|     world_config: &WorldConfig, |  | ||||||
| ) -> Result<u64, std::io::Error> { |  | ||||||
|     let mut end_path = world_path.clone(); |  | ||||||
|     end_path.push("DIM1"); |  | ||||||
| 
 |  | ||||||
|     backup_world(end_path, backup_path, world_config) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Compress the backup after the files have been copied
 |  | ||||||
| ///
 |  | ||||||
| /// # Param
 |  | ||||||
| /// * `tmp_dir`: tmp directory with the backed up files
 |  | ||||||
| /// * `output_file`: output archive
 |  | ||||||
| fn compress_backup(tmp_dir: &PathBuf, output_file: &PathBuf) -> Result<(), std::io::Error> { |  | ||||||
|     let archive = File::create(output_file)?; |  | ||||||
|     let enc = GzEncoder::new(archive, Compression::default()); |  | ||||||
|     let mut tar_builder = tar::Builder::new(enc); |  | ||||||
|     tar_builder.append_dir_all(".", tmp_dir)?; |  | ||||||
|     Ok(()) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Get the time of the backup from a file name
 |  | ||||||
| ///
 |  | ||||||
| /// # Param
 |  | ||||||
| /// * `archive_entry`: archive entry
 |  | ||||||
| fn get_time_from_file_name( |  | ||||||
|     archive_entry: &DirEntry, |  | ||||||
| ) -> Result<Option<NaiveDateTime>, std::io::Error> { |  | ||||||
|     let file_name = archive_entry.file_name().to_str().unwrap().to_string(); |  | ||||||
|     let name: Vec<&str> = file_name.split("_").collect(); |  | ||||||
| 
 |  | ||||||
|     Ok(chrono::NaiveDateTime::parse_from_str(name[0], "%d-%m-%y-%H:%M:%S").ok()) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Removes the old backups from the ouput directory
 |  | ||||||
| ///
 |  | ||||||
| /// # Params
 |  | ||||||
| /// * `output_dir` - output directory containing
 |  | ||||||
| /// * `keep` - number of backups to keep
 |  | ||||||
| fn remove_old_backups(output_dir: &PathBuf, keep: u64) -> Result<usize, std::io::Error> { |  | ||||||
|     let mut backups = vec![]; |  | ||||||
|     let mut num_of_removed_backups: usize = 0; |  | ||||||
| 
 |  | ||||||
|     for entry in output_dir.read_dir()? { |  | ||||||
|         let entry = entry?; |  | ||||||
| 
 |  | ||||||
|         if let Some(ext) = entry.path().extension() { |  | ||||||
|             if ext == "gz" { |  | ||||||
|                 backups.push(entry); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if backups.len() > keep as usize { |  | ||||||
|         backups.sort_by(|a, b| { |  | ||||||
|             let a_time = get_time_from_file_name(a).unwrap().unwrap(); |  | ||||||
|             let b_time = get_time_from_file_name(b).unwrap().unwrap(); |  | ||||||
| 
 |  | ||||||
|             b_time.cmp(&a_time) |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         num_of_removed_backups = backups.len() - keep as usize; |  | ||||||
| 
 |  | ||||||
|         for _i in 0..num_of_removed_backups { |  | ||||||
|             let oldest = backups.pop().unwrap(); |  | ||||||
|             remove_file(oldest.path())?; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     Ok(num_of_removed_backups) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Sends a webhook to Discord if its configured
 |  | ||||||
| ///
 |  | ||||||
| /// # Params
 |  | ||||||
| /// * `msg` - Message to send to discord
 |  | ||||||
| /// * `cfg` - Albatross config
 |  | ||||||
| fn send_webhook(msg: &str, cfg: &AlbatrossConfig) { |  | ||||||
|     if let Some(webhook) = &cfg.backup.discord_webhook { |  | ||||||
|         let mut map = HashMap::new(); |  | ||||||
| 
 |  | ||||||
|         map.insert("content".to_string(), msg.to_string()); |  | ||||||
| 
 |  | ||||||
|         let client = reqwest::blocking::Client::new(); |  | ||||||
|         client.post(webhook).json(&map).send().ok(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Backup the configured worlds from a minecraft server
 |  | ||||||
| ///
 |  | ||||||
| /// # Params
 |  | ||||||
| /// * `cfg` - config file
 |  | ||||||
| fn do_backup(cfg: AlbatrossConfig) -> Result<(), std::io::Error> { |  | ||||||
|     let server_base_dir = cfg.backup.minecraft_dir.clone(); |  | ||||||
|     let worlds = cfg.world_config.clone().expect("No worlds configured"); |  | ||||||
|     let time_str = Utc::now().format("%d-%m-%y-%H:%M:%S").to_string(); |  | ||||||
|     let backup_name = format!("{}_backup.tar.gz", time_str); |  | ||||||
|     let mut output_archive = cfg.backup.output_dir.clone(); |  | ||||||
|     output_archive.push(backup_name); |  | ||||||
|     let mut tmp_dir = cfg.backup.output_dir.clone(); |  | ||||||
|     tmp_dir.push("tmp"); |  | ||||||
|     remove_dir_all(&tmp_dir).ok(); |  | ||||||
| 
 |  | ||||||
|     create_dir_all(tmp_dir.clone()).unwrap(); |  | ||||||
| 
 |  | ||||||
|     send_webhook("**Albatross is swooping in to backup your worlds!**", &cfg); |  | ||||||
|     let timer = Instant::now(); |  | ||||||
|     for world in worlds { |  | ||||||
|         let mut world_dir = server_base_dir.clone(); |  | ||||||
|         let world_name = world.world_name.clone(); |  | ||||||
|         let world_type = match world.world_type.clone() { |  | ||||||
|             Some(world_type) => world_type, |  | ||||||
|             None => WorldType::OVERWORLD, |  | ||||||
|         }; |  | ||||||
|         world_dir.push(world_name.clone()); |  | ||||||
| 
 |  | ||||||
|         if world_dir.exists() && world_dir.is_dir() { |  | ||||||
|             send_webhook( |  | ||||||
|                 format!("Starting backup of **{}**", world_name).as_str(), |  | ||||||
|                 &cfg, |  | ||||||
|             ); |  | ||||||
|             match world_type { |  | ||||||
|                 WorldType::OVERWORLD => { |  | ||||||
|                     let region_count = |  | ||||||
|                         backup_overworld(world_dir.clone(), tmp_dir.clone(), &world)?; |  | ||||||
|                     let player_count = backup_dir("playerdata", &world_dir, &tmp_dir)?; |  | ||||||
|                     send_webhook( |  | ||||||
|                         format!( |  | ||||||
|                             "{} regions and {} player files backed up.", |  | ||||||
|                             region_count, player_count |  | ||||||
|                         ) |  | ||||||
|                         .as_str(), |  | ||||||
|                         &cfg, |  | ||||||
|                     ); |  | ||||||
|                 } |  | ||||||
|                 WorldType::NETHER => { |  | ||||||
|                     let region_count = backup_nether(world_dir, tmp_dir.clone(), &world)?; |  | ||||||
|                     send_webhook( |  | ||||||
|                         format!("{} regions backed up.", region_count).as_str(), |  | ||||||
|                         &cfg, |  | ||||||
|                     ); |  | ||||||
|                 } |  | ||||||
|                 WorldType::END => { |  | ||||||
|                     let region_count = backup_end(world_dir, tmp_dir.clone(), &world)?; |  | ||||||
|                     send_webhook( |  | ||||||
|                         format!("{} regions backed up.", region_count).as_str(), |  | ||||||
|                         &cfg, |  | ||||||
|                     ); |  | ||||||
|                 } |  | ||||||
|             }; |  | ||||||
|         } else { |  | ||||||
|             send_webhook(format!("Error: {} not found.", world_name).as_str(), &cfg); |  | ||||||
|             println!("World \"{}\" not found", world_name.clone()); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     compress_backup(&tmp_dir, &output_archive)?; |  | ||||||
| 
 |  | ||||||
|     remove_dir_all(&tmp_dir)?; |  | ||||||
| 
 |  | ||||||
|     let backups_removed = remove_old_backups(&cfg.backup.output_dir, cfg.backup.backups_to_keep)?; |  | ||||||
| 
 |  | ||||||
|     if backups_removed > 0 { |  | ||||||
|         let msg = format!( |  | ||||||
|             "Albatross mistook **{}** of your old backups for some french fries and ate them!! SKRAWWWW", |  | ||||||
|             backups_removed |  | ||||||
|         ); |  | ||||||
|         send_webhook(msg.as_str(), &cfg); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     let secs = timer.elapsed().as_secs(); |  | ||||||
|     send_webhook( |  | ||||||
|         format!("**Full backup completed in {}s**! *SKREEEEEEEEEE*", secs).as_str(), |  | ||||||
|         &cfg, |  | ||||||
|     ); |  | ||||||
|     Ok(()) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Albatross
 |  | ||||||
| ///
 |  | ||||||
| /// Run backups of a Minecraft world
 |  | ||||||
| fn main() { | fn main() { | ||||||
|     let mut app = App::new("Albatross").about("Backup your worlds").arg( |     let opt = Albatross::from_args(); | ||||||
|         Arg::with_name("config") |  | ||||||
|             .index(1) |  | ||||||
|             .short("c") |  | ||||||
|             .long("config") |  | ||||||
|             .value_name("CONFIG_PATH") |  | ||||||
|             .help("Config file path"), |  | ||||||
|     ); |  | ||||||
|     // Get arg parser
 |  | ||||||
|     let matches = app.clone().get_matches(); |  | ||||||
| 
 | 
 | ||||||
|     if let Some(cfg_path) = matches.value_of("config") { |     let cfg = AlbatrossConfig::new(opt.config_path.into_os_string().to_str().unwrap()) | ||||||
|         let cfg = AlbatrossConfig::new(cfg_path).expect("Config not found"); |         .expect("Config not found"); | ||||||
| 
 | 
 | ||||||
|     if cfg.world_config.is_some() { |     if cfg.world_config.is_some() { | ||||||
|  |         match opt.sub_command { | ||||||
|  |             SubCommand::Backup { output } => { | ||||||
|                 println!("Starting backup"); |                 println!("Starting backup"); | ||||||
|             match do_backup(cfg) { |                 match do_backup(cfg, output) { | ||||||
|                 Err(e) => println!("Error doing backup: {}", e), |                     Ok(_) => println!("Backup complete!"), | ||||||
|                 _ => {} |                     Err(e) => println!("Error doing backup: {:?}", e), | ||||||
|  |                 }; | ||||||
|  |             } | ||||||
|  |             SubCommand::Export { | ||||||
|  |                 input_backup, | ||||||
|  |                 output, | ||||||
|  |             } => { | ||||||
|  |                 println!("Starting export"); | ||||||
|  |                 match convert_backup_to_sp(&cfg, &input_backup, &output) { | ||||||
|  |                     Ok(_) => println!("Export complete!"), | ||||||
|  |                     Err(e) => println!("Error exporting backup: {:?}", e), | ||||||
|  |                 }; | ||||||
|             } |             } | ||||||
| 
 |  | ||||||
|             println!("Backup complete"); |  | ||||||
|         } else { |  | ||||||
|             println!("No worlds specified to backed up!") |  | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         app.print_help().expect("Unable to print help"); |         println!("No worlds specified in config file!") | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										42
									
								
								src/region.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/region.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | |||||||
|  | use regex::Regex; | ||||||
|  | use std::convert::TryFrom; | ||||||
|  | use std::error::Error; | ||||||
|  | use std::fmt; | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone)] | ||||||
|  | pub struct RegionParseError; | ||||||
|  | 
 | ||||||
|  | impl fmt::Display for RegionParseError { | ||||||
|  |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|  |         write!(f, "Unable to parse region file name") | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Error for RegionParseError {} | ||||||
|  | 
 | ||||||
|  | /// Struct to store information about the region
 | ||||||
|  | pub struct Region { | ||||||
|  |     /// x position of the region
 | ||||||
|  |     pub x: i64, | ||||||
|  |     /// y position of the region
 | ||||||
|  |     pub y: i64, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl TryFrom<String> for Region { | ||||||
|  |     type Error = RegionParseError; | ||||||
|  | 
 | ||||||
|  |     /// Try from string
 | ||||||
|  |     fn try_from(value: String) -> Result<Self, Self::Error> { | ||||||
|  |         let re = Regex::new(r"r\.(?P<x>-?[0-9]*)+\.(?P<y>-?[0-9]*)").unwrap(); | ||||||
|  |         if re.is_match(&value) { | ||||||
|  |             let captures = re.captures(value.as_str()).unwrap(); | ||||||
|  | 
 | ||||||
|  |             return Ok(Region { | ||||||
|  |                 x: captures["x"].parse::<i64>().unwrap(), | ||||||
|  |                 y: captures["y"].parse::<i64>().unwrap(), | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         Err(RegionParseError) | ||||||
|  |     } | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user