diff --git a/.gitignore b/.gitignore index ec180a76..add94270 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ target/ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock +#Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk @@ -15,6 +15,8 @@ Cargo.lock .history/ -config/config.yml +config/config.yaml .idea/ + +.env \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..7b84db47 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1897 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" + +[[package]] +name = "async-channel" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "pure-rust-locales", + "time 0.1.45", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "concurrent-queue" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time 0.3.21", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e4b6aa369f41f5faa04bb80c9b1f4216ea81646ed6124d76ba5c49a7aafd9cd" +dependencies = [ + "cookie", + "idna 0.2.3", + "log", + "publicsuffix", + "serde", + "serde_json", + "time 0.3.21", + "url", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "dm-ticket" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-channel", + "bytes", + "chrono", + "dotenv", + "fast_qr", + "futures", + "image", + "log", + "md5", + "pretty_env_logger", + "rand", + "reqwest", + "rqrr", + "schemars", + "serde", + "serde_json", + "serde_yaml", + "tokio", + "urlencoding", +] + +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + +[[package]] +name = "dyn-clone" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "exr" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd2162b720141a91a054640662d3edce3d50a944a50ffca5313cd951abb35b4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide 0.6.2", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fast_qr" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b373fdbfe37320939c04845ff030e142dbfa1f4dc4857d1270b81c74025d8" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "fdeflate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "flate2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +dependencies = [ + "crc32fast", + "miniz_oxide 0.7.1", +] + +[[package]] +name = "flume" +version = "0.10.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project", + "spin 0.9.8", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "g2gen" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2c7625b2fc250dd90b63f7887a6bb0f7ec1d714c8278415bea2669ef20820e" +dependencies = [ + "g2poly", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "g2p" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc36d9bdc3d2da057775a9f4fa7d7b09edab3e0eda7a92cc353358fa63b8519e" +dependencies = [ + "g2gen", + "g2poly", +] + +[[package]] +name = "g2poly" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af6a86e750338603ea2c14b1c0bfe58cd61f87ca67a0021d9334996024608e12" + +[[package]] +name = "getrandom" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "gif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "h2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" +dependencies = [ + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error", +] + +[[package]] +name = "hyper" +version = "0.14.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0646026eb1b3eea4cd9ba47912ea5ce9cc07713d105b1a14698f4e6433d348b7" +dependencies = [ + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "image" +version = "0.24.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "jpeg-decoder", + "num-rational", + "num-traits", + "png", + "qoi", + "tiff", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "ipnet" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "jpeg-decoder" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" +dependencies = [ + "rayon", +] + +[[package]] +name = "js-sys" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + +[[package]] +name = "libc" +version = "0.2.144" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" + +[[package]] +name = "lru" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e7d46de488603ffdd5f30afbc64fbba2378214a2c3a2fb83abf3d33126df17" +dependencies = [ + "hashbrown 0.13.2", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", +] + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b" + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "png" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaeebc51f9e7d2c150d3f3bfeb667f2aa985db5ef1e3d212847bdedb488beeaa" +dependencies = [ + "bitflags", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide 0.7.1", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "pretty_env_logger" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" +dependencies = [ + "env_logger", + "log", +] + +[[package]] +name = "proc-macro2" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + +[[package]] +name = "publicsuffix" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a8c1bda5ae1af7f99a2962e49df150414a43d62404644d98dd5c3a93d07457" +dependencies = [ + "idna 0.3.0", + "psl-types", +] + +[[package]] +name = "pure-rust-locales" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45c49fc4f91f35bae654f85ebb3a44d60ac64f11b3166ffa609def390c732d8" + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "regex" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" + +[[package]] +name = "reqwest" +version = "0.11.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +dependencies = [ + "base64", + "bytes", + "cookie", + "cookie_store", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-rustls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rqrr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a8b87d1f9f69bb1a6c77e20fd303f9617b2b68dcff87cd9bcbfff2ced4b8a0b" +dependencies = [ + "g2p", + "image", + "lru", +] + +[[package]] +name = "rustls" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c911ba11bc8433e811ce56fde130ccf32f5127cab0e0194e9c68c5a5b671791e" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-webpki" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "schemars" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "serde" +version = "1.0.163" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.163" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_json" +version = "1.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9d684e3ec7de3bf5466b32bd75303ac16f0736426e5a4e0d6e489559ce1249c" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "tiff" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7449334f9ff2baf290d55d73983a7d6fa15e01198faef72af07e2a8db851e471" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "time" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" +dependencies = [ + "itoa", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" +dependencies = [ + "autocfg", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0d409377ff5b1e3ca6437aa86c1eb7d40c134bfec254e44c830defa92669db5" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unsafe-libyaml" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna 0.3.0", + "percent-encoding", +] + +[[package]] +name = "urlencoding" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.18", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" + +[[package]] +name = "web-sys" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki", +] + +[[package]] +name = "weezl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] diff --git a/Cargo.toml b/Cargo.toml index 68c817ad..f3641452 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,12 +10,38 @@ serde = {version = "1.0.148", features = ["derive"]} serde_json = {version = "1.0.89", default-features = false, features = ["alloc"]} serde_yaml = "0.9.21" schemars = "0.8.12" -tokio = { version = "1.21.2", default-features = false, features = ["macros", "rt-multi-thread", "signal", "time"] } +tokio = { version = "1.21.2", default-features = false, features = ["macros", "rt-multi-thread", "signal", "time", "fs"] } reqwest = {version="0.11.12", default-features=false, features = ["json", "rustls-tls", "cookies", "multipart"]} anyhow = {version="1.0.66"} log={version="0.4.17"} pretty_env_logger="0.4.0" md5 = {version="0.7.0"} +dotenv = {version= "0.15.0"} futures = {version="0.3.28"} chrono = {version="0.4.24", features = ["unstable-locales"] } -async-channel={version = "1.8"} \ No newline at end of file +async-channel={version = "1.8"} +urlencoding = {version = "2.1.2"} +fast_qr = "0.8.5" +image = {version = "0.24.6"} +rqrr = {version = "0.6.0"} +bytes = "1" +rand={version="0.8.5"} + + +[[bin]] +name = "dm-login" +path = "src/bin/login.rs" + + +[[bin]] +name = "dm-ticket" +path = "src/bin/ticket.rs" + + + +[profile.release] +lto = true +opt-level="z" +codegen-units = 1 +panic = "abort" +strip = true \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index efea58ee..0a07332f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,11 +15,17 @@ COPY .cargo /usr/src/dm-ticket/.cargo WORKDIR /usr/src/dm-ticket +RUN mkdir src/bin && cat src/main.rs > src/bin/login.rs + RUN cargo build --release --verbose COPY src /usr/src/dm-ticket/src/ -RUN RUST_BACKTRACE=1 cargo build --release && upx /usr/src/dm-ticket/target/release/dm-ticket +RUN RUST_BACKTRACE=1 cargo build --release --verbose --bin dm-ticket && upx /usr/src/dm-ticket/target/release/dm-ticket + + +RUN RUST_BACKTRACE=1 cargo build --release --verbose --bin dm-login && upx /usr/src/dm-ticket/target/release/dm-login + FROM --platform=$TARGETPLATFORM alpine:3.17 as runtime @@ -36,4 +42,6 @@ WORKDIR /src/ COPY --from=builder /usr/src/dm-ticket/target/release/dm-ticket /usr/bin/dm-ticket +COPY --from=builder /usr/src/dm-ticket/target/release/dm-login /usr/bin/dm-login + CMD ["/usr/sbin/crond", "-f", "-d", "0"] \ No newline at end of file diff --git a/README.md b/README.md index 0f533414..704f73c4 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,9 @@ 大麦网自动购票, 支持docker一键部署。 +**友情提示:** +1. 请先将README看完, 会避免一些问题。 +2. 如有问题,大部分可以在issue中找到答案。 ## 特别声明 - 本项目内所有资源文件,禁止任何公众号、自媒体进行任何形式的转载、发布。 @@ -27,16 +30,33 @@ - sample 2: ![run2.png](./images/run2.png) + +## 命令列表 + +- 自动购票: `docker exec -it dm-ticket dm-ticket` +- 扫码登录: `docker exec -it dm-ticket dm-login` + + + +## 常见问题 + - 如遇到`Connection refused (os error 111)`错误, 说明token-server还没启动完成, 等待片刻即可。 ![Connection refused (os error 111)](./images/connection_errors.png) +- 生成订单失败, ["RGV587_ERROR::SM::哎哟喂,被挤爆啦,请稍后重试!"], 请检查是否复制了完整的cookie, ip有问题(一般是使用了大厂服务器/代理, 实在需要可以使用这种[动态VPS](https://www.fwvps.com/?aff=6bb13))。 +- ["B-00203-200-100::网络开小差了,再试一次吧~"], 请检查是否复制了完整的cookie。 +- docker/docker-compose安装使用问题,请善用搜索引擎, 自行搜索解决方案。 +- 是否支持多账号, v0.1.0版本是支持多账号的。后续可能取消。要实现多账号支持, 开启多个docker容器也可以支持。 +- 频繁尝试运行程序出现, ["RGV587_ERROR::SM::哎哟喂,被挤爆啦,请稍后重试!"]。请重新登陆。 +- 仅支持[H5端](https://m.daima.cn)可以购买的票。 ## 其他说明 - 如何获取cookie? - 登录[大麦网](https://m.damai.cn/), F12打开控制台查看网络请求, 复制请求中的cookie。 + 1. 登录[大麦网](https://m.damai.cn/), F12打开控制台查看网络请求, 复制请求中的cookie。 ![img.png](images/cookie.png) + 2. 使用扫码登录: `docker exec -it dm-ticket dm-login` - 如何获取演唱会id? @@ -59,8 +79,9 @@ ## TODO -- [ ] 扫码登录 -- [ ] ... +- [x] 扫码登录 +- [ ] 捡漏功能 +- [ ] 其他... ## 赞赏 diff --git a/config/config.yaml b/config/config.yaml.example similarity index 53% rename from config/config.yaml rename to config/config.yaml.example index 5626f42c..e6bdaef8 100644 --- a/config/config.yaml +++ b/config/config.yaml.example @@ -1,23 +1,30 @@ accounts: - # .damai.cn中的cookie字符串 - - cookie: "cna=XD3FHNoADAMCAdzxKqCz0c5s; xxx=ssss;xxx=ssss;" + # .damai.cn中的cookie完整字符串 + - cookie: "" remark: 账号1 # 抢票轮训间隔, 单位毫秒 interval: 30 # 最早提前多少毫秒发包。 开抢剩余时间戳 = 开始售票的时间戳 - 当前时间戳。 当开抢剩余时间戳时间小于 submit_time时, 开始发送数据包。 - earliest_submit_time: 15 + earliest_submit_time: 5 + + # 请求时间 时间戳毫秒 用于捡漏 . 例如 50分是开票时间 但没有抢到,那么这里可以设置55分再抢一次,可能抢到二次放出的票 + # 1685415600100 + request_time: -1 + + # 重试次数 + retry_times: 2 + + # 重试间隔 单位毫秒 + retry_interval: 100 ticket: # 需要抢购的门票ID, 门票详情页URL中的itemId.如:https://m.damai.cn/damai/detail/item.html?utm=&itemId=710947802955 - id: "719062769469" + id: "721069251830" # 需要抢购的门票数量 - num: 2 + num: 1 # 需要抢购的场次序号. - sessions: 2 + sessions: 1 # 需要抢购的票档序号 - grade: 2 - - + grade: 1 - \ No newline at end of file diff --git a/src/bin/login.rs b/src/bin/login.rs new file mode 100644 index 00000000..6277ea83 --- /dev/null +++ b/src/bin/login.rs @@ -0,0 +1,29 @@ +use std::env; + +use anyhow::Result; +use dm_ticket::login::DmLogin; +use dotenv::dotenv; + +#[tokio::main] +async fn main() -> Result<()> { + dotenv().ok(); + + if env::var("RUST_LOG").is_err() { + env::set_var("RUST_LOG", "INFO"); + } + + if env::var("TOKEN_SERVER_URL").is_err() { + env::set_var("TOKEN_SERVER_URL", "http://127.0.0.1:8080/"); + } + + if env::var("QRCODE_PATH").is_err() { + env::set_var("QRCODE_PATH", "./qrcode.png"); + } + + pretty_env_logger::init(); + + let app = DmLogin::new().await?; + app.run().await?; + + Ok(()) +} diff --git a/src/main.rs b/src/bin/ticket.rs similarity index 85% rename from src/main.rs rename to src/bin/ticket.rs index 8185b9bb..85f68122 100644 --- a/src/main.rs +++ b/src/bin/ticket.rs @@ -1,14 +1,16 @@ use anyhow::Result; use dm_ticket::{ config::{load_global_config, Config}, - dm, + ticket, }; +use dotenv::dotenv; use futures::future::join_all; - use std::env; #[tokio::main] async fn main() -> Result<()> { + dotenv().ok(); + if env::var("RUST_LOG").is_err() { env::set_var("RUST_LOG", "INFO"); } @@ -22,10 +24,11 @@ async fn main() -> Result<()> { let config: Config = load_global_config().unwrap(); let mut handlers = Vec::new(); + for account in config.accounts.iter() { let account = account.clone(); let handler = tokio::spawn(async move { - let dm_ticket = dm::DmTicket::new(account).await.unwrap(); + let dm_ticket = ticket::DmTicket::new(account).await.unwrap(); dm_ticket.run().await.unwrap(); }); handlers.push(handler); diff --git a/src/client.rs b/src/clients/dm.rs similarity index 65% rename from src/client.rs rename to src/clients/dm.rs index fad2fa49..c0384f4f 100644 --- a/src/client.rs +++ b/src/clients/dm.rs @@ -1,85 +1,14 @@ -use std::{env, time::Instant}; - +use crate::models::{ticket::TicketInfoParams, DmRes, DmToken}; use anyhow::Result; -use log::{debug, warn}; + +use super::token::TokenClient; use reqwest::{ header::{HeaderMap, HeaderValue}, Client, }; use serde_json::{json, Value}; -use crate::models::{ticket::TicketInfoParams, DmRes, DmToken}; - -const SUCCESS_CODE: u64 = 200; -const SYSTEM_ERROR_CODE: u16 = 500; - -pub struct TokenClient { - pub client: Client, -} - -impl TokenClient { - pub fn new() -> Result { - let client = reqwest::Client::builder().build()?; - Ok(Self { client }) - } - - // Get value from api. - pub async fn get_value(&self, key: &str) -> Result { - let url = env::var("TOKEN_SERVER_URL").unwrap(); - - let params = json!({ - "key": key, - }); - - let data = self - .client - .get(url) - .query(¶ms) - .send() - .await? - .json::() - .await?; - - let code = data - .get("code") - .unwrap_or(&SYSTEM_ERROR_CODE.into()) - .as_u64() - .unwrap(); - - Ok(match code { - SUCCESS_CODE => { - let value = data["data"]["value"].as_str().unwrap().to_string(); - debug!("Get {}:{}", key, value); - value - } - _ => { - warn!("Fail to get {}.", key); - "".to_string() - } - }) - } - - // Get bx ua. - pub async fn get_bx_ua(&self) -> Result { - let start = Instant::now(); - let bx_ua = self.get_value("bx_ua").await?; - debug!("获取bx_ua: {:?}, 花费时间:{:?}", bx_ua, start.elapsed()); - Ok(bx_ua) - } - - /// Get bx token. - pub async fn get_bx_token(&self) -> Result { - let start = Instant::now(); - let bx_token = self.get_value("bx_token").await?; - debug!( - "获取bx_token: {:?}, 花费时间:{:?}", - bx_token, - start.elapsed() - ); - Ok(bx_token) - } -} - +#[derive(Debug)] pub struct DmClient { pub client: Client, pub token_client: TokenClient, @@ -87,6 +16,7 @@ pub struct DmClient { pub bx_token: String, } +// 获取token pub async fn get_token(cookie: &str) -> Result { let mut headers = HeaderMap::new(); let url = "https://mtop.damai.cn/"; @@ -123,6 +53,7 @@ pub async fn get_token(cookie: &str) -> Result { } impl DmClient { + // 初始化请求客户端 pub async fn new(cookie: String) -> Result { let token_client = TokenClient::new()?; @@ -161,6 +92,7 @@ impl DmClient { }) } + // 请求API pub async fn request(&self, url: &str, mut params: Value, data: Value) -> Result { let s = format!( "{}&{}&{}&{}", @@ -179,8 +111,6 @@ impl DmClient { let form = json!({ "data": serde_json::to_string(&data)?, - // "bx-umidtoken": params["bx-umidtoken"], - // "bx-ua": params["bx-ua"] }); let response = self diff --git a/src/clients/login.rs b/src/clients/login.rs new file mode 100644 index 00000000..d4cc8681 --- /dev/null +++ b/src/clients/login.rs @@ -0,0 +1,167 @@ +use std::env; + +use crate::clients::token::TokenClient; +use crate::models::qrcode::{ + QrCodeLoginGetResForm, QrCodeLoginGetResParams, QrCodeLoginStatusData, QrcodeContentGetParams, + QrcodeData, +}; +use crate::models::DmLoginRes; +use anyhow::Result; +use fast_qr::{QRBuilder, QRCode}; + +use reqwest::{ + header::{HeaderMap, HeaderValue}, + Client, +}; +use serde_json::{json, Value}; +use tokio::{fs, io::AsyncWriteExt}; +use urlencoding; + +#[derive(Debug)] +pub struct LoginClient { + pub token_client: TokenClient, + pub client: Client, + pub bx_token: String, +} + +impl LoginClient { + pub async fn new() -> Result { + let token_client = TokenClient::new()?; + let bx_token = token_client.get_bx_token().await?; + + let mut headers = HeaderMap::new(); + headers.append("user-agent", HeaderValue::from_str("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36")?); + headers.append("referer", HeaderValue::from_str("https://ipassport.damai.cn/mini_login.htm?lang=zh_cn&appName=damai&appEntrance=default&styleType=vertical&bizParams=¬LoadSsoView=true¬KeepLogin=false&isMobile=false&showSnsLogin=false®Url=https%3A%2F%2Fpassport.damai.cn%2Fregister&plainReturnUrl=https%3A%2F%2Fpassport.damai.cn%2Flogin&returnUrl=https%3A%2F%2Fpassport.damai.cn%2Fdologin.htm%3FredirectUrl%3Dhttps%25253A%25252F%25252Fwww.damai.cn%25252F%26platform%3D106002&rnd=0.6260742856882737")?); + headers.append( + "content-type", + HeaderValue::from_str("application/json;charset=UTF-8")?, + ); + let client = reqwest::Client::builder() + .default_headers(headers) + .cookie_store(true) + .http2_prior_knowledge() + .user_agent("Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3") + .use_rustls_tls() + .build()?; + Ok(Self { + token_client, + client, + bx_token, + }) + } + + pub async fn request(&self, url: &str, mut params: Value, data: Value) -> Result { + params["bx-umidtoken"] = self.bx_token.clone().into(); + params["bx-ua"] = self.token_client.get_bx_ua().await?.into(); + + let response = self + .client + .post(url) + .query(¶ms) + .form(&data) + .send() + .await?; + + let data = response.json::().await?; + + Ok(data) + } + + // 生成二维码 + pub async fn generate_qrcode(&self) -> Result { + let url = "https://ipassport.damai.cn/newlogin/qrcode/generate.do"; + let res = self + .request(url, QrcodeContentGetParams::build()?, json!({})) + .await?; + let data = serde_json::from_value(res.content.data)?; + Ok(data) + } + + // 获取二维码 + pub async fn get_qrcode(&self, qrcode_content: String) -> Result { + let qrcode_path = env::var("QRCODE_PATH").unwrap(); + let url = format!( + "https://gcodex.alicdn.com/qrcode.do?biz_code=havana&size=140&content={}", + urlencoding::encode(&qrcode_content) + ); + let mut source = self.client.get(&url).send().await?; + + let mut dest = fs::OpenOptions::new() + .create(true) + .write(true) + .open(&qrcode_path) + .await?; + + while let Some(chunk) = source.chunk().await? { + dest.write_all(&chunk).await?; + } + + let img = image::open(&qrcode_path)?.to_luma8(); + + let mut img = rqrr::PreparedImage::prepare(img); + + let grids = img.detect_grids(); + let (_, content) = grids[0].decode()?; + + let qrcode = QRBuilder::new(content).build().unwrap(); + + let _ = fs::remove_file(qrcode_path).await; + + Ok(qrcode) + } + + // 获取登录结果 + pub async fn get_login_result(&self, t: u64, ck: String) -> Result { + let url = "https://ipassport.damai.cn/newlogin/qrcode/query.do"; + let params = QrCodeLoginGetResParams::build()?; + let form_data = QrCodeLoginGetResForm::build(t, ck)?; + let res = self.request(url, params, form_data).await?; + let data = serde_json::from_value(res.content.data)?; + Ok(data) + } + + // 获取cookie + pub async fn get_cookie( + &self, + cookie2: String, + return_url: String, + st: String, + ) -> Result { + let mut headers = HeaderMap::new(); + headers.append("user-agent", HeaderValue::from_str("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36")?); + headers.append( + "referer", + HeaderValue::from_str( + "https://passport.damai.cn/login?ru=https%3A%2F%2Fwww.damai.cn%2F", + )?, + ); + headers.append( + "cookie", + HeaderValue::from_str(&format!("cookie2={};", cookie2))?, + ); + let url = format!("{}&st={}", return_url, st); + + let client = reqwest::Client::builder() + .default_headers(headers) + .cookie_store(true) + .http2_prior_knowledge() + .use_rustls_tls() + .build()?; + + let _ = client.get(url).send().await?; + + let response = client.get("https://passport.damai.cn/accountinfo/myinfo?spm=a2oeg.home.nick.duserinfo.591b23e1MY5Dhp").send().await?; + + let mut cookies = format!("cookie2={};", cookie2); + for (name, value) in response.headers() { + let name = name.to_string(); + let value = value.to_str().unwrap().to_string(); + if name.starts_with("set-cookie") { + let values = value.split(' ').collect::>(); + let cookie = values[0]; + cookies.push_str(cookie); + } + } + Ok(cookies) + } +} diff --git a/src/clients/mod.rs b/src/clients/mod.rs new file mode 100644 index 00000000..915f4b38 --- /dev/null +++ b/src/clients/mod.rs @@ -0,0 +1,3 @@ +pub mod dm; +pub mod login; +pub mod token; diff --git a/src/clients/token.rs b/src/clients/token.rs new file mode 100644 index 00000000..fc8e856e --- /dev/null +++ b/src/clients/token.rs @@ -0,0 +1,81 @@ +use anyhow::Result; +use log::{debug, warn}; +use reqwest::Client; +use serde_json::{json, Value}; +use std::{ + env, + time::{Duration, Instant}, +}; + +const SUCCESS_CODE: u64 = 200; +const SYSTEM_ERROR_CODE: u16 = 500; + +#[derive(Debug)] +pub struct TokenClient { + pub client: Client, +} + +impl TokenClient { + pub fn new() -> Result { + let client = reqwest::Client::builder() + .timeout(Duration::from_secs(3)) + .build()?; + Ok(Self { client }) + } + + // Get value from api. + pub async fn get_value(&self, key: &str) -> Result { + let url = env::var("TOKEN_SERVER_URL").unwrap(); + + let params = json!({ + "key": key, + }); + + let data = self + .client + .get(url) + .query(¶ms) + .send() + .await? + .json::() + .await?; + + let code = data + .get("code") + .unwrap_or(&SYSTEM_ERROR_CODE.into()) + .as_u64() + .unwrap(); + + Ok(match code { + SUCCESS_CODE => { + let value = data["data"]["value"].as_str().unwrap().to_string(); + debug!("Get {}:{}", key, value); + value + } + _ => { + warn!("Fail to get {}.", key); + "".to_string() + } + }) + } + + // Get bx ua. + pub async fn get_bx_ua(&self) -> Result { + let start = Instant::now(); + let bx_ua = self.get_value("bx_ua").await?; + debug!("获取bx_ua: {:?}, 花费时间:{:?}", bx_ua, start.elapsed()); + Ok(bx_ua) + } + + // Get bx token. + pub async fn get_bx_token(&self) -> Result { + let start = Instant::now(); + let bx_token = self.get_value("bx_token").await?; + debug!( + "获取bx_token: {:?}, 花费时间:{:?}", + bx_token, + start.elapsed() + ); + Ok(bx_token) + } +} diff --git a/src/config.rs b/src/config.rs index 174944a2..104ad06f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -17,6 +17,9 @@ pub struct Account { pub ticket: Ticket, pub interval: Option, pub earliest_submit_time: Option, + pub request_time: Option, + pub retry_times: Option, + pub retry_interval: Option, } #[derive(Serialize, Deserialize, Debug)] @@ -31,21 +34,19 @@ where // 1.通过std::fs读取配置文件内容 // 2.通过serde_yaml解析读取到的yaml配置转换成json对象 match serde_yaml::from_str::( - &std::fs::read_to_string(path).unwrap_or_else(|_| panic!("failure read file {}", path)), + &std::fs::read_to_string(path).unwrap_or_else(|_| panic!("读取配置文件失败:{}", path)), ) { Ok(root_schema) => { - // 通过serde_json把json对象转换指定的model let data = serde_json::to_string_pretty(&root_schema).expect("failure to parse RootSchema"); + let config = serde_json::from_str::(&data) - .unwrap_or_else(|_| panic!("failure to format json str {}", &data)); - // 返回格式化结果 + .unwrap_or_else(|_| panic!("配置文件格式错误, 请检查配置: {}", &data)); + Some(config) } Err(err) => { - // 记录日志 error!("{}", err); - // 返回None None } } diff --git a/src/lib.rs b/src/lib.rs index 81b93abc..b199c617 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ -pub mod client; +pub mod clients; pub mod config; -pub mod dm; +pub mod login; pub mod models; +pub mod ticket; diff --git a/src/login.rs b/src/login.rs new file mode 100644 index 00000000..590877af --- /dev/null +++ b/src/login.rs @@ -0,0 +1,90 @@ +use log::{debug, error, info}; +use std::{ + io::{self, Write}, + time::Duration, +}; + +use crate::clients::login::LoginClient; +use anyhow::Result; + +pub struct DmLogin { + client: LoginClient, +} + +impl DmLogin { + pub async fn new() -> Result { + Ok(Self { + client: LoginClient::new().await?, + }) + } + + pub async fn run(&self) -> Result<()> { + info!("正在获取二维码...\n"); + let qrcode_data = match self.client.generate_qrcode().await { + Ok(data) => { + debug!("Get qrcode data:{:?}", data); + data + } + Err(e) => { + error!("Fail to get qrcode data, error:{:?}", e); + return Err(e); + } + }; + + let qrcode = match self.client.get_qrcode(qrcode_data.code_content).await { + Ok(code) => { + debug!("success to get qrcode!"); + code + } + Err(e) => { + error!("Fail to get qrcode, error:{:?}", e); + return Err(e); + } + }; + + println!("{}\n", qrcode.to_str()); + + let t = qrcode_data.t; + let ck = qrcode_data.ck.clone(); + + let max_times = 60 * 5; + + for i in 0..max_times { + let qrcode_scan_status = self.client.get_login_result(t, ck.clone()).await?; + + match qrcode_scan_status.qrcode_status.as_str() { + "NEW" => { + print!("\r请使用大麦APP扫码, 倒计时:{}秒\t", max_times - i); + let _ = io::stdout().flush(); + tokio::time::sleep(Duration::from_secs(1)).await; + } + "SCANED" => { + print!("\r##########请点击确认登录#######\t\t"); + let _ = io::stdout().flush(); + tokio::time::sleep(Duration::from_secs(1)).await; + } + "CONFIRMED" => { + println!("\r\n已确认, 登录成功, cookie如下: "); + let cookie2 = qrcode_scan_status.cookie2.unwrap(); + let return_url = qrcode_scan_status.return_url.unwrap(); + let st = qrcode_scan_status.st.unwrap(); + let cookies = self.client.get_cookie(cookie2, return_url, st).await?; + println!("\n{}\n", cookies); + return Ok(()); + } + "EXPIRED" => { + print!("\r\t二维码已过期, 请重新执行程序..."); + return Ok(()); + } + _ => { + error!("未知状态:{:?}, 退出...", qrcode_scan_status); + return Ok(()); + } + } + } + + info!("二维码已过期, 请重新执行程序..."); + + Ok(()) + } +} diff --git a/src/model.rs b/src/model.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/src/models/mod.rs b/src/models/mod.rs index da5f7282..f5d6ee53 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,10 +1,12 @@ pub mod order; pub mod perform; +pub mod qrcode; pub mod ticket; +pub mod user; use chrono::{DateTime, Local}; use serde::{Deserialize, Serialize}; -use serde_json::value; +use serde_json::{value, Value}; // cookie token. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct DmToken { @@ -22,6 +24,21 @@ pub struct DmRes { pub v: Option, } +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct DmLoginResContent { + pub status: i32, + pub success: bool, + pub data: Value, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct DmLoginRes { + #[serde(rename = "hasError")] + pub has_error: bool, + + pub content: DmLoginResContent, +} + // 通用参数 #[derive(Serialize, Deserialize, Debug)] pub struct CommonParams { diff --git a/src/models/order.rs b/src/models/order.rs index 16d01498..35f4b06f 100644 --- a/src/models/order.rs +++ b/src/models/order.rs @@ -4,7 +4,7 @@ use serde_json::{json, Value}; use super::CommonParams; -pub struct OrderParams {} +pub struct OrderParams; // 生成订单的请求参数 impl OrderParams { @@ -21,7 +21,7 @@ impl OrderParams { } } -pub struct OrderForm {} +pub struct OrderForm; // 生成订单表单参数 impl OrderForm { @@ -47,13 +47,13 @@ impl OrderForm { } #[derive(Serialize, Deserialize, Debug)] -pub struct OrderInfoContainer {} +pub struct OrderInfoContainer; #[derive(Serialize, Deserialize, Debug)] -pub struct OrderInfoData {} +pub struct OrderInfoData; #[derive(Serialize, Deserialize, Debug)] -pub struct OrderInfoEndpoint {} +pub struct OrderInfoEndpoint; #[derive(Serialize, Deserialize, Debug)] pub struct OrderInfoGlobal { @@ -109,7 +109,7 @@ pub struct OrderInfo { pub linkage: OrderInfoLinkage, } -pub struct SubmitOrderParams {} +pub struct SubmitOrderParams; impl SubmitOrderParams { pub fn build(submitref: String) -> Result { diff --git a/src/models/perform.rs b/src/models/perform.rs index 2faa364c..391f1c2d 100644 --- a/src/models/perform.rs +++ b/src/models/perform.rs @@ -4,7 +4,7 @@ use serde_json::{json, Value}; use super::CommonParams; -pub struct PerformParams {} +pub struct PerformParams; impl PerformParams { pub fn build() -> Result { @@ -16,7 +16,7 @@ impl PerformParams { } } -pub struct PerformForm {} +pub struct PerformForm; impl PerformForm { pub fn build(ticket_id: String, perform_id: String) -> Result { let ex_params = json!({ diff --git a/src/models/qrcode.rs b/src/models/qrcode.rs new file mode 100644 index 00000000..c14efd83 --- /dev/null +++ b/src/models/qrcode.rs @@ -0,0 +1,116 @@ +use anyhow::Result; +use rand::distributions::Alphanumeric; +use rand::{thread_rng, Rng}; +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; +pub struct QrcodeContentGetParams; + +impl QrcodeContentGetParams { + pub fn build() -> Result { + let mut rng = thread_rng(); + let csrf_token: String = (0..21).map(|_| rng.sample(Alphanumeric) as char).collect(); + let umid_token: String = (0..40).map(|_| rng.sample(Alphanumeric) as char).collect(); + let hsiz: String = (0..10).map(|_| rng.sample(Alphanumeric) as char).collect(); + let hsiz = format!("{:?}", md5::compute(hsiz)).to_ascii_lowercase(); + Ok(json!({ + "appName": "damai", + "fromSite": "18", + "appEntrance": "damai", + "_csrf_token": csrf_token, + "umidToken": umid_token.to_lowercase(), + "isMobile": "false", + "lang": "zh_CN", + "returnUrl": "https://passport.damai.cn/dologin.htm?redirectUrl=https%253A%252F%252Fwww.damai.cn%252F&platform=106002", + "hsiz": hsiz, + "bizParams": "", + "umidTag": "SERVER", + "_bx-v": "2.5.0", + })) + } +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct QrcodeData { + pub t: u64, + + #[serde(rename = "codeContent")] + pub code_content: String, + + pub ck: String, + + #[serde(rename = "resultCode")] + pub result_code: u32, +} + +pub struct QrCodeLoginGetResParams; + +impl QrCodeLoginGetResParams { + pub fn build() -> Result { + Ok(json!({ + "appName": "damai", + "fromSite": "18", + "_bx-v": "2.5.0", + })) + } +} + +pub struct QrCodeLoginGetResForm; + +impl QrCodeLoginGetResForm { + pub fn build(t: u64, ck: String) -> Result { + let mut rng = thread_rng(); + let csrf_token: String = (0..21).map(|_| rng.sample(Alphanumeric) as char).collect(); + let umid_token: String = (0..40).map(|_| rng.sample(Alphanumeric) as char).collect(); + let hsiz: String = (0..10).map(|_| rng.sample(Alphanumeric) as char).collect(); + let hsiz = format!("{:?}", md5::compute(hsiz)).to_ascii_lowercase(); + let page_trace_id: String = (0..40).map(|_| rng.sample(Alphanumeric) as char).collect(); + let device_id: String = (0..24).map(|_| rng.sample(Alphanumeric) as char).collect(); + let ua = "140#5kTduXsozzW+Aewfweo23zaT4pN8s77o4RXRIbLoFyw9NRhkRYvtS/4OctmocZ9Cs0AEwJMDXuJqlbzxh31jNxOBzFcbs2c9lpTzzPzbVXlqlbnzrk82tQQLl6bydCi6u2sd1mMy/4hjXObdYFq23fAzl7MydD174LFdlKuvn6CXDpMnORKHtDXi1qXv4bMH4B2nQbfvr1VXwhQn+EcHcldUOuF0afRzPRuc8Ls0j1oRLR+c+ZcmWvp6UTQshAdtx/X+JG1sr/WW+KO+cY8xWSexILbspEOUrmz+QETswRlPCsS3dXjzcP1VT/1pnUySn4X3OHQpF3mPBGb3Viqz40AOlaMprMGxF2b4hOuJ3+CFogb477wDwWfYg2TJzZbCHB+4yZMJu3mFxYS4+Nko+Qsd96ONvCxk+3FKIBsNFulZFGrKyFIon0i0NjzNsP+pmKS2hksOnsmCDg+2+9YdKEbQSOzOVb+m3Hr21FOOu+lCqa+2JWIdqZbZvpz9xe3jrKfL7Ls94towPOFLrHNrwf3H1h+9iWfT/3SLVqz7W2WfAtfmbRccfIusEku71bMcXKMmNb27d2gfwmQmsWKcFntTO+faySt3F91He9+aognA/NFH2PPKV66ezzrb2Xe8r0wAONdOHaU3/0KBb7uu0oGGEUQ4zHWq9Rafxi2W+aD4KJeTMTAnykbFd/81MJFh0z38CZG72DC5Ml59C9zoi6cE3NcmpNxg+nOOP/bGCERdFbtsZLGADUDiHU690c5qzv6LgbqAV6f6Q2I+P+ArfqWu20V9dLAMz1nrHQ8efMQLRxvQBIxf3zB0fbc8IQ1gK/hXjFfF22u0NzJtpEk9gL9tbUhZ4T6ePh2GGcndSKoYQBbp2vAXiA78ju1qWYArzkhBWpEWQ+xMabDg3Wpbq6s3lhFYcdnrPUj122nb6pTZwqqm0Zh37dLAzxhHOpiVDcsTPlyqoMTOGvFMeNmqmUKVp1v8Vyqrronfoq/l+IBbU1Yg6I0hPQbDMyhdBdwrtPlySsWHU79k22k054q8JEY5iv5Ajv348XNE0osS5ATxeq1YgqkGlJq1371U2RMRPh9kYqTGloVab7fXo1tbC12/zI9A+3vYuZk6f34418v9/jklfYRCGsgeGi2ElfiDkGF3frEM1JghUU7VHU991TJCKNFUhPPX2MhOTjSiDRyaJ5HsFlb8zSK1deU1SPmR/HO57dzCZ+8txKpSRbJvpCdKmHM4duGN0gaWdKX8m7cxRtjitACkvrnyjzp4EOIXHO+wIkz5UuAKgLpQ7dtmSIHCHFNQ9LGS5xqcVzm1BcdZLeCzJjS4EZofyRTjbYq7BbJlqFAai93mxTtAbXpztIlxM8BYCe17Uz7rCZxRJwvVtzqEKuS9GRn9fCyDYsoSUtQUb1xevim+AQOPc5DO14+sQosqpLB65XELHQ/H4RXOTMwi23CUoivNXP2KkzVbqLunhbGJuzZDHB9RTKsMRNIfSnSQBzHQ8fnyky8LhjgkLAHG65X4cIbBKZVFR0jQVZLK80gpqvlmE+biVOPezteVmcqheeASnr0bjfJnN7jddaAXzmDirIU1+O0vb0wxmVFmqWAlpYOStgR2CZguHdGGrYCdsAQLDgiPulnLZPufSeR3GNldmnqZHli+FBloV4fEDIIHMlA9Ykgsc2UNZ0iBRiVxZoJDQHR1dnutQpO3XWh8aWwcSK0aepay6MZc8QshNbDDSU9fWXXG7t9+cpqNEmdtNH8dNw9MJP0QLR23G+M7MVQSc8LE0m9JT2CCrI6/6pU3gfIfx+GLzkcnNN2OuBaYjoysWAiea62wxlgHQ1xMQdEo8ZY/x/xTZ7HR"; + let form = json!({ + "t": t.to_string(), + "ck": ck.as_str(), + "ua": ua, + "appName": "damai", + "appEntrance": "damai", + "fromSite": "18", + "_csrf_token": csrf_token, + "umidToken": umid_token.to_lowercase(), + "isMobile": "false", + "lang": "zh_CN", + "returnUrl": "https://passport.damai.cn/dologin.htm?redirectUrl=https%253A%252F%252Fwww.damai.cn%252F&platform=106002", + "hsiz": hsiz, + "bizParams": "", + "umidTag": "SERVER", + "navlanguage": "zh-CN", + "navUserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36", + "navPlatform": "MacIntel", + "deviceId": device_id, + "pageTraceId": page_trace_id.to_lowercase(), + }); + Ok(form) + } +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct QrCodeLoginStatusData { + #[serde(rename = "resultCode")] + pub result_code: u32, + + #[serde(rename = "qrCodeStatus")] + pub qrcode_status: String, + + pub st: Option, + + #[serde(rename = "loginType")] + pub login_type: Option, + + #[serde(rename = "loginScene")] + pub login_scene: Option, + + pub sid: Option, + + pub cookie2: Option, + + #[serde(rename = "returnUrl")] + pub return_url: Option, +} diff --git a/src/models/ticket.rs b/src/models/ticket.rs index 07f75111..825cf316 100644 --- a/src/models/ticket.rs +++ b/src/models/ticket.rs @@ -19,7 +19,7 @@ impl TicketInfoForm { item_id: ticket_id, dm_channel: "damai@damaih5_h5", }; - Ok(serde_json::to_value(&data)?) + Ok(serde_json::to_value(data)?) } } diff --git a/src/models/user.rs b/src/models/user.rs new file mode 100644 index 00000000..7f0926de --- /dev/null +++ b/src/models/user.rs @@ -0,0 +1,32 @@ +use anyhow::Result; +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; + +use super::CommonParams; + +pub struct GetUserInfoParams; + +impl GetUserInfoParams { + pub fn build() -> Result { + let mut params = serde_json::to_value(CommonParams::build())?; + params["api"] = "mtop.damai.wireless.user.session.transform".into(); + params["v"] = "1.0".into(); + Ok(params) + } +} + +pub struct GetUserInfoForm; + +impl GetUserInfoForm { + pub fn build() -> Result { + Ok(json!({"source":"h5","dmChannel":"damai@damaih5_h5"})) + } +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct UserInfoData { + pub nickname: String, + + #[serde(rename = "userId")] + pub user_id: u64, +} diff --git a/src/dm.rs b/src/ticket.rs similarity index 84% rename from src/dm.rs rename to src/ticket.rs index eac34ac6..8a0b38fe 100644 --- a/src/dm.rs +++ b/src/ticket.rs @@ -4,12 +4,13 @@ use std::{ }; use crate::{ - client::DmClient, + clients::dm::DmClient, config::Account, models::{ order::{OrderForm, OrderInfo, OrderParams, SubmitOrderParams}, perform::{PerformForm, PerformInfo, PerformParams}, ticket::{TicketInfo, TicketInfoForm, TicketInfoParams}, + user::{GetUserInfoForm, GetUserInfoParams, UserInfoData}, DmRes, }, }; @@ -42,6 +43,20 @@ impl DmTicket { Ok(Self { client, account }) } + // 获取用户信息 + pub async fn get_user_info(&self) -> Result { + let url = "https://mtop.damai.cn/h5/mtop.damai.wireless.user.session.transform/1.0/"; + let params = GetUserInfoParams::build()?; + let form = GetUserInfoForm::build()?; + let res = self.client.request(url, params, form).await?; + if res.ret.contains(&SUCCESS_FLAG.to_string()) { + let user_info_data = serde_json::from_value(res.data)?; + Ok(user_info_data) + } else { + Err(anyhow!("{}", res.ret[0])) + } + } + // 获取门票信息 pub async fn get_ticket_info(&self, ticket_id: String) -> Result { let url = "https://mtop.damai.cn/h5/mtop.alibaba.damai.detail.getdetail/1.2"; @@ -176,6 +191,7 @@ impl DmTicket { Ok(res) } + // 获取场次/票档信息 pub async fn get_perform_info( &self, ticket_id: String, @@ -198,6 +214,7 @@ impl DmTicket { Ok(perform_info) } + // 购买流程 pub async fn buy(&self, item_id: &String, sku_id: &String) -> Result { let start = Instant::now(); @@ -233,6 +250,7 @@ impl DmTicket { } } + // 毫秒转时分秒 pub fn ms_to_hms(&self, ms: i64) -> (u64, u64, f64) { let sec = ms as f64 / 1000.0; let hour = (sec / 3600.0) as u64; @@ -242,7 +260,20 @@ impl DmTicket { (hour, min, sec) } + // 程序入口 pub async fn run(&self) -> Result<()> { + let user_info = match self.get_user_info().await { + Ok(info) => info, + Err(e) => { + if e.to_string().contains("FAIL_SYS_SESSION_EXPIRED::Session") { + error!("获取用户信息失败, cookie已过期, 请重新登陆!"); + } else { + error!("获取用户信息失败, 原因:{:?}", e); + } + + return Ok(()); + } + }; let ticket_id = self.account.ticket.id.clone(); let perfomr_idx = self.account.ticket.sessions - 1; // 场次索引 let sku_idx = self.account.ticket.grade - 1; // 票档索引 @@ -286,16 +317,25 @@ impl DmTicket { .item .item .sell_start_time_str; - let start_timestamp = ticket_info + let mut start_timestamp = ticket_info .detail_view_component_map .item .item .sell_start_timestamp .parse::()?; + let request_time = self.account.request_time.unwrap_or(-1); + + let retry_times = self.account.retry_times.unwrap_or(2); + let retry_interval = self.account.retry_interval.unwrap_or(100); + + if request_time > 0 { + start_timestamp = request_time + } + println!( - "\r\n\t账号备注:{}\n\t门票名称:{}\n\t场次名称:{}\n\t票档名称:{}\n\t开抢时间:{}\n", - self.account.remark, ticket_name, perform_name, sku_name, start_time_str + "\r\n\t账号备注: {}\n\t账号昵称: {}\n\t门票名称: {}\n\t场次名称: {}\n\t票档名称: {}\n\t开抢时间: {}\n", + self.account.remark, user_info.nickname, ticket_name, perform_name, sku_name, start_time_str ); let (s, r) = async_channel::unbounded::(); @@ -303,6 +343,7 @@ impl DmTicket { let interval = self.account.interval.unwrap_or(50); let earliest_submit_time = self.account.earliest_submit_time.unwrap_or(1); + // 轮询等待开抢 loop { tokio::select! { _ = signal::ctrl_c() => { @@ -322,16 +363,20 @@ impl DmTicket { let _ =io::stdout().flush(); } - // info!("剩余时间毫秒:{}", time_left_millis); } _ = r.recv() => { - for _ in 0..2 { + + // 多次重试 + for _ in 0..retry_times { if let Ok(res) = self.buy(&item_id, &sku_id).await { if res {// 抢购成功, 退出 return Ok(()); } } + + // 重试间隔 + tokio::time::sleep(Duration::from_millis(retry_interval)).await; } return Ok(()); }