diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..2322c32 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,32 @@ +# Include any files or directories that you don't want to be copied to your +# container here (e.g., local build artifacts, temporary files, etc.). +# +# For more help, visit the .dockerignore file reference guide at +# https://docs.docker.com/engine/reference/builder/#dockerignore-file + +**/.DS_Store +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/charts +**/docker-compose* +**/compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/secrets.dev.yaml +**/values.dev.yaml +/bin +/target +LICENSE +README.md \ No newline at end of file diff --git a/.env.example b/.env.example index 88f5e1d..c890035 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,3 @@ -TOKEN= -HOST= -MODE= -EDITOR= +TELOXIDE_TOKEN= +GITHUB_TOKEN= +WEBHOOK_URL= \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a206208..cda78f4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,24 +1,22 @@ name: Test CI -on: [push, pull_request] +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] -jobs: - lint: - runs-on: ubuntu-latest - steps: - - name: Clone repository - uses: actions/checkout@v2 - with: - submodules: false - persist-credentials: false +env: + CARGO_TERM_COLOR: always - - name: Set up Deno - uses: denoland/setup-deno@v1 - with: - deno-version: vx.x.x +jobs: + build: - - name: Format - run: deno fmt + runs-on: ubuntu-latest - - name: Lint - run: deno lint + steps: + - uses: actions/checkout@v3 + - name: Build + run: cargo build --verbose + # - name: Run tests + # run: cargo test --verbose diff --git a/.gitignore b/.gitignore index 0cfe994..1a24e62 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .env .idea .DS_Store +target/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..352a626 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "rust-analyzer.linkedProjects": [ + "./Cargo.toml" + ] +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c2f4229 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2270 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +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 = "aquamarine" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a941c39708478e8eea39243b5983f1c42d2717b3620ee91f4a52115fd02ac43f" +dependencies = [ + "itertools", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "arc-swap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" + +[[package]] +name = "async-trait" +version = "0.1.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[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.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "crates_io_api" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0346712abc6ffc5b287637815c838f6126ff62df37f12806fa029e66c0ec285c" +dependencies = [ + "chrono", + "futures", + "reqwest", + "serde", + "serde_derive", + "serde_json", + "serde_path_to_error", + "tokio", + "url", +] + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "deranged" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "dptree" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d81175dab5ec79c30e0576df2ed2c244e1721720c302000bb321b107e82e265c" +dependencies = [ + "futures", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_logger" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "erasable" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f11890ce181d47a64e5d1eb4b6caba0e7bae911a356723740d058a5d0340b7d" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "errno" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" + +[[package]] +name = "futures-executor" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" + +[[package]] +name = "futures-macro" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "futures-sink" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" + +[[package]] +name = "futures-task" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" + +[[package]] +name = "futures-util" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + +[[package]] +name = "h2" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +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 = "http-range-header" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.4.10", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http", + "hyper", + "log", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[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 = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "iri-string" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21859b667d66a4c1dacd9df0863b3efb65785474255face87f5bca39dd8407c0" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys", +] + +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "js-sys" +version = "0.3.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonwebtoken" +version = "9.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "155c4d7e39ad04c172c5e3a99c434ea3b4a7ba7960b38ecd562b270b097cce09" +dependencies = [ + "base64", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "linux-raw-sys" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[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.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "never" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91" + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[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-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "octocrab" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abfeeafb5fa0da7046229ec3c7b3bd2981aae05c549871192c408d59fc0fffd5" +dependencies = [ + "arc-swap", + "async-trait", + "base64", + "bytes", + "cfg-if", + "chrono", + "either", + "futures", + "futures-util", + "http", + "http-body", + "hyper", + "hyper-rustls", + "hyper-timeout", + "jsonwebtoken", + "once_cell", + "percent-encoding", + "pin-project", + "secrecy", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "snafu", + "tokio", + "tower", + "tower-http 0.4.4", + "tracing", + "url", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "openssl" +version = "0.10.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a257ad03cd8fb16ad4172fedf8094451e1af1c4b70097636ef2eac9a5f0cc33" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "pem" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3163d2912b7c3b52d651a055f2c7eec9ba5cd22d26ef75b8dd3a59980b185923" +dependencies = [ + "base64", + "serde", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[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.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c" +dependencies = [ + "env_logger", + "log", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[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", +] + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +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 = "rc-box" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0690759eabf094030c2cdabc25ade1395bac02210d920d655053c1d49583fd8" +dependencies = [ + "erasable", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "reqwest" +version = "0.11.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "winreg", +] + +[[package]] +name = "ring" +version = "0.17.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys", +] + +[[package]] +name = "rustacean" +version = "0.1.0" +dependencies = [ + "crates_io_api", + "log", + "octocrab", + "pretty_env_logger", + "reqwest", + "serde", + "serde_json", + "teloxide", + "tokio", + "url", + "uuid", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ad981d6c340a49cdc40a1028d9c6084ec7e9fa33fcb839cab656a267071e234" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustls" +version = "0.21.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" + +[[package]] +name = "serde" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +dependencies = [ + "itoa", + "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_with_macros" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[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 = "simple_asn1" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror", + "time", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "snafu" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" +dependencies = [ + "backtrace", + "doc-comment", + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[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.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "take_mut" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" + +[[package]] +name = "takecell" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20f34339676cdcab560c9a82300c4c2581f68b9369aedf0fae86f2ff9565ff3e" + +[[package]] +name = "teloxide" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c63345cf32a8850ebddcdd769dc2d5193d5e231262d5dada264b79da01a664da" +dependencies = [ + "aquamarine", + "axum", + "bytes", + "derive_more", + "dptree", + "futures", + "log", + "mime", + "pin-project", + "rand", + "serde", + "serde_json", + "serde_with_macros", + "teloxide-core", + "teloxide-macros", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tower-http 0.3.5", + "url", +] + +[[package]] +name = "teloxide-core" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "303db260110c238e3af77bb9dff18bf7a5b5196f783059b0852aab75f91d5a16" +dependencies = [ + "bitflags 1.3.2", + "bytes", + "chrono", + "derive_more", + "either", + "futures", + "log", + "mime", + "never", + "once_cell", + "pin-project", + "rc-box", + "reqwest", + "serde", + "serde_json", + "serde_with_macros", + "take_mut", + "takecell", + "thiserror", + "tokio", + "tokio-util", + "url", + "uuid", +] + +[[package]] +name = "teloxide-macros" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f1d653b093dba5e44cada57a516f572167df37b8a619443e59c8c517bb6d804" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tempfile" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys", +] + +[[package]] +name = "termcolor" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "time" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +dependencies = [ + "deranged", + "itoa", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +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.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.5.5", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" +dependencies = [ + "bitflags 1.3.2", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" +dependencies = [ + "bitflags 2.4.1", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[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.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +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.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +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.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "uuid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" +dependencies = [ + "getrandom", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[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.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.39", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" + +[[package]] +name = "wasm-streams" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +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-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d5b8ce2 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "rustacean" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +teloxide = { version = "0.12", features = ["macros", "webhooks", "webhooks-axum"] } +log = "0.4" +pretty_env_logger = "0.5.0" +tokio = { version = "1.8", features = ["rt-multi-thread", "macros"] } +url = "2.4.1" +crates_io_api = "0.8.2" +uuid = { version = "1.5.0", features = ["v4"] } +serde_json = "1.0.108" +serde = { version = "1.0.192", features = ["derive"] } +octocrab = { version = "0.32.0" } +reqwest = { version = "0.11.22", features = ["blocking", "json"] } + + +[profile.release] +strip = true +opt-level = "z" +lto = true +codegen-units = 1 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1087541 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,45 @@ +ARG RUST_VERSION=1.74.0 +ARG APP_NAME=rustacean + +FROM rust:${RUST_VERSION}-slim-bullseye AS build +ARG APP_NAME +WORKDIR /app + +RUN --mount=type=bind,source=src,target=src \ + --mount=type=bind,source=Cargo.toml,target=Cargo.toml \ + --mount=type=bind,source=Cargo.lock,target=Cargo.lock \ + --mount=type=bind,source=data,target=data \ + --mount=type=cache,target=/app/target/ \ + --mount=type=cache,target=/usr/local/cargo/registry/ \ + < { - await console.log(blue("[INFO]"), `bot is starting on ${env["HOST"]}`); - await delta(bot); - await bot.catch((error) => { - console.log(error, error.ctx.api); - }); -}; - -const webhook = async () => { - await console.log(blue("[INFO]"), `bot is starting on ${env["HOST"]}`); - await serve(async (req) => { - const url = new URL(req.url); - - if (req.method == "POST") { - switch (url.pathname) { - case "/bot": - try { - return await handle(req); - } catch (err) { - console.error(err); - return new Response("Nope, not working..."); - } - default: - return new Response("What you're trying post?"); - } - } - - switch (url.pathname) { - case "/webhook": - try { - await bot.api.setWebhook(`https://${url.hostname}/bot`); - return new Response("Done. Set"); - } catch (_) { - return new Response("Couldn't succeed with installing webhook"); - } - default: - return Response.redirect("https://t.me/rustaceanbot", 302); - } - }); -}; - -const polling = async () => { - await bot.start(); -}; - -export const launch = async () => { - switch (env["HOST"]) { - case "WEBHOOK": - await initializer(); - await webhook(); - break; - case "POLLING": - await initializer(); - await polling(); - break; - default: - throw new Error("Deploy method not validated!"); - } -}; - -await launch(); diff --git a/communities.json b/data/communities.json similarity index 100% rename from communities.json rename to data/communities.json diff --git a/source.json b/data/source.json similarity index 82% rename from source.json rename to data/source.json index 94bfb33..c448424 100644 --- a/source.json +++ b/data/source.json @@ -1,5 +1,5 @@ { - "tutorial": [ + "guides": [ { "name": "Rust 101", "desc": "Just a basic, hands-on course", @@ -12,8 +12,8 @@ }, { "name": "Rust Cookbook", - "desc": "https://rust-lang-nursery.github.io/rust-cookbook/", - "link": "Learn Rust good practices with practical code samples" + "desc": "Learn Rust good practices with practical code samples", + "link": "https://rust-lang-nursery.github.io/rust-cookbook/" }, { "name": "CIS 198", @@ -24,9 +24,19 @@ "name": "tourofrust", "desc": "A step by step guide through the features of the Rust programming language", "link": "https://tourofrust.com/" + }, + { + "name": "Rust Design Patterns", + "desc": "Learning how to implement design patterns on Rust programming language", + "link": "https://rust-unofficial.github.io/patterns/" } ], "podcasts": [ + { + "name": "The Rustacean Station Podcast", + "desc": "Come journey with us into the weird, wonderful, and wily world of Rust", + "link": "https://rustacean-station.org/" + }, { "name": "New Rustacean", "desc": "For beginner Rustaceans", diff --git a/delta/about.ts b/delta/about.ts deleted file mode 100644 index e9b2930..0000000 --- a/delta/about.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Composer, Context, InlineKeyboard } from "../deps.ts"; - -const composer = new Composer(); - -export const message = - `Hello fellow (rustacean|rustina|evangelist)! \n` + - `\n` + - `This is a telegram bot created by a rustacean to help ` + - `people interact with various Rust API way more conveniently.`; - -export const keyboard = new InlineKeyboard().url( - `Source Code`, - `https://github.com/rust-lang-uz/telegram`, -).url( - `Author`, - `https://github.com/uwussimo`, -); - -composer.command("about", async (ctx: Context): Promise => { - await ctx.reply(message, { - parse_mode: "HTML", - reply_markup: keyboard, - }); -}); - -export default composer; diff --git a/delta/groups.ts b/delta/groups.ts deleted file mode 100644 index 430e8e5..0000000 --- a/delta/groups.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { Composer, Context, InlineKeyboard } from "../deps.ts"; -import communities from "../communities.json" assert { type: "json" }; -import pager from "../utils/pager.ts"; - -const composer = new Composer(); -const ctxMenuText = - "List of Rust Language communities on Telegram:\nIf you want to add your own community to our list, update our community.json"; - -composer.command("group", async (ctx: Context): Promise => { - const keyboard = new InlineKeyboard(); - - for (const community of pager(1)) { - keyboard.text( - community.name, - `detail_${1}_${community.telegram.replace("@", "")}`, - ).row(); - } - - if (pager(2).length > 0) { - keyboard.text(`Next ➡️`, `group_2`); - } - - await ctx.reply(ctxMenuText, { - parse_mode: "HTML", - reply_markup: keyboard, - disable_web_page_preview: true, - }); -}); - -composer.callbackQuery(/^group_(\d+)$/, async (ctx: Context) => { - const page = Number(ctx.match![1]); - const keyboard = new InlineKeyboard(); - - for (const community of pager(page)) { - keyboard.text( - community.name, - `detail_${page}_${community.telegram.replace("@", "")}`, - ).row(); - } - - if (pager(page - 1).length > 0) { - keyboard.text(`⬅️ Previous`, `group_${page - 1}`); - } - - if (pager(page + 1).length > 0) { - keyboard.text(`Next ➡️`, `group_${page + 1}`); - } - - await ctx.editMessageText(ctxMenuText, { - parse_mode: "HTML", - reply_markup: keyboard, - disable_web_page_preview: true, - }); -}); - -composer.callbackQuery(/^detail_(\d+)_(.*)$/, async (ctx: Context) => { - const keyboard = new InlineKeyboard(); - const page = ctx.match![1]; - const result = communities.filter((com) => - com.telegram.replace("@", "") === ctx.match![2] - ); - - if (result.length) { - const data = result[0]; - - if (data.telegram) { - keyboard.url( - `Telegram`, - `https://t.me/${data.telegram.replace("@", "")}`, - ); - } - - if (data.link) { - keyboard.url(`Web`, data.link); - } - - keyboard.row().text(`🔙 Back`, `group_${page}`); - - await ctx.editMessageText( - `${data.name}` + - `\n` + - `\n` + - `${data.about}` + - `\n` + - `\n` + - `Use the following buttons to get to the links:`, - { - parse_mode: "HTML", - reply_markup: keyboard, - }, - ); - } else { - await ctx.editMessageText(`The group or community doesn't exist!`, { - parse_mode: "HTML", - reply_markup: new InlineKeyboard().text(`🔙 Back`, `group_${page}`), - }); - } -}); - -export default composer; diff --git a/delta/help.ts b/delta/help.ts deleted file mode 100644 index 62fadef..0000000 --- a/delta/help.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Composer, Context, InlineKeyboard } from "../deps.ts"; - -const composer = new Composer(); - -export const message = `List of available commands:` + - `\n` + - `\n` + - `/help - show this message` + - `\n` + - `/about - briefly about this bot` + - `\n` + - `/last - get short info about latest version` + - `\n` + - `/version - get information about specific version` + - `\n` + - `/group - rust communities` + - `\n` + - `/run <code> - run code and show stout & sterr` + - `\n` + - `\n` + - `Additionally, you can use inline mode to search packages from crates.io. Press the button below to get started!`; - -export const keyboard = new InlineKeyboard().switchInlineCurrent( - "Start searching crates!", - "rand", -); - -composer.command("help", async (ctx: Context): Promise => { - await ctx.reply(message, { - parse_mode: "HTML", - reply_markup: keyboard, - }); -}); - -export default composer; diff --git a/delta/inline.ts b/delta/inline.ts deleted file mode 100644 index 9385c1b..0000000 --- a/delta/inline.ts +++ /dev/null @@ -1,99 +0,0 @@ -// deno-lint-ignore-file no-explicit-any -import { api, Composer, Context, InlineKeyboard } from "../deps.ts"; -import encoder from "../utils/encoder.ts"; - -const composer = new Composer(); - -composer.inlineQuery(/(.*)/ig, async (ctx: Context): Promise => { - if (ctx.inlineQuery?.query) { - const request = await api.search(ctx.inlineQuery?.query, 50); - - if (request.meta.total === 0) { - return await ctx.answerInlineQuery([{ - type: "article", - id: "404", - title: "Error 404!", - description: `No results found for ${ctx.inlineQuery?.query}!`, - reply_markup: new InlineKeyboard().switchInlineCurrent( - "Shall we try again?", - "rand", - ), - input_message_content: { - message_text: - `"${ctx.inlineQuery?.query}" did not match any packages!` + - `\n` + - `Please, check for typos and try again!.`, - parse_mode: "HTML", - disable_web_page_preview: true, - }, - }]); - } - - return await ctx.answerInlineQuery( - request.crates.map((item) => ({ - type: "article", - id: crypto.randomUUID(), - title: item.name, - url: `https://crates.io/crates/${item.id}`, - description: item.description, - reply_markup: (() => { - const keyboard = new InlineKeyboard(); - keyboard.url(`Crate`, `https://crates.io/crates/${item.name}`); - if (item.homepage) keyboard.url(`Homepage`, item.homepage).row(); - if (item.documentation) { - keyboard.url(`Documentation`, item.documentation).row(); - } - if (item.repository) keyboard.url(`Repository`, item.repository); - return keyboard; - })(), - input_message_content: { - message_text: `🦀 Rusty Telegram Crate 🦀\n\n` + - `📦 Name: ${item.name}` + - `\n` + - `🚨 Last Version: ${item.newest_version} \n` + - `🎚 Downloads: recent: ${item.recent_downloads} | all: ${item.downloads} \n` + - `⌚️ Created: ${ - new Date(item.created_at).toLocaleString() - } \n` + - `📡 Updated: ${ - new Date(item.updated_at).toLocaleString() - } \n` + - `📰 Description: ${ - (encoder(item.description)).substring(0, 100) - }${item.description.length > 100 ? "..." : ""} \n\n` + - `🔌 Add (in your Cargo.toml): \n` + - `[dependencies]\n${item.name} = "${item.max_stable_version}"`, - parse_mode: "HTML", - disable_web_page_preview: true, - }, - })), - { cache_time: 1 }, // { cache_time: 24 * 3600 }, - ); - } - - if (!ctx.inlineQuery?.query) { - return await ctx.answerInlineQuery([{ - type: "article", - id: "101", - title: "Start typing!", - description: "Write the name of package you would like to search!", - reply_markup: new InlineKeyboard().switchInlineCurrent( - "Shall we try again?", - "rand", - ), - input_message_content: { - message_text: `Hello user!` + - `\n` + - `You just tried inline mode. This feature helps you to search packages ` + - `from crates.io using API SDK typescript library. ` + - `In order to start searching, simply write: ` + - `\n` + - `@rustaceanbot <name>`, - parse_mode: "HTML", - disable_web_page_preview: true, - }, - }]); - } -}); - -export default composer; diff --git a/delta/install.ts b/delta/install.ts deleted file mode 100644 index e69de29..0000000 diff --git a/delta/latest.ts b/delta/latest.ts deleted file mode 100644 index 556707d..0000000 --- a/delta/latest.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Composer, Context, InlineKeyboard } from "../deps.ts"; -import type { Release } from "../types/Github.d.ts"; -import { last } from "../utils/generator.ts"; -import hecker from "../utils/checker.ts"; - -const composer = new Composer(); - -export const message = async (data: Release) => - // make a message about the release date of the new release - `Latest version is ${data.tag_name} released at ${ - new Date(data.published_at).toDateString() - } by ${data.author.login}` + - `\n` + - `\n` + - `In order to install latest update, execute rustup update on your terminal!`; - -export const keyboard = (data: Release) => - new InlineKeyboard().url("More Information", data.html_url); - -composer.command("last", async (ctx: Context): Promise => { - const req = await last(); - await ctx.reply(await message(req), { - parse_mode: "HTML", - reply_markup: keyboard(req), - }); -}); - -export default composer; diff --git a/delta/mod.ts b/delta/mod.ts deleted file mode 100644 index 402b911..0000000 --- a/delta/mod.ts +++ /dev/null @@ -1,21 +0,0 @@ -import start from "./start.ts"; -import help from "./help.ts"; -import inline from "./inline.ts"; -import { Bot } from "../deps.ts"; -import about from "./about.ts"; -import groups from "./groups.ts"; -import run from "./run.ts"; -import version from "./version.ts"; -import latest from "./latest.ts"; - -export default async (bot: Bot) => { - await bot - .use(start) - .use(help) - .use(inline) - .use(groups) - .use(about) - .use(run) - .use(version) - .use(latest); -}; diff --git a/delta/run.ts b/delta/run.ts deleted file mode 100644 index 6e6857e..0000000 --- a/delta/run.ts +++ /dev/null @@ -1,89 +0,0 @@ -// deno-lint-ignore-file no-explicit-any -import { Composer, Context, InputFile } from "../deps.ts"; -import encoder from "../utils/encoder.ts"; -import playground from "../utils/playground.ts"; -import env from "../utils/config.ts"; - -const composer = new Composer(); - -composer.hears(/^\/(run|run@rustaceanbot)(.*)/ig, async (ctx: Context): Promise => { - if (ctx.chat!.type !== "private") return await ctx.reply( - "Please, don't spam this group chat and move to DM!", - { - parse_mode: "HTML", - }, - ); - - const code = ctx.message!.text!.charAt(4) === "@" - ? ctx.message!.text!.slice(17).trim() - : ctx.message!.text!.slice(4).trim() - - if (!code) { - return await ctx.reply( - "You should enter some rusty code or send me rusty code file on PM bruh!\n\nFor example:\n" + - '/run fn main() {\n println!("Hello, world!");\n}\n\n' + - "Btw, if you gonna send me some obscure loop shit, I'll simply ignore it :)", - { - parse_mode: "HTML", - }, - ); - } - - const instance = await playground(code); - - await ctx.reply( - `📃 Result : (${instance.success ? "✅ Success" : "❌ Failed"}) \n\n` + - `Output:\n
${
-        encoder(instance.stdout)
-      }
\nTerminal:\n
${encoder(instance.stderr)}
`, - { - parse_mode: "HTML", - reply_to_message_id: ctx.message!.message_id, - }, - ); -}); - -composer.on("message:document", async (ctx: Context): Promise => { - if (ctx.chat!.type !== "private") return; - if (!ctx.message!.document!.file_name!.endsWith(".rs")) { - await ctx.reply( - "You should send me rusty code with UTF8 encode!\n" + - "Btw, if you gonna send me some obscure loop shit, I'll simply ignore it :)" + - "\n\nFor example:", - { - parse_mode: "HTML", - }, - ); - - await ctx.replyWithDocument( - new InputFile({ - url: - "https://raw.githubusercontent.com/rust-lang-uz/telegram/main/example.rs", - }), - { - parse_mode: "HTML", - }, - ); - - return; - } - - const request = await fetch( - "https://api.telegram.org/file/bot" + env["TOKEN"] + "/" + - (await ctx.getFile()).file_path, - ); - const instance = await playground(await request.text()); - - await ctx.reply( - `📃 Result : (${instance.success ? "✅ Success" : "❌ Failed"}) \n\n` + - `Output:\n
${
-        encoder(instance.stdout)
-      }
\nTerminal:\n
${encoder(instance.stderr)}
`, - { - parse_mode: "HTML", - reply_to_message_id: ctx.message!.message_id, - }, - ); -}); - -export default composer; diff --git a/delta/start.ts b/delta/start.ts deleted file mode 100644 index 3b43859..0000000 --- a/delta/start.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Composer, Context, InlineKeyboard } from "../deps.ts"; - -const composer = new Composer(); - -export const message: string = - `Hello fellow (rustacean|rustina|rust evangelist)! \n` + - `\n` + - `Glad that you decided to give a try this bot. ` + - `This bot helps you to do some rusty things without leaving telegram ` + - `social messenger and interact with various APIs way more conveniently. `; - -export const keyboard = new InlineKeyboard() - .url("Rust", "https://rust-lang.org") - .url("GitHub", "https://github.com/rust-lang") - .row() - .url("Our Team", "https://github.com/rust-lang-uz") - .url("Rustacean", "https://rustacean.net"); - -composer.command("start", async (ctx: Context): Promise => { - await ctx.reply(message, { - parse_mode: "HTML", - reply_markup: keyboard, - }); -}); - -export default composer; diff --git a/delta/version.ts b/delta/version.ts deleted file mode 100644 index 3cf116a..0000000 --- a/delta/version.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Composer, Context, InlineKeyboard } from "../deps.ts"; -import { finder, pager as generator } from "../utils/generator.ts"; -import hecker from "../utils/checker.ts"; - -const composer = new Composer(); -const ctxMenuText = "List of Rust Programming Language Version:"; - -composer.command("version", async (ctx: Context): Promise => { - const keyboard = new InlineKeyboard(); - - for (const release of await generator(1)) { - keyboard.text( - release.tag_name, - `changelog_${1}_${release.id}`, - ).row(); - } - - if ((await generator(2)).length > 0) { - keyboard.text(`Next ➡️`, `version_2`); - } - - await ctx.reply(ctxMenuText, { - parse_mode: "HTML", - reply_markup: keyboard, - disable_web_page_preview: true, - }); -}); - -composer.callbackQuery(/^version_(\d+)$/, async (ctx: Context) => { - const page = Number(ctx.match![1]); - const keyboard = new InlineKeyboard(); - - for (const release of await generator(page)) { - keyboard.text( - release.tag_name, - `changelog_${page}_${release.id}`, - ).row(); - } - - if (page > 1) { - keyboard.text(`⬅️ Previous`, `version_${page - 1}`); - } - - if ((await generator(page + 1)).length > 0) { - keyboard.text(`Next ➡️`, `version_${page + 1}`); - } - - await ctx.editMessageText(ctxMenuText, { - parse_mode: "HTML", - reply_markup: keyboard, - disable_web_page_preview: true, - }); -}); - -composer.callbackQuery(/^changelog_(\d+)_(\d+)$/, async (ctx: Context) => { - const keyboard = new InlineKeyboard(); - const page = Number(ctx.match![1]); - const data = await finder(Number(ctx.match![2])); - - keyboard.url( - `📝 Read at GitHub`, - data.html_url, - ); - - keyboard.row().text(`🔙 Back`, `version_${page}`); - - await ctx.editMessageText( - `${data.name}` + - `\n` + - `\n` + - `Created at:${new Date(data.created_at).toDateString()}` + - `\n` + - `Published at:${new Date(data.published_at).toDateString()}` + - `\n` + - `Install: rustup install ${data.tag_name}` + - `\n` + - `\n` + - `Use the instant view or the following buttons to get more info:`, - { - parse_mode: "HTML", - reply_markup: keyboard, - }, - ); -}); - -export default composer; diff --git a/deno.json b/deno.json deleted file mode 100644 index 4081ee6..0000000 --- a/deno.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "compilerOptions": { - "strictPropertyInitialization": false, - "noImplicitReturns": true - }, - "fmt": { - "options": { - "indentWidth": 2, - "proseWrap": "preserve" - } - } -} diff --git a/deps.ts b/deps.ts deleted file mode 100644 index cba8ab2..0000000 --- a/deps.ts +++ /dev/null @@ -1,19 +0,0 @@ -export { - Bot, - Composer, - Context, - InlineKeyboard, - InputFile, - webhookCallback, -} from "https://deno.land/x/grammy@v1.9.0/mod.ts"; -export { serve } from "https://deno.land/std@0.144.0/http/server.ts"; -export type { NextFunction } from "https://deno.land/x/grammy@v1.9.0/mod.ts"; -export { - blue, - bold, - green, - red, - yellow, -} from "https://deno.land/std@0.144.0/fmt/colors.ts"; -export { api, Types } from "https://deno.land/x/crates@v1.0.1/mod.ts"; -export { config } from "https://deno.land/x/dotenv@v3.2.0/mod.ts"; diff --git a/example.rs b/example.rs deleted file mode 100644 index 4eafafd..0000000 --- a/example.rs +++ /dev/null @@ -1,12 +0,0 @@ -extern crate chrono; -use chrono::{DateTime, Utc}; - - -fn main() { - let now: DateTime = Utc::now(); - - println!("UTC now is: {}", now); - println!("UTC now in RFC 2822 is: {}", now.to_rfc2822()); - println!("UTC now in RFC 3339 is: {}", now.to_rfc3339()); - println!("UTC now in a custom format is: {}", now.format("%a %b %e %T %Y")); -} diff --git a/justfile b/justfile deleted file mode 100644 index 0d60edc..0000000 --- a/justfile +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env just --justfile - -alias s := start -alias d := development -alias f := format -alias l := lint - -# Start the freaking project -start: - deno run --allow-all mod.ts --config deno.json - -# Start the local development -development: - deno run --watch --allow-all mod.ts --config deno.json - -# Formatting source codes -format: - deno fmt --config deno.json - -# Check for eslint errors -lint: - deno lint --config deno.json diff --git a/mod.ts b/mod.ts deleted file mode 100644 index 13873f9..0000000 --- a/mod.ts +++ /dev/null @@ -1 +0,0 @@ -import "./core.ts"; diff --git a/src/functions/about.rs b/src/functions/about.rs new file mode 100644 index 0000000..1df5156 --- /dev/null +++ b/src/functions/about.rs @@ -0,0 +1,34 @@ +use crate::{ + hooks, + utils::{keyboard::Keyboard, message::Rustina}, +}; +use teloxide::{ + payloads::SendMessageSetters, + prelude::*, + types::{InlineKeyboardMarkup, ParseMode}, +}; + +static TEXT: &str = r#" +Hello fellow (rustacean|rustina|evangelist)! + +This is a telegram bot created by a rustacean to help people interact with various Rust APIs way more conveniently. +"#; + +pub async fn command(bot: &Bot, msg: &Message) -> ResponseResult<()> { + if !hooks::is_private(bot, msg).await.unwrap() { + return Ok(()); + } + + bot.send_message_tf(msg.chat.id, TEXT, msg) + .parse_mode(ParseMode::Html) + .reply_markup(keyboard()) + .await?; + + Ok(()) +} + +pub fn keyboard() -> InlineKeyboardMarkup { + let mut keyboard = Keyboard::new(); + keyboard.url("Source Code", "https://github.com/rust-lang-uz/telegram"); + keyboard.url("Author", "https://github.com/orzklv") +} diff --git a/src/functions/groups.rs b/src/functions/groups.rs new file mode 100644 index 0000000..660a3d2 --- /dev/null +++ b/src/functions/groups.rs @@ -0,0 +1,121 @@ +use crate::utils::{ + groups::{Group, Groups}, + keyboard::Keyboard, + message::Rustina, +}; +use teloxide::{ + payloads::{EditMessageTextSetters, SendMessageSetters}, + prelude::*, + types::{InlineKeyboardMarkup, ParseMode}, +}; + +static TEXT: &str = "List of Rust Language communities on Telegram:\nIf you want to add your own community to our list, update our community.json"; + +pub async fn command(bot: &Bot, msg: &Message, groups: &Groups) -> ResponseResult<()> { + bot.send_message_tf(msg.chat.id, TEXT, msg) + .parse_mode(ParseMode::Html) + .reply_markup(keyboard_list(groups, 1)) + .disable_web_page_preview(true) + .await?; + + Ok(()) +} + +pub async fn callback_list( + bot: &Bot, + q: &CallbackQuery, + args: &Vec<&str>, + groups: &Groups, +) -> ResponseResult<()> { + if !args.is_empty() { + if let Some(Message { id, chat, .. }) = q.message.clone() { + bot.edit_message_text(chat.id, id, TEXT) + .parse_mode(ParseMode::Html) + .reply_markup(keyboard_list(groups, args[0].parse().unwrap_or(1))) + .disable_web_page_preview(true) + .await?; + } else if let Some(id) = q.inline_message_id.clone() { + bot.edit_message_text_inline(id, "Oopsie, something went wrong...") + .await?; + } + } + + Ok(()) +} + +pub async fn callback_detail(bot: &Bot, q: &CallbackQuery, args: &Vec<&str>) -> ResponseResult<()> { + let groups: Groups = Groups::new(); + let find = groups.find_group(args[1..].join("_").to_string()); + + if !args.is_empty() { + if let Some(Message { id, chat, .. }) = q.message.clone() { + bot.edit_message_text(chat.id, id, view_detail(&find)) + .parse_mode(ParseMode::Html) + .reply_markup(keyboard_detail(args[0].parse().unwrap_or(1), &find)) + .await?; + } else if let Some(id) = q.inline_message_id.clone() { + bot.edit_message_text_inline(id, "Oopsie, something went wrong...") + .await?; + } + } + + Ok(()) +} + +pub fn view_detail(data: &Option) -> String { + match data { + Some(d) => { + format!( + "{}\n\n{}\n\nUse the following buttons to get to the links:", + d.name, d.about + ) + } + None => "The following group doesn't seem like exist!".to_string(), + } +} + +pub fn keyboard_list(groups: &Groups, page: i32) -> InlineKeyboardMarkup { + let mut keyboard = Keyboard::new(); + + for group in groups.get_groups(page, 5) { + keyboard.text( + &group.name, + &format!( + "detail_{}_{}", + page, + group.telegram.clone().replace('@', "") + ), + ); + keyboard.row(); + } + + if !groups.get_groups(page + 1, 5).is_empty() { + keyboard.text("Next ➡️", &format!("group_{}", page + 1)); + } + + if page > 1 { + keyboard.text("🔙 Back", &format!("group_{}", page - 1)); + } + + keyboard.get() +} + +pub fn keyboard_detail(page: i32, data: &Option) -> InlineKeyboardMarkup { + let mut keyboard = Keyboard::new(); + + if let Some(group) = data { + keyboard.url("Telegram", &format!("https://t.me/{}", group.telegram)); + + if group.link.is_some() { + keyboard.url("Web", &group.link.clone().unwrap()); + } + + keyboard.row(); + + keyboard.text("🔙 Back", &format!("group_{}", page)); + } else { + keyboard.text("🔙 Back", &format!("group_{}", page)); + } + + keyboard.get() +} diff --git a/src/functions/help.rs b/src/functions/help.rs new file mode 100644 index 0000000..d91604c --- /dev/null +++ b/src/functions/help.rs @@ -0,0 +1,34 @@ +use super::start::keyboard; +use crate::{utils::message::Rustina, Command}; +use teloxide::{payloads::SendMessageSetters, prelude::*, types::ParseMode}; + +static TEXT: &[(&str, &str)] = &[ + ("help", "show this message"), + ("about", "briefly about this bot"), + ("latest", "get short info about latest version"), + ("version", "get information about specific version"), + ("group", "rust communities"), + ("run <code>", "run code and show stout & sterr"), + ("useful", "useful resources"), +]; + +pub async fn command(bot: &Bot, msg: &Message, _cmd: &Command) -> ResponseResult<()> { + let mut text = String::new(); + + text.push_str("List of available commands:\n\n"); + + for cmd in TEXT { + text.push('/'); + text.push_str(cmd.0); + text.push_str(" - "); + text.push_str(format!("{text}", text = cmd.1).as_str()); + text.push('\n'); + } + + bot.send_message_tf(msg.chat.id, text, msg) + .parse_mode(ParseMode::Html) + .reply_markup(keyboard()) + .await?; + + Ok(()) +} diff --git a/src/functions/inline.rs b/src/functions/inline.rs new file mode 100644 index 0000000..a1e1908 --- /dev/null +++ b/src/functions/inline.rs @@ -0,0 +1,87 @@ +use crates_io_api::{AsyncClient, Crate, CratesQuery}; +use std::error::Error; +use teloxide::{prelude::*, types::*}; + +use crate::utils::inlines::*; + +pub async fn inline( + bot: Bot, + crates_client: AsyncClient, + q: InlineQuery, +) -> Result<(), Box> { + if q.query.is_empty() { + return { + bot.answer_inline_query( + q.id, + vec![InlineQueryResultArticle::new( + "101", + "Start searching!", + InputMessageContent::Text( + InputMessageContentText::new(NO_INPUT) + .parse_mode(ParseMode::Html) + .disable_web_page_preview(true), + ), + ) + .reply_markup(err_keyboard()) + .into()], + ) + .await?; + Ok(()) + }; + } + + let request: CratesQuery = CratesQuery::builder() + .search(q.query.clone()) + .page(1) + .page_size(50) + .build(); + + let request: Vec = crates_client.crates(request).await.unwrap().crates; + + if request.is_empty() { + return { + bot.answer_inline_query( + q.id, + vec![InlineQueryResultArticle::new( + "404", + "Couldn't find!", + InputMessageContent::Text( + InputMessageContentText::new( + format!("There are no results related to {}!\nPlease, Try to search with other names or parameters!", + q.query.clone()) + ) + .parse_mode(ParseMode::Html) + .disable_web_page_preview(true), + ), + ) + .reply_markup(err_keyboard()) + .into()], + ) + .await?; + Ok(()) + }; + } + + let request: Vec = request + .iter() + .map(|c: &Crate| { + InlineQueryResult::Article( + InlineQueryResultArticle::new( + uuid::Uuid::new_v4(), + c.name.clone(), + InputMessageContent::Text( + InputMessageContentText::new(view_generate(c)) + .parse_mode(ParseMode::Html) + .disable_web_page_preview(true), + ), + ) + .description(c.description.clone().unwrap()) + .url(url::Url::parse(&format!("https://crates.io/crates/{}", c.id)).unwrap()) + .reply_markup(kb_generate(c)), + ) + }) + .collect(); + + bot.answer_inline_query(q.id, request).send().await?; + Ok(()) +} diff --git a/src/functions/latest.rs b/src/functions/latest.rs new file mode 100644 index 0000000..3e45711 --- /dev/null +++ b/src/functions/latest.rs @@ -0,0 +1,39 @@ +use crate::utils::{github::GitHub, keyboard::Keyboard, message::Rustina}; +use octocrab::models::repos::Release; +use teloxide::{ + payloads::SendMessageSetters, + prelude::*, + types::{InlineKeyboardMarkup, ParseMode}, +}; + +pub async fn command(bot: &Bot, github: GitHub, msg: &Message) -> ResponseResult<()> { + let latest = github.get_latest().await.unwrap(); + + bot.send_message_tf(msg.chat.id, view(&latest), msg) + .parse_mode(ParseMode::Html) + .reply_markup(keyboard(&latest)) + .await?; + + Ok(()) +} + +pub fn view(release: &Release) -> String { + format!( + "Latest version is \ + {} released at {} by \ + {}.\ + \n\n\ + In order to install latest update, execute rustup update on your terminal! + ", + release.tag_name, + release.tag_name, + release.published_at.unwrap().date_naive(), + release.author.html_url, + release.author.login, + ) +} + +pub fn keyboard(release: &Release) -> InlineKeyboardMarkup { + let mut keyboard = Keyboard::new(); + keyboard.url("More Information", release.html_url.as_str()) +} diff --git a/src/functions/mod.rs b/src/functions/mod.rs new file mode 100644 index 0000000..78852f4 --- /dev/null +++ b/src/functions/mod.rs @@ -0,0 +1,77 @@ +pub mod about; +pub mod groups; +pub mod help; +pub mod inline; +pub mod latest; +pub mod run; +pub mod start; +pub mod useful; +pub mod version; + +pub use inline::inline; + +use crate::utils::{github::GitHub, groups::Groups, resources::Resources}; +use crate::Command; +use std::error::Error; +use teloxide::{prelude::*, types::*}; + +pub async fn commands( + bot: Bot, + _me: Me, + msg: Message, + cmd: Command, + github: GitHub, + groups: Groups, + resources: Resources, +) -> Result<(), Box> { + let _ = match cmd { + Command::Start => crate::functions::start::command(&bot, &msg).await, // done + Command::Help => crate::functions::help::command(&bot, &msg, &cmd).await, // partially done + Command::About => crate::functions::about::command(&bot, &msg).await, // done + Command::Group => crate::functions::groups::command(&bot, &msg, &groups).await, // done + Command::Run => crate::functions::run::command(&bot, &msg).await, // done + Command::Latest => crate::functions::latest::command(&bot, github, &msg).await, // done + Command::Version => crate::functions::version::command(&bot, github, &msg).await, // done + Command::Useful => crate::functions::useful::command(&bot, &msg, &resources).await, // done + }; + + Ok(()) +} + +pub async fn callback( + bot: Bot, + q: CallbackQuery, + github: GitHub, + groups: Groups, + resources: Resources, +) -> Result<(), Box> { + let mut args: Vec<&str> = Vec::new(); + + if let Some(data) = q.data.clone() { + if data.contains('_') { + args = data.split('_').collect(); + } else { + args.push(&data); + } + + let _ = match args.remove(0) { + "group" => crate::functions::groups::callback_list(&bot, &q, &args, &groups).await, + "detail" => crate::functions::groups::callback_detail(&bot, &q, &args).await, + "version" => crate::functions::version::callback_list(&bot, &q, &args, github).await, + "changelog" => { + crate::functions::version::callback_detail(&bot, &q, &args, github).await + } + "useful" => crate::functions::useful::callback_list(&bot, &q, &resources).await, + "category" => { + crate::functions::useful::callback_category_list(&bot, &q, &args, &resources).await + } + "material" => { + crate::functions::useful::callback_material_detail(&bot, &q, &args, &resources) + .await + } + _ => Ok(()), + }; + } + + Ok(()) +} diff --git a/src/functions/run.rs b/src/functions/run.rs new file mode 100644 index 0000000..af09bd3 --- /dev/null +++ b/src/functions/run.rs @@ -0,0 +1,25 @@ +use crate::utils::{keyboard::Keyboard, message::Rustina}; +use teloxide::{ + payloads::SendMessageSetters, + prelude::*, + types::{InlineKeyboardMarkup, ParseMode}, +}; + +static CONTENT: &str = r#" +We tried to experiment with many types of way implementing playground and ended up with adding it as web app! +"#; + +pub async fn command(bot: &Bot, msg: &Message) -> ResponseResult<()> { + bot.send_message_tf(msg.chat.id, CONTENT, msg) + .parse_mode(ParseMode::Html) + .reply_markup(keyboard()) + .reply_to_message_id(msg.id) + .await?; + + Ok(()) +} + +pub fn keyboard() -> InlineKeyboardMarkup { + let mut keyboard = Keyboard::new(); + keyboard.web_app("Playground", "https://play.rust-lang.org") +} diff --git a/src/functions/start.rs b/src/functions/start.rs new file mode 100644 index 0000000..b993f9d --- /dev/null +++ b/src/functions/start.rs @@ -0,0 +1,30 @@ +use crate::utils::{keyboard::Keyboard, message::Rustina}; +use teloxide::{ + payloads::SendMessageSetters, + prelude::*, + types::{InlineKeyboardMarkup, ParseMode}, +}; + +static TEXT: &str = r#" +Hello fellow (rustacean|rustina|rust evangelist)! + +Glad that you decided to give a try this bot. This bot helps you to do some rusty things without leaving telegram social messenger and interact with various APIs way more conveniently. +"#; + +pub async fn command(bot: &Bot, msg: &Message) -> ResponseResult<()> { + bot.send_message_tf(msg.chat.id, TEXT, msg) + .parse_mode(ParseMode::Html) + .reply_markup(keyboard()) + .await?; + + Ok(()) +} + +pub fn keyboard() -> InlineKeyboardMarkup { + let mut keyboard = Keyboard::new(); + keyboard.url("Rust", "https://rust-lang.org"); + keyboard.url("GitHub", "https://github.com/rust-lang"); + keyboard.row(); + keyboard.url("Our Team", "https://github.com/rust-lang-uz"); + keyboard.url("Rustacean", "https://rustacean.net") +} diff --git a/src/functions/useful.rs b/src/functions/useful.rs new file mode 100644 index 0000000..1f611c2 --- /dev/null +++ b/src/functions/useful.rs @@ -0,0 +1,156 @@ +use crate::utils::{ + keyboard::Keyboard, + message::Rustina, + resources::{Resource, Resources}, +}; +use teloxide::{ + payloads::SendMessageSetters, + prelude::*, + types::{InlineKeyboardMarkup, ParseMode}, +}; + +static TEXT: &str = "Materials related to Rust:\n\ +If you would like to add more materials, please, check out our \ +\ +source.json and send PR!"; + +pub async fn command(bot: &Bot, msg: &Message, resources: &Resources) -> ResponseResult<()> { + let categories = resources.get_keys(); + + bot.send_message_tf(msg.chat.id, TEXT, msg) + .parse_mode(ParseMode::Html) + .reply_markup(keyboard_list(categories)) + .disable_web_page_preview(true) + .await?; + + Ok(()) +} + +pub async fn callback_list( + bot: &Bot, + q: &CallbackQuery, + resources: &Resources, +) -> ResponseResult<()> { + let categories = resources.get_keys(); + + if let Some(Message { id, chat, .. }) = q.message.clone() { + bot.edit_message_text(chat.id, id, TEXT) + .parse_mode(ParseMode::Html) + .reply_markup(keyboard_list(categories)) + .disable_web_page_preview(true) + .await?; + } else if let Some(id) = q.inline_message_id.clone() { + bot.edit_message_text_inline(id, "Oopsie, something went wrong...") + .await?; + } + + Ok(()) +} + +pub async fn callback_category_list( + bot: &Bot, + q: &CallbackQuery, + args: &Vec<&str>, + resources: &Resources, +) -> ResponseResult<()> { + let find = resources.get_materials(args.join("_").as_str()).unwrap(); + + if !args.is_empty() { + if let Some(Message { id, chat, .. }) = q.message.clone() { + bot.edit_message_text(chat.id, id, view_category_list(&args.join(" "))) + .parse_mode(ParseMode::Html) + .reply_markup(keyboard_category_list(find, args.join("_"))) + .await?; + } else if let Some(id) = q.inline_message_id.clone() { + bot.edit_message_text_inline(id, "Oopsie, something went wrong...") + .await?; + } + } + + Ok(()) +} + +pub async fn callback_material_detail( + bot: &Bot, + q: &CallbackQuery, + args: &Vec<&str>, + resources: &Resources, +) -> ResponseResult<()> { + let find = resources + .get_material(args[1..].join("_").as_str(), args[0].parse().unwrap()) + .unwrap(); + + if !args.is_empty() { + if let Some(Message { id, chat, .. }) = q.message.clone() { + bot.edit_message_text(chat.id, id, view_material_detail(find)) + .parse_mode(ParseMode::Html) + .reply_markup(keyboard_material_detail(find, args[1..].join("_"))) + .await?; + } else if let Some(id) = q.inline_message_id.clone() { + bot.edit_message_text_inline(id, "Oopsie, something went wrong...") + .await?; + } + } + + Ok(()) +} + +pub fn view_category_list(category: &str) -> String { + format!("You're located at {}{} category.\nPlease, choose one of provided materials above...", + &category[0..1].to_uppercase(), &category[1..].replace('_', " ")) +} + +pub fn view_material_detail(material: &Resource) -> String { + format!( + "{}\n\n{}\n\nGo to website by pressing the button provided below:", + material.name, material.desc + ) +} + +pub fn keyboard_list(categories: Vec) -> InlineKeyboardMarkup { + let mut keyboard = Keyboard::new(); + + for category in categories { + keyboard.text( + &format!( + "{}{}", + &category[0..1].to_uppercase(), + &category[1..].replace('_', " ") + ), + &format!("category_{}", category), + ); + keyboard.row(); + } + + keyboard.get() +} + +pub fn keyboard_category_list(material: &[Resource], category: String) -> InlineKeyboardMarkup { + let mut keyboard = Keyboard::new(); + + for (index, value) in material.iter().enumerate() { + keyboard.text( + &format!( + "{}{}", + &value.name[0..1].to_uppercase(), + &value.name[1..].replace('_', " ") + ), + &format!("material_{}_{}", index, category), + ); + keyboard.row(); + } + + keyboard.text("🔙 Back", "useful"); + + keyboard.get() +} + +pub fn keyboard_material_detail(resource: &Resource, category: String) -> InlineKeyboardMarkup { + let mut keyboard = Keyboard::new(); + + keyboard.url("Website", &resource.link); + keyboard.row(); + keyboard.text("🔙 Back", &format!("category_{}", category)); + + keyboard.get() +} diff --git a/src/functions/version.rs b/src/functions/version.rs new file mode 100644 index 0000000..d690326 --- /dev/null +++ b/src/functions/version.rs @@ -0,0 +1,121 @@ +use crate::utils::{github::GitHub, keyboard::Keyboard, message::Rustina}; +use octocrab::models::repos::Release; +use teloxide::{ + payloads::SendMessageSetters, + prelude::*, + types::{InlineKeyboardMarkup, ParseMode}, +}; + +static TEXT: &str = "List of rust programming language versions:"; + +pub async fn command(bot: &Bot, github: GitHub, msg: &Message) -> ResponseResult<()> { + let versions = github.get_list(1).await.unwrap(); + let next_page = github.get_list(2).await.unwrap(); + + bot.send_message_tf(msg.chat.id, TEXT, msg) + .parse_mode(ParseMode::Html) + .reply_markup(keyboard_list(1, versions, Some(next_page))) + .await?; + + Ok(()) +} + +pub async fn callback_list( + bot: &Bot, + q: &CallbackQuery, + args: &Vec<&str>, + github: GitHub, +) -> ResponseResult<()> { + let page = args[0].parse::().unwrap(); + let versions: Vec = github.get_list(page).await.unwrap(); + let next_page = github.get_list(page + 1).await.unwrap(); + + if !args.is_empty() { + if let Some(Message { id, chat, .. }) = q.message.clone() { + bot.edit_message_text(chat.id, id, TEXT) + .parse_mode(ParseMode::Html) + .reply_markup(keyboard_list(page, versions, Some(next_page))) + .await?; + } else if let Some(id) = q.inline_message_id.clone() { + bot.edit_message_text_inline(id, "Oopsie, something went wrong...") + .await?; + } + } + + Ok(()) +} + +pub async fn callback_detail( + bot: &Bot, + q: &CallbackQuery, + args: &Vec<&str>, + github: GitHub, +) -> ResponseResult<()> { + let page = args[0].parse::().unwrap(); + let version: Release = github.get_detail(args[1]).await.unwrap(); + + if !args.is_empty() { + if let Some(Message { id, chat, .. }) = q.message.clone() { + bot.edit_message_text(chat.id, id, view_detail(&version)) + .parse_mode(ParseMode::Html) + .reply_markup(keyboard_detail(page, version)) + .await?; + } else if let Some(id) = q.inline_message_id.clone() { + bot.edit_message_text_inline(id, "Oopsie, something went wrong...") + .await?; + } + } + + Ok(()) +} + +pub fn view_detail(release: &Release) -> String { + format!( + "{}\n\n\ + Created at: {}\n\ + Published at: {}\n\ + Install: rustup install {}\n\n\ + Use \"Instant view\" or the following buttons to get more info:", + release.html_url, + release.name.clone().unwrap(), + release.created_at.unwrap().format("%d.%m.%Y"), + release.published_at.unwrap().format("%d.%m.%Y"), + release.tag_name + ) +} + +pub fn keyboard_list( + page: u32, + releases: Vec, + next_page: Option>, +) -> InlineKeyboardMarkup { + let mut keyboard = Keyboard::new(); + + for release in releases { + keyboard.text( + &release.tag_name, + &format!("changelog_{}_{}", page, release.tag_name), + ); + keyboard.row(); + } + + if page > 1 { + keyboard.text("⬅️ Previous", &format!("version_{}", page - 1)); + } + + if next_page.is_some() && !next_page.unwrap().is_empty() { + keyboard.text("Next ➡️", &format!("version_{}", page + 1)); + } + + keyboard.get() +} + +pub fn keyboard_detail(page: u32, release: Release) -> InlineKeyboardMarkup { + let mut keyboard = Keyboard::new(); + + keyboard.url("📝 Read at GitHub", release.html_url.as_str()); + keyboard.row(); + keyboard.text("🔙 Back", &format!("version_{}", page)); + + keyboard.get() +} diff --git a/src/hooks/is_private.rs b/src/hooks/is_private.rs new file mode 100644 index 0000000..1c00203 --- /dev/null +++ b/src/hooks/is_private.rs @@ -0,0 +1,38 @@ +use teloxide::{ + payloads::SendMessageSetters, + prelude::*, + types::{InlineKeyboardMarkup, ParseMode}, +}; + +use crate::utils::{ + keyboard::Keyboard, + message::{delete_timer, Rustina}, +}; + +static TEXT: &str = "⚠️ This command can be used only in private chat!"; + +pub fn keyboard() -> InlineKeyboardMarkup { + let mut keyboard: Keyboard = Keyboard::new(); + keyboard.url("Go to private chat", "https://t.me/rustaceanbot") +} + +pub async fn is_private(bot: &Bot, msg: &Message) -> ResponseResult { + if msg.chat.is_private() { + return Ok(true); + } + + match bot.delete_message(msg.chat.id, msg.id).await { + Ok(_) => {} + Err(_) => {} + }; + + let message = bot + .send_message_tf(msg.chat.id, TEXT, msg) + .parse_mode(ParseMode::Html) + .reply_markup(keyboard()) + .await?; + + delete_timer(bot, &message, 10).await?; + + Ok(false) +} diff --git a/src/hooks/mod.rs b/src/hooks/mod.rs new file mode 100644 index 0000000..887384c --- /dev/null +++ b/src/hooks/mod.rs @@ -0,0 +1,3 @@ +pub mod is_private; + +pub use is_private::is_private; diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..92f85a1 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,54 @@ +#![allow(clippy::single_match)] + +pub mod functions; +pub mod hooks; +pub mod utils; + +use teloxide::{ + dispatching::{UpdateFilterExt, UpdateHandler}, + prelude::*, + utils::command::BotCommands, +}; + +#[derive(BotCommands, Clone, Debug)] +#[command(rename_rule = "lowercase", parse_with = "split")] +#[command(description = "These are the commands that I can understand:")] +pub enum Command { + /// List existing commands + Help, + + /// Starting point of the bot + Start, + + /// About the bot + About, + + /// Latest version + Latest, + + /// Specific version + Version, + + /// Available groups + Group, + + /// Run rust code + Run, + + /// Useful resources + Useful, +} + +pub fn handler() -> UpdateHandler> { + dptree::entry() + // Inline Queries + .branch(Update::filter_inline_query().endpoint(functions::inline)) + // Callbacks + .branch(Update::filter_callback_query().endpoint(functions::callback)) + // Commands + .branch( + Update::filter_message() + .filter_command::() + .endpoint(functions::commands), + ) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..d4be7b3 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,63 @@ +use crates_io_api::AsyncClient; +use std::error::Error; +use telegram::{ + handler, + utils::{cargo_like_log, github::GitHub, groups::Groups, resources::Resources}, +}; +use teloxide::{prelude::*, update_listeners::webhooks}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + pretty_env_logger::init(); + log::info!("Starting Rustina Assistant..."); + + let bot = Bot::from_env(); + + let groups = Groups::new(); + let github = GitHub::new(); + let crates_client = AsyncClient::new( + "Rustina Assistant (rust@maid.uz)", + std::time::Duration::from_millis(100), + ) + .unwrap(); + let resources = Resources::new(); + + // Dispatcher flow control + let mut dispatcher = Dispatcher::builder(bot.clone(), handler()) + .dependencies(dptree::deps![crates_client, github, groups, resources]) + // If no handler succeeded to handle an update, this closure will be called + .default_handler(|upd| async move { + log::warn!("Unhandled update: {:?}", upd); + }) + // If the dispatcher fails for some reason, execute this handler + .error_handler(LoggingErrorHandler::with_custom_text( + "An error has occurred in the dispatcher", + )) + .enable_ctrlc_handler() + .build(); + + match std::env::var("WEBHOOK_URL") { + Ok(v) => { + cargo_like_log("Mode", &format!("starting webhook on {}", v)); + let addr = ([0, 0, 0, 0], 8444).into(); // port 8444 + let listener = webhooks::axum(bot, webhooks::Options::new(addr, v.parse().unwrap())) + .await + .expect("Couldn't setup webhook"); + + dispatcher + .dispatch_with_listener( + listener, + LoggingErrorHandler::with_custom_text( + "An error has occurred in the dispatcher", + ), + ) + .await; + } + Err(_) => { + cargo_like_log("Mode", "starting polling on localhost"); + dispatcher.dispatch().await; + } + } + + Ok(()) +} diff --git a/src/utils/github.rs b/src/utils/github.rs new file mode 100644 index 0000000..53dbd9a --- /dev/null +++ b/src/utils/github.rs @@ -0,0 +1,71 @@ +use octocrab::{models::repos::Release, Octocrab}; +use std::error::Error; + +#[derive(Clone, Debug)] +pub struct GitHub { + client: Octocrab, +} + +impl Default for GitHub { + fn default() -> Self { + Self::new() + } +} + +impl GitHub { + pub fn new() -> Self { + Self { + client: Octocrab::builder() + .add_header( + "User-Agent".parse().unwrap(), + "Rustina Assistant (rust@maid.uz)".to_string(), + ) + .add_header( + "Authorization".parse().unwrap(), + std::env::var("GITHUB_TOKEN").unwrap(), + ) + .build() + .unwrap(), + } + } + + pub async fn get_latest(&self) -> Result> { + let latest = self + .client + .repos("rust-lang", "rust") + .releases() + .get_latest() + .await?; + + Ok(latest) + } + + pub async fn get_list(&self, page: u32) -> Result, Box> { + let versions = self + .client + .repos("rust-lang", "rust") + .releases() + .list() + .per_page(5) + .page(page) + .send() + .await? + .take_items(); + + Ok(versions) + } + + pub async fn get_detail( + &self, + tag_name: &str, + ) -> Result> { + let detail = self + .client + .repos("rust-lang", "rust") + .releases() + .get_by_tag(tag_name) + .await?; + + Ok(detail) + } +} diff --git a/src/utils/groups.rs b/src/utils/groups.rs new file mode 100644 index 0000000..882b5cd --- /dev/null +++ b/src/utils/groups.rs @@ -0,0 +1,63 @@ +use serde::{Deserialize, Serialize}; + +static GROUPS: &str = include_str!("../../data/communities.json"); + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct Group { + pub name: String, + pub about: String, + pub telegram: String, + pub link: Option, +} + +#[derive(Debug, Clone)] +pub struct Groups { + groups: Vec, +} + +impl Default for Groups { + fn default() -> Self { + Self::new() + } +} + +impl Groups { + pub fn new() -> Self { + let json: Vec = serde_json::from_str(GROUPS).unwrap(); + + Self { groups: json } + } + + pub fn get_groups(&self, page_number: i32, page_size: i32) -> Vec { + let start = (page_number - 1) * page_size; + let end = page_number * page_size; + + if start < 0 || end < 0 { + return Vec::new(); + } + + if start as usize > self.groups.len() { + return Vec::new(); + } + + if end as usize > self.groups.len() { + return self.groups[start as usize..].to_vec(); + } + + self.groups[start as usize..end as usize].to_vec() + } + + pub fn find_group(&self, query: String) -> Option { + let search: Vec<&Group> = self + .groups + .iter() + .filter(|group| group.telegram[1..] == query) + .collect(); + + if search.is_empty() { + return None; + } + + Some(search[0].clone()) + } +} diff --git a/src/utils/inlines.rs b/src/utils/inlines.rs new file mode 100644 index 0000000..58d3342 --- /dev/null +++ b/src/utils/inlines.rs @@ -0,0 +1,90 @@ +use super::keyboard::Keyboard; +use crates_io_api::Crate; +use teloxide::types::*; + +pub static NO_INPUT: &str = r#" +Hello user! + +You just tried inline mode. This feature helps you to search packages from crates.io using API SDK typescript library. In order to start searching, simply write: + +@rustaceanbot <name> +"#; + +pub fn view_generate(c: &Crate) -> String { + let mut result = String::from("🦀 Rusty Telegram Crate 🦀\n\n"); + + result.push_str(&format!("📦 Name: {}\n", c.name)); + result.push_str(&format!( + "🚨 Last Version: {}\n", + c.max_version + )); + result.push_str(&format!( + "🎚 Downloads: recent: {} | all: {}\n", + c.recent_downloads.unwrap(), + c.downloads + )); + result.push_str(&format!( + "⌚️ Created: {}\n", + c.created_at.date_naive() + )); + result.push_str(&format!( + "📡 Updated: {}\n", + c.updated_at.date_naive() + )); + result.push_str(&format!( + "📰 Description: {}{}\n\n", + if c.description.clone().unwrap().len() > 100 { + c.description + .clone() + .unwrap() + .chars() + .take(100) + .collect::() + } else { + c.description.clone().unwrap() + }, + if c.description.clone().unwrap().len() > 100 { + "..." + } else { + "" + } + )); + result.push_str("🔌 Add (in your Cargo.toml): \n"); + result.push_str(&format!( + "[dependencies]\n{} = \"{}\"", + c.name, c.max_version + )); + + result +} + +pub fn kb_generate(c: &Crate) -> InlineKeyboardMarkup { + let mut keyboard = Keyboard::new(); + + keyboard.url( + "Crate", + format!("https://crates.io/crates/{}", c.name).as_str(), + ); + + if c.homepage.is_some() { + keyboard.url("Homepage", &c.homepage.clone().unwrap()); + keyboard.row(); + } + + if c.documentation.is_some() { + keyboard.url("Documentation", &c.documentation.clone().unwrap()); + keyboard.row(); + } + + if c.repository.is_some() { + keyboard.url("Repository", &c.repository.clone().unwrap()); + keyboard.row(); + } + + keyboard.get() +} + +pub fn err_keyboard() -> InlineKeyboardMarkup { + let mut keyboard = Keyboard::new(); + keyboard.switch_inline_current("Shall we try again?", "rand") +} diff --git a/src/utils/keyboard.rs b/src/utils/keyboard.rs new file mode 100644 index 0000000..3ecbbe4 --- /dev/null +++ b/src/utils/keyboard.rs @@ -0,0 +1,78 @@ +use std::str::FromStr; + +use teloxide::types::{InlineKeyboardButton, InlineKeyboardMarkup, WebAppInfo}; +use url::Url; + +#[derive(Clone)] +pub struct Keyboard { + keyboard: Vec>, +} + +impl Default for Keyboard { + fn default() -> Self { + Keyboard::new() + } +} + +impl Keyboard { + pub fn new() -> Self { + Self { + keyboard: vec![vec![]], + } + } + + /// Add a text callback to keyboard + pub fn text(&mut self, text: &str, callback: &str) -> InlineKeyboardMarkup { + self.keyboard + .last_mut() + .unwrap() + .push(InlineKeyboardButton::callback(text, callback)); + + self.get() + } + + /// Add an url button to keyboard + pub fn url(&mut self, text: &str, url: &str) -> InlineKeyboardMarkup { + let parsed_url = Url::parse(url).unwrap(); + + self.keyboard + .last_mut() + .unwrap() + .push(InlineKeyboardButton::url(text, parsed_url)); + + self.get() + } + + pub fn switch_inline_current(&mut self, text: &str, query: &str) -> InlineKeyboardMarkup { + self.keyboard.last_mut().unwrap().push( + InlineKeyboardButton::switch_inline_query_current_chat(text, query), + ); + + self.get() + } + + pub fn web_app(&mut self, text: &str, link: &str) -> InlineKeyboardMarkup { + self.keyboard + .last_mut() + .unwrap() + .push(InlineKeyboardButton::web_app( + text, + WebAppInfo { + url: Url::from_str(link).unwrap(), + }, + )); + + self.get() + } + + /// Add next buttons from new line + pub fn row(&mut self) -> InlineKeyboardMarkup { + self.keyboard.push(vec![]); + self.get() + } + + /// Return the final result + pub fn get(&self) -> InlineKeyboardMarkup { + InlineKeyboardMarkup::new(self.keyboard.clone()) + } +} diff --git a/src/utils/message.rs b/src/utils/message.rs new file mode 100644 index 0000000..7028fb5 --- /dev/null +++ b/src/utils/message.rs @@ -0,0 +1,54 @@ +use teloxide::{ + prelude::*, + requests::JsonRequest, + types::*, + {payloads, payloads::*}, +}; + +pub trait Rustina { + type Err: std::error::Error + Send; + type SendMessageTF: Request; + + /// For Telegram documentation see [`SendMessage`]. + fn send_message_tf(&self, chat_id: C, text: T, message: &Message) -> Self::SendMessageTF + where + C: Into, + T: Into; +} + +impl Rustina for Bot { + type Err = teloxide::errors::RequestError; + type SendMessageTF = JsonRequest; + + fn send_message_tf(&self, chat_id: C, text: T, message: &Message) -> Self::SendMessageTF + where + C: Into, + T: Into, + { + match message.thread_id { + Some(thread_id) => { + Self::SendMessageTF::new(self.clone(), payloads::SendMessage::new(chat_id, text)) + .message_thread_id(thread_id) + } + None => { + Self::SendMessageTF::new(self.clone(), payloads::SendMessage::new(chat_id, text)) + } + } + } +} + +// Delete a message after a certain time +pub async fn delete_timer(bot: &Bot, message: &Message, timer: u64) -> ResponseResult<()> { + let bot = bot.clone(); + let message = message.clone(); + + tokio::spawn(async move { + tokio::time::sleep(tokio::time::Duration::from_secs(timer)).await; + match bot.delete_message(message.chat.id, message.id).await { + Ok(_) => {} + Err(_) => {} + }; + }); + + Ok(()) +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 0000000..1865a24 --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1,16 @@ +pub mod github; +pub mod groups; +pub mod inlines; +pub mod keyboard; +pub mod message; +pub mod resources; + +pub fn cargo_like_log(title: &str, message: &str) { + println!( + "{}\x1b[1;32m{}\x1b[0m {} {}", + " ".repeat(12 - title.len()), + title, + message, + " ".repeat(8) + ); +} diff --git a/src/utils/resources.rs b/src/utils/resources.rs new file mode 100644 index 0000000..a06536a --- /dev/null +++ b/src/utils/resources.rs @@ -0,0 +1,42 @@ +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +static RESOURCE: &str = include_str!("../../data/source.json"); + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct Resource { + pub name: String, + pub desc: String, + pub link: String, +} + +#[derive(Clone, Debug)] +pub struct Resources { + materials: HashMap>, +} + +impl Default for Resources { + fn default() -> Self { + Self::new() + } +} + +impl Resources { + pub fn new() -> Self { + Resources { + materials: serde_json::from_str(RESOURCE).unwrap(), + } + } + + pub fn get_keys(&self) -> Vec { + self.materials.keys().map(|k| k.to_string()).collect() + } + + pub fn get_materials(&self, key: &str) -> Option<&Vec> { + self.materials.get(key) + } + + pub fn get_material(&self, key: &str, index: usize) -> Option<&Resource> { + self.materials.get(key).and_then(|v| v.get(index)) + } +} diff --git a/types/Github.d.ts b/types/Github.d.ts deleted file mode 100644 index b4f5f38..0000000 --- a/types/Github.d.ts +++ /dev/null @@ -1,55 +0,0 @@ -export interface Reactions { - url: string; - total_count: number; - "+1": number; - "-1": number; - laugh: number; - hooray: number; - confused: number; - heart: number; - rocket: number; - eyes: number; -} - -export interface ReleaseAuthor { - "login": string; - "id": number; - "node_id": string; - "avatar_url": string; - "gravatar_id": string; - "url": string; - "html_url": string; - "followers_url": string; - "following_url": string; - "gists_url": string; - "starred_url": string; - "subscriptions_url": string; - "organizations_url": string; - "repos_url": string; - "events_url": string; - "received_events_url": string; - "type": string; - "site_admin": boolean; -} - -export interface Release { - "url": string; - "assets_url": string; - "upload_url": string; - "html_url": string; - "id": number; - "author": ReleaseAuthor; - "node_id": string; - "tag_name": string; - "target_commitish": string; - "name": string; - "draft": boolean; - "prerelease": boolean; - "created_at": string | Date; - "published_at": string | Date; - "assets": []; - "tarball_url": string; - "zipball_url": string; - "body": string; - "reactions": Reactions; -} diff --git a/utils/checker.ts b/utils/checker.ts deleted file mode 100644 index 66d37fc..0000000 --- a/utils/checker.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { editor } from "../core.ts"; -import { Page } from "https://deno.land/x/telegraph@v1.0.2/src/types.ts"; -import { parseMarkdown } from "https://deno.land/x/telegraph@v1.0.2/src/parse.ts"; - -export const hecker = async ( - version: string, - content: string, -): Promise => { - const existing = (await editor.getPages()); - - for (const some of existing.pages) { - if (some.title === `Rust ${version}`) { - return some; - } - } - - return await editor.create({ - title: `Rust ${version}`, - content: parseMarkdown(content) || - "Oops, something happened while fetching data", - }); -}; - -export default hecker; diff --git a/utils/config.ts b/utils/config.ts deleted file mode 100644 index 0116f32..0000000 --- a/utils/config.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { config } from "../deps.ts"; - -const inits = () => { - if (Deno.env.get("MODE") === "NOFS") { - return Deno.env.toObject(); - } else { - return config(); - } -}; - -const dots = inits(); - -export default dots; diff --git a/utils/encoder.ts b/utils/encoder.ts deleted file mode 100644 index b945090..0000000 --- a/utils/encoder.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default (input: string) => - input.replaceAll(/[\u00A0-\u9999<>\&]/g, (i) => { - return "&#" + i.charCodeAt(0) + ";"; - }); diff --git a/utils/generator.ts b/utils/generator.ts deleted file mode 100644 index e151ef6..0000000 --- a/utils/generator.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { Release } from "../types/Github.d.ts"; - -export const finder = async (id: number) => { - const request = await fetch( - `https://api.github.com/repos/rust-lang/rust/releases/${id}`, - ); - - return await request.json() as Release; -}; - -export const pager = async (page: number, size = 5) => { - const request = await fetch( - `https://api.github.com/repos/rust-lang/rust/releases?per_page=${size}&page=${page}`, - ); - - return await request.json() as Release[]; -}; - -export const last = async () => { - const request = await fetch( - `https://api.github.com/repos/rust-lang/rust/releases/latest`, - ); - - return await request.json() as Release; -}; diff --git a/utils/pager.ts b/utils/pager.ts deleted file mode 100644 index 11b8e0f..0000000 --- a/utils/pager.ts +++ /dev/null @@ -1,8 +0,0 @@ -import communities from "../communities.json" assert { type: "json" }; - -export default (page_number: number, page_size = 5) => { - return communities.slice( - (page_number - 1) * page_size, - page_number * page_size, - ); -}; diff --git a/utils/playground.ts b/utils/playground.ts deleted file mode 100644 index 2b8ade2..0000000 --- a/utils/playground.ts +++ /dev/null @@ -1,25 +0,0 @@ -export interface Compiler { - success: boolean; - stdout: string; - stderr: string; -} - -export default async (code: string): Promise => { - const request = await fetch("https://play.rust-lang.org/execute", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - channel: "stable", - mode: "debug", - edition: "2021", - crateType: "bin", - tests: false, - code: code.trim(), - backtrace: false, - }), - }); - - return await request.json() as Compiler; -};