From 763288866c92ebca1658151202e92c10fb7a61de Mon Sep 17 00:00:00 2001 From: Artyom Belousov Date: Fri, 5 Jan 2024 22:14:57 +0300 Subject: [PATCH] Rewrite it in rust --- .github/workflows/deploy.yml | 18 +- .gitignore | 11 +- Cargo.lock | 1985 ++++++++ Cargo.toml | 17 + Dockerfile | 30 +- cmd/bot/main.go | 142 - go.mod | 24 - go.sum | 280 -- internal/caching/cache.go | 108 - internal/cardsinfo/card.go | 23 - internal/cardsinfo/fetcher.go | 4 - internal/cardsinfo/names.go | 51 - internal/cardsinfo/names_test.go | 62 - internal/cardsinfo/prices.go | 48 - internal/cardsinfo/prices_test.go | 106 - .../cardsinfo/test_data/AcademyRuinsTest.html | 3005 ------------ internal/cardsinfo/test_data/EmptyTest.html | 356 -- internal/cardsinfo/test_data/NonCards.html | 4139 ----------------- internal/scenario/scenario.go | 91 - internal/scenario/scenario_test.go | 83 - internal/scenario/test_cache.go | 29 - internal/scenario/test_ctx.go | 39 - internal/scenario/test_info_fetcher.go | 27 - internal/scenario/test_sender.go | 26 - internal/telegram/handler.go | 33 - internal/telegram/sender.go | 41 - internal/telegram/sender_test.go | 31 - internal/vk/format.go | 22 - internal/vk/format_test.go | 19 - internal/vk/handler.go | 70 - internal/vk/handler_test.go | 38 - internal/vk/sender.go | 64 - internal/vk/sender_test.go | 65 - internal/vk/test_ctx.go | 35 - justfile | 4 + src/main.rs | 467 ++ 36 files changed, 2506 insertions(+), 9087 deletions(-) create mode 100644 Cargo.lock create mode 100644 Cargo.toml delete mode 100644 cmd/bot/main.go delete mode 100644 go.mod delete mode 100644 go.sum delete mode 100644 internal/caching/cache.go delete mode 100644 internal/cardsinfo/card.go delete mode 100644 internal/cardsinfo/fetcher.go delete mode 100644 internal/cardsinfo/names.go delete mode 100644 internal/cardsinfo/names_test.go delete mode 100644 internal/cardsinfo/prices.go delete mode 100644 internal/cardsinfo/prices_test.go delete mode 100644 internal/cardsinfo/test_data/AcademyRuinsTest.html delete mode 100644 internal/cardsinfo/test_data/EmptyTest.html delete mode 100644 internal/cardsinfo/test_data/NonCards.html delete mode 100644 internal/scenario/scenario.go delete mode 100644 internal/scenario/scenario_test.go delete mode 100644 internal/scenario/test_cache.go delete mode 100644 internal/scenario/test_ctx.go delete mode 100644 internal/scenario/test_info_fetcher.go delete mode 100644 internal/scenario/test_sender.go delete mode 100644 internal/telegram/handler.go delete mode 100644 internal/telegram/sender.go delete mode 100644 internal/telegram/sender_test.go delete mode 100644 internal/vk/format.go delete mode 100644 internal/vk/format_test.go delete mode 100644 internal/vk/handler.go delete mode 100644 internal/vk/handler_test.go delete mode 100644 internal/vk/sender.go delete mode 100644 internal/vk/sender_test.go delete mode 100644 internal/vk/test_ctx.go create mode 100644 justfile create mode 100644 src/main.rs diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 382ffd2..9a07c68 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,5 +1,6 @@ name: Push To Yandex Cloud CR on: + pull_request: push: branches: - 'master' @@ -29,6 +30,9 @@ jobs: - name: Deploy Serverless Container id: deploy-sls-container uses: yc-actions/yc-sls-container-deploy@v2 + env: + secret: e6qb3muh98m0as2r1r4b + revision: e6q2tsf58jo5qtpjqkf5 with: yc-sa-json-credentials: ${{ secrets.YC_SA_JSON_CREDENTIALS }} container-name: mtg-price-bot @@ -40,14 +44,14 @@ jobs: revision-concurrency: 1 revision-image-url: cr.yandex/crp7gp50nf1pitkpk3hq/mtg-price-bot:${{ github.sha }} revision-execution-timeout: 10 - revision-env: | - YDB_CONNECTION_STRING=grpcs://ydb.serverless.yandexcloud.net:2135/ru-central1/b1gofbt3bqdgf7fash84/etnp6sprstj3c6k8n34s revision-secrets: | - TG_TOKEN=e6qb3muh98m0as2r1r4b/e6q6lnhd66itrki6ao0s/TG_TOKEN - VK_SECRET_KEY=e6qb3muh98m0as2r1r4b/e6q6lnhd66itrki6ao0s/VK_SECRET_KEY - VK_GROUP_ID=e6qb3muh98m0as2r1r4b/e6q6lnhd66itrki6ao0s/VK_GROUP_ID - VK_TOKEN=e6qb3muh98m0as2r1r4b/e6q6lnhd66itrki6ao0s/VK_TOKEN - VK_CONFIRMATION_STRING=e6qb3muh98m0as2r1r4b/e6q6lnhd66itrki6ao0s/VK_CONFIRMATION_STRING + TELOXIDE_TOKEN=$secret/$revision/TELOXIDE_TOKEN + VK_TOKEN=$secret/$revision/VK_TOKEN + VK_GROUP_ID=$secret/$revision/VK_GROUP_ID + VK_SECRET=$secret/$revision/VK_SECRET + VK_CONFIRMATION_STRING=$secret/$revision/VK_CONFIRMATION_STRING + TG_SECRET=$secret/$revision/TG_SECRET + SCG_CLIENT_GUID=$secret/$revision/SCG_CLIENT_GUID - name: Deploy API Gateway id: deploy-gateway diff --git a/.gitignore b/.gitignore index 8172dbf..fedaa2b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,2 @@ -vendor -*.swp -hosts -.idea -.venv -coverage.out -.envrc -/my_token -/bot \ No newline at end of file +/target +.env diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..d60b1df --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1985 @@ +# 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 = "anyhow" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" + +[[package]] +name = "aquamarine" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a941c39708478e8eea39243b5983f1c42d2717b3620ee91f4a52115fd02ac43f" +dependencies = [ + "itertools 0.9.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "async-trait" +version = "0.1.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.47", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "axum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d09dbe0e490df5da9d69b36dca48a76635288a82f92eca90024883a56202026d" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.1.0", + "hyper-util", + "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", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e87c8503f93e6d144ee5690907ba22db7ba79ab001a932ab99034f0fe836b3df" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[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", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.48.5", +] + +[[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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[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 = "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 = "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 = "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.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.47", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[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 0.2.11", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d308f63daf4181410c242d34c11f928dcb3aa105852019e043c9d1f4e4368a" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 1.0.0", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[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" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.11", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http 1.0.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" +dependencies = [ + "bytes", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "httpstatus" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbcf281a2062fc93deb46c99bd072cda6888a560c8a4f223d6824c1128832002" +dependencies = [ + "serde", +] + +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.22", + "http 0.2.11", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.0", + "http 1.0.0", + "http-body 1.0.0", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper 0.14.28", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "hyper-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdea9aac0dbe5a9240d68cfd9501e2db94222c6dc06843e06640b9e07f0fdc67" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "hyper 1.1.0", + "pin-project-lite", + "socket2", + "tokio", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +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.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +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 = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + +[[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.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[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.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[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.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "mtg-price-bot" +version = "0.1.0" +dependencies = [ + "anyhow", + "axum", + "regex", + "reqwest", + "scryfall", + "serde", + "serde_json", + "teloxide", + "tokio", +] + +[[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-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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl" +version = "0.10.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" +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.47", +] + +[[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.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[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.47", +] + +[[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.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" + +[[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.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[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.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.22", + "http 0.2.11", + "http-body 0.4.6", + "hyper 0.14.28", + "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 = "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.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[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.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scryfall" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cd4eeb4ef853b9a5de6ed255a1d91820ee7659f3010e756a0b25ddac9d9d7c6" +dependencies = [ + "async-trait", + "cfg-if", + "chrono", + "futures", + "heck", + "httpstatus", + "itertools 0.10.5", + "once_cell", + "percent-encoding", + "reqwest", + "serde", + "serde_json", + "serde_urlencoded", + "static_assertions", + "thiserror", + "tinyvec", + "url", + "uuid", +] + +[[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.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" + +[[package]] +name = "serde" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.47", +] + +[[package]] +name = "serde_json" +version = "1.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd154a240de39fdebcf5775d2675c204d7c13cf39a4c697be6493c8e734337c" +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 = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[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.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1726efe18f42ae774cc644f330953a5e7b3c3003d3edcecf18850fe9d4dd9afb" +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", + "bytes", + "derive_more", + "dptree", + "futures", + "log", + "mime", + "pin-project", + "serde", + "serde_json", + "serde_with_macros", + "teloxide-core", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "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 = "tempfile" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "thiserror" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.47", +] + +[[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.35.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[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.47", +] + +[[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-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", + "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-core", +] + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[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.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" + +[[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 = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "uuid" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +dependencies = [ + "getrandom", + "serde", +] + +[[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.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.47", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.47", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + +[[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.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[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_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[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_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[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_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[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_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e4006a3 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "mtg-price-bot" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.79" +axum = "0.7.3" +regex = "1.10.2" +reqwest = "0.11.23" +scryfall = "0.14.2" +serde = { version = "1.0.194", features = ["derive"] } +serde_json = "1.0.111" +teloxide = "0.12.2" +tokio = { version = "1.35.1", features = ["full"] } diff --git a/Dockerfile b/Dockerfile index 0b5aef4..11e5829 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,21 @@ -FROM golang:1.20.4-alpine3.18 -COPY . /go/src/go-mtg-vk -WORKDIR /go/src/go-mtg-vk -RUN go build ./cmd/bot - -FROM alpine:3.18 -RUN mkdir /app +FROM lukemathwalker/cargo-chef:latest-rust-1 AS chef WORKDIR /app -COPY --from=0 /go/src/go-mtg-vk/bot . -ENV GIN_MODE=release -ENTRYPOINT ./bot \ No newline at end of file + +FROM chef AS planner +COPY . . +RUN cargo chef prepare --recipe-path recipe.json + +FROM chef AS builder +COPY --from=planner /app/recipe.json recipe.json +# Build dependencies - this is the caching Docker layer! +RUN cargo chef cook --release --recipe-path recipe.json +# Build application +COPY . . +RUN cargo build --release --bin mtg-price-bot + +# We do not need the Rust toolchain to run the binary! +FROM debian:bookworm-slim AS runtime +RUN apt-get update && apt-get -y install libssl3 ca-certificates && rm -rf /var/lib/apt/lists/* +WORKDIR /app +COPY --from=builder /app/target/release/mtg-price-bot /usr/local/bin +ENTRYPOINT ["/usr/local/bin/mtg-price-bot"] diff --git a/cmd/bot/main.go b/cmd/bot/main.go deleted file mode 100644 index 1252dd4..0000000 --- a/cmd/bot/main.go +++ /dev/null @@ -1,142 +0,0 @@ -package main - -import ( - "context" - "fmt" - "log" - "math/rand" - "os" - "strconv" - "time" - - "gitlab.com/flygrounder/go-mtg-vk/internal/caching" - "gitlab.com/flygrounder/go-mtg-vk/internal/cardsinfo" - "gitlab.com/flygrounder/go-mtg-vk/internal/scenario" - "gitlab.com/flygrounder/go-mtg-vk/internal/telegram" - - "github.com/gin-gonic/gin" - tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" - environ "github.com/ydb-platform/ydb-go-sdk-auth-environ" - "github.com/ydb-platform/ydb-go-sdk/v3" - "gitlab.com/flygrounder/go-mtg-vk/internal/vk" -) - -type config struct { - tgToken string - vkGroupId int64 - vkSecretKey string - vkToken string - vkConfirmationString string - ydbConnectionString string - port string -} - -func getConfig() *config { - var cfg config - var exists bool - var err error - - cfg.tgToken, exists = os.LookupEnv("TG_TOKEN") - if !exists { - panic("TG_TOKEN environment variable not defined") - } - - vkGroupId, exists := os.LookupEnv("VK_GROUP_ID") - if !exists { - panic("VK_GROUP_ID environment variable not defined") - } - cfg.vkGroupId, err = strconv.ParseInt(vkGroupId, 10, 64) - if err != nil { - panic("VK_GROUP_ID is not a number") - } - - cfg.vkSecretKey, exists = os.LookupEnv("VK_SECRET_KEY") - if !exists { - panic("VK_SECRET_KEY environment variable not defined") - } - - cfg.vkToken, exists = os.LookupEnv("VK_TOKEN") - if !exists { - panic("VK_TOKEN environment variable not defined") - } - - cfg.vkConfirmationString, exists = os.LookupEnv("VK_CONFIRMATION_STRING") - if !exists { - panic("VK_CONFIRMATION_STRING environment variable not defined") - } - - cfg.ydbConnectionString, exists = os.LookupEnv("YDB_CONNECTION_STRING") - if !exists { - panic("YDB_CONNECTION_STRING environment variable not defined") - } - - cfg.port, exists = os.LookupEnv("PORT") - if !exists { - panic("PORT environment variable not defined") - } - - return &cfg -} - -func main() { - rand.Seed(time.Now().UTC().UnixNano()) - - cfg := getConfig() - - bot, _ := tgbotapi.NewBotAPI(cfg.tgToken) - r := gin.Default() - - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - - db, err := ydb.Open(ctx, - cfg.ydbConnectionString, - environ.WithEnvironCredentials(ctx), - ) - if err != nil { - panic(fmt.Errorf("connect error: %w", err)) - } - defer func() { _ = db.Close(ctx) }() - - cache := &caching.CacheClient{ - Storage: db.Table(), - Expiration: 12 * time.Hour, - Prefix: db.Name(), - } - err = cache.Init(context.Background()) - if err != nil { - panic(fmt.Errorf("init error: %w", err)) - } - - logger := log.New(os.Stdout, "", 0) - fetcher := &cardsinfo.Fetcher{} - handler := vk.Handler{ - Scenario: &scenario.Scenario{ - Sender: &vk.ApiSender{ - Token: cfg.vkToken, - Logger: logger, - }, - Logger: logger, - InfoFetcher: fetcher, - Cache: cache, - }, - SecretKey: cfg.vkSecretKey, - GroupId: cfg.vkGroupId, - ConfirmationString: cfg.vkConfirmationString, - } - - tgHandler := telegram.Handler{ - Scenario: &scenario.Scenario{ - Sender: &telegram.Sender{ - API: bot, - }, - Logger: logger, - InfoFetcher: fetcher, - Cache: cache, - }, - } - - r.POST("vk", handler.HandleMessage) - r.POST("tg", tgHandler.HandleMessage) - _ = r.Run(":" + cfg.port) -} diff --git a/go.mod b/go.mod deleted file mode 100644 index a05ecba..0000000 --- a/go.mod +++ /dev/null @@ -1,24 +0,0 @@ -module gitlab.com/flygrounder/go-mtg-vk - -go 1.12 - -require ( - github.com/antchfx/htmlquery v1.2.3 - github.com/gin-gonic/gin v1.4.0 - github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible - github.com/golang/protobuf v1.5.3 // indirect - github.com/google/go-cmp v0.5.9 // indirect - github.com/kr/pretty v0.3.0 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect - github.com/pkg/errors v0.9.1 - github.com/rogpeppe/go-internal v1.9.0 // indirect - github.com/stretchr/testify v1.8.1 - github.com/technoweenie/multipartstreamer v1.0.1 // indirect - github.com/ydb-platform/ydb-go-sdk-auth-environ v0.1.3 - github.com/ydb-platform/ydb-go-sdk/v3 v3.47.2 - golang.org/x/net v0.10.0 // indirect - google.golang.org/protobuf v1.30.0 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect - gopkg.in/h2non/gock.v1 v1.0.16 - gopkg.in/yaml.v2 v2.2.4 // indirect -) diff --git a/go.sum b/go.sum deleted file mode 100644 index e8f26b2..0000000 --- a/go.sum +++ /dev/null @@ -1,280 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/antchfx/htmlquery v1.2.3 h1:sP3NFDneHx2stfNXCKbhHFo8XgNjCACnU/4AO5gWz6M= -github.com/antchfx/htmlquery v1.2.3/go.mod h1:B0ABL+F5irhhMWg54ymEZinzMSi0Kt3I2if0BLYa3V0= -github.com/antchfx/xpath v1.1.6 h1:6sVh6hB5T6phw1pFpHRQ+C4bd8sNI+O58flqtg7h0R0= -github.com/antchfx/xpath v1.1.6/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g= -github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= -github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ= -github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= -github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU= -github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM= -github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kUcQ= -github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= -github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= -github.com/jonboulle/clockwork v0.3.0 h1:9BSCMi8C+0qdApAp4auwX0RkLGUjs956h0EkuQymUhg= -github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4= -github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rekby/fixenv v0.3.2/go.mod h1:/b5LRc06BYJtslRtHKxsPWFT/ySpHV+rWvzTg+XWk4c= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM= -github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= -github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/yandex-cloud/go-genproto v0.0.0-20211115083454-9ca41db5ed9e h1:9LPdmD1vqadsDQUva6t2O9MbnyvoOgo8nFNPaOIH5U8= -github.com/yandex-cloud/go-genproto v0.0.0-20211115083454-9ca41db5ed9e/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE= -github.com/ydb-platform/ydb-go-genproto v0.0.0-20220203104745-929cf9c248bc/go.mod h1:cc138nptTn9eKptCQl/grxP6pBKpo/bnXDiOxuVZtps= -github.com/ydb-platform/ydb-go-genproto v0.0.0-20221215182650-986f9d10542f h1:BBczNIM1MJHT7XkIUA8pThXWxJvxoBjcWvne3xwe2RI= -github.com/ydb-platform/ydb-go-genproto v0.0.0-20221215182650-986f9d10542f/go.mod h1:Er+FePu1dNUieD+XTMDduGpQuCPssK5Q4BjF+IIXJ3I= -github.com/ydb-platform/ydb-go-sdk-auth-environ v0.1.3 h1:+DrFgi0hjjLarFcWHwI6WPk28hcWr6N7ga+OHAxtemI= -github.com/ydb-platform/ydb-go-sdk-auth-environ v0.1.3/go.mod h1:K5wHHoLBfmWc7zyiETOA1Spx7DN+4skQ7YeWy5snXKo= -github.com/ydb-platform/ydb-go-sdk/v3 v3.25.3/go.mod h1:PFizF/vJsdAgEwjK3DVSBD52kdmRkWfSIS2q2pA+e88= -github.com/ydb-platform/ydb-go-sdk/v3 v3.47.1/go.mod h1:oSLwnuilwIpaF5bJJMAofnGgzPJusoI3zWMNb8I+GnM= -github.com/ydb-platform/ydb-go-sdk/v3 v3.47.2 h1:nBPXs27hwve26x2DIec45IO0EnxZiB63SwTIPanre/4= -github.com/ydb-platform/ydb-go-sdk/v3 v3.47.2/go.mod h1:oSLwnuilwIpaF5bJJMAofnGgzPJusoI3zWMNb8I+GnM= -github.com/ydb-platform/ydb-go-yc v0.10.2 h1:RAHy6g7ncxk1y0N4oS2MwYXLATqRqKBI6DYXuxpV2wo= -github.com/ydb-platform/ydb-go-yc v0.10.2/go.mod h1:U1dX3LJy6zADId2DciCXlgrU/vphK1+CQzaefKq21dQ= -github.com/ydb-platform/ydb-go-yc-metadata v0.5.2 h1:nMtixUijP0Z7iHJNT9fOL+dbmEzZxqU6Xk87ll7hqXg= -github.com/ydb-platform/ydb-go-yc-metadata v0.5.2/go.mod h1:82SQ4L3PewiEmFW4oTMc1sfPjODasIYxD/SKGsbK74s= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3 h1:SeX3QUcBj3fciwnfPT9kt5gBhFy/FCZtYZ+I/RB8agc= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= -google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= -gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= -gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= -gopkg.in/h2non/gentleman.v1 v1.0.4/go.mod h1:JYuHVdFzS4MKOXe0o+chKJ4hCe6tqKKw9XH9YP6WFrg= -gopkg.in/h2non/gock.v1 v1.0.16 h1:F11k+OafeuFENsjei5t2vMTSTs9L62AdyTe4E1cgdG8= -gopkg.in/h2non/gock.v1 v1.0.16/go.mod h1:XVuDAssexPLwgxCLMvDTWNU5eqklsydR6I5phZ9oPB8= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/caching/cache.go b/internal/caching/cache.go deleted file mode 100644 index d34b184..0000000 --- a/internal/caching/cache.go +++ /dev/null @@ -1,108 +0,0 @@ -package caching - -import ( - "context" - "encoding/json" - "fmt" - "path" - "time" - - "github.com/pkg/errors" - "github.com/ydb-platform/ydb-go-sdk/v3/table" - "github.com/ydb-platform/ydb-go-sdk/v3/table/options" - "github.com/ydb-platform/ydb-go-sdk/v3/table/result" - "github.com/ydb-platform/ydb-go-sdk/v3/table/result/named" - "github.com/ydb-platform/ydb-go-sdk/v3/table/types" - "gitlab.com/flygrounder/go-mtg-vk/internal/cardsinfo" -) - -type CacheClient struct { - Storage table.Client - Expiration time.Duration - Prefix string -} - -func (client *CacheClient) Init(ctx context.Context) error { - return client.Storage.Do(ctx, func(ctx context.Context, s table.Session) error { - return s.CreateTable( - ctx, - path.Join(client.Prefix, "cache"), - options.WithColumn("card", types.TypeString), - options.WithColumn("prices", types.Optional(types.TypeJSON)), - options.WithColumn("created_at", types.Optional(types.TypeTimestamp)), - options.WithTimeToLiveSettings( - options.NewTTLSettings().ColumnDateType("created_at").ExpireAfter(client.Expiration), - ), - options.WithPrimaryKeyColumn("card"), - ) - }) -} - -func (client *CacheClient) Set(ctx context.Context, key string, prices []cardsinfo.ScgCardPrice) error { - const query = ` - DECLARE $cacheData AS List>; - - INSERT INTO cache SELECT cd.card AS card, cd.prices AS prices, cd.created_at AS created_at FROM AS_TABLE($cacheData) cd LEFT OUTER JOIN cache c ON cd.card = c.card WHERE c.card IS NULL` - value, _ := json.Marshal(prices) - return client.Storage.Do(ctx, func(ctx context.Context, s table.Session) (err error) { - _, _, err = s.Execute(ctx, writeTx(), query, table.NewQueryParameters( - table.ValueParam("$cacheData", types.ListValue( - types.StructValue( - types.StructFieldValue("card", types.StringValueFromString(key)), - types.StructFieldValue("prices", types.JSONValueFromBytes(value)), - types.StructFieldValue("created_at", types.TimestampValueFromTime(time.Now())), - ))), - )) - return - }) -} - -func (client *CacheClient) Get(ctx context.Context, key string) ([]cardsinfo.ScgCardPrice, error) { - const query = ` - DECLARE $card AS String; - - SELECT UNWRAP(prices) AS prices FROM cache WHERE card = $card` - var pricesStr string - var res result.Result - err := client.Storage.Do(ctx, func(ctx context.Context, s table.Session) (err error) { - _, res, err = s.Execute(ctx, readTx(), query, table.NewQueryParameters( - table.ValueParam("$card", types.StringValueFromString(key)), - )) - return - }) - if err != nil { - return nil, errors.Wrap(err, "Failed to get key") - } - ok := res.NextResultSet(ctx) - if !ok { - return nil, errors.New("no key") - } - ok = res.NextRow() - if !ok { - return nil, errors.New("no key") - } - err = res.ScanNamed( - named.Required("prices", &pricesStr), - ) - if err != nil { - return nil, fmt.Errorf("failed to scan prices: %w", err) - } - var prices []cardsinfo.ScgCardPrice - _ = json.Unmarshal([]byte(pricesStr), &prices) - return prices, nil -} - -func writeTx() *table.TransactionControl { - return table.TxControl(table.BeginTx( - table.WithSerializableReadWrite(), - ), table.CommitTx()) -} - -func readTx() *table.TransactionControl { - return table.TxControl(table.BeginTx( - table.WithOnlineReadOnly(), - ), table.CommitTx()) -} diff --git a/internal/cardsinfo/card.go b/internal/cardsinfo/card.go deleted file mode 100644 index a7b9748..0000000 --- a/internal/cardsinfo/card.go +++ /dev/null @@ -1,23 +0,0 @@ -package cardsinfo - -import ( - "strings" -) - -type ScgCardPrice struct { - Price string - Edition string - Link string -} - -type card struct { - Name string `json:"name"` - Layout string `json:"layout"` -} - -func (c *card) getName() string { - if c.Layout == "transform" { - return strings.Replace(c.Name, "//", "|", 1) - } - return c.Name -} diff --git a/internal/cardsinfo/fetcher.go b/internal/cardsinfo/fetcher.go deleted file mode 100644 index c66b357..0000000 --- a/internal/cardsinfo/fetcher.go +++ /dev/null @@ -1,4 +0,0 @@ -package cardsinfo - -type Fetcher struct { -} diff --git a/internal/cardsinfo/names.go b/internal/cardsinfo/names.go deleted file mode 100644 index 4ff67cb..0000000 --- a/internal/cardsinfo/names.go +++ /dev/null @@ -1,51 +0,0 @@ -package cardsinfo - -import ( - "encoding/json" - "io/ioutil" - "net/http" - "net/url" - "strings" -) - -const scryfallUrl = "https://api.scryfall.com" - -func (f *Fetcher) GetNameByCardId(set string, number string) string { - /* - Note: number is string because some cards contain letters in their numbers. - */ - path := scryfallUrl + "/cards/" + strings.ToLower(set) + "/" + number - return getCardByUrl(path) -} - -func (f *Fetcher) GetOriginalName(name string) string { - path := scryfallUrl + "/cards/named?fuzzy=" + applyFilters(name) - result := getCardByUrl(path) - return result -} - -func applyFilters(name string) string { - /* - Despite of the rules of Russian language, letter ё is replaced with e on cards - Sometimes it leads to wrong search results - */ - name = strings.ReplaceAll(name, "ё", "е") - return url.QueryEscape(name) -} - -func getCardByUrl(path string) string { - response, err := http.Get(path) - if err != nil { - return "" - } - defer func() { - _ = response.Body.Close() - }() - data, _ := ioutil.ReadAll(response.Body) - var v card - err = json.Unmarshal(data, &v) - if err != nil { - return "" - } - return v.getName() -} diff --git a/internal/cardsinfo/names_test.go b/internal/cardsinfo/names_test.go deleted file mode 100644 index 4855929..0000000 --- a/internal/cardsinfo/names_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package cardsinfo - -import ( - "errors" - "net/http" - "testing" - - "github.com/stretchr/testify/assert" - "gopkg.in/h2non/gock.v1" -) - -func TestGetNameByCardId(t *testing.T) { - defer gock.Off() - - gock.New(scryfallUrl + "/set/1").Reply(http.StatusOK).JSON(card{ - Name: "card", - }) - f := &Fetcher{} - name := f.GetNameByCardId("set", "1") - assert.Equal(t, "card", name) -} - -func TestGetOriginalName_Scryfall(t *testing.T) { - defer gock.Off() - - gock.New(scryfallUrl + "/cards/named?fuzzy=card").Reply(http.StatusOK).JSON(card{ - Name: "Result Card", - }) - f := &Fetcher{} - name := f.GetOriginalName("card") - assert.Equal(t, "Result Card", name) -} - -func TestGetOriginalName_BadJson(t *testing.T) { - defer gock.Off() - - gock.New(scryfallUrl + "/cards/named?fuzzy=card").Reply(http.StatusOK).BodyString("}") - f := &Fetcher{} - name := f.GetOriginalName("card") - assert.Equal(t, "", name) -} - -func TestGetOriginalName_DoubleSide(t *testing.T) { - defer gock.Off() - - gock.New(scryfallUrl + "/cards/named?fuzzy=card").Reply(http.StatusOK).JSON(card{ - Name: "Legion's Landing // Adanto, the First Fort", - Layout: "transform", - }) - f := &Fetcher{} - name := f.GetOriginalName("card") - assert.Equal(t, "Legion's Landing | Adanto, the First Fort", name) -} - -func TestGetOriginalName_Error(t *testing.T) { - defer gock.Off() - - gock.New(scryfallUrl + "/cards/named?fuzzy=card").ReplyError(errors.New("internal server error")) - f := &Fetcher{} - name := f.GetOriginalName("card") - assert.Equal(t, "", name) -} diff --git a/internal/cardsinfo/prices.go b/internal/cardsinfo/prices.go deleted file mode 100644 index a5709dd..0000000 --- a/internal/cardsinfo/prices.go +++ /dev/null @@ -1,48 +0,0 @@ -package cardsinfo - -import ( - "net/url" - "strings" - - "github.com/antchfx/htmlquery" - "github.com/pkg/errors" -) - -const scgDomain = "https://starcitygames.com" -const scgSearchUrlTemplate = "https://starcitygames.hawksearch.com/sites/starcitygames/?tournament_legality=Legal&search_query=" - -func (f *Fetcher) GetPrices(name string) ([]ScgCardPrice, error) { - prices, err := getPricesScg(name) - if err != nil { - return nil, err - } - if len(prices) > 5 { - return prices[:5], nil - } - return prices, nil -} - -func getPricesScg(name string) ([]ScgCardPrice, error) { - escapedName := url.QueryEscape(name) - searchUrl := scgSearchUrlTemplate + escapedName - node, err := htmlquery.LoadURL(searchUrl) - if err != nil { - return nil, errors.Wrap(err, "cannot load url") - } - blocks := htmlquery.Find(node, "//div[@class=\"hawk-results-item\"]") - var results []ScgCardPrice - for _, block := range blocks { - price := ScgCardPrice{} - linkNode := htmlquery.FindOne(block, "//h2/a") - price.Link = scgDomain + htmlquery.SelectAttr(linkNode, "href") - editionNode := htmlquery.FindOne(block, "//p[@class=\"hawk-results-item__category\"]/a") - if !strings.HasPrefix(htmlquery.SelectAttr(editionNode, "href"), "/shop/singles/") { - continue - } - price.Edition = editionNode.FirstChild.Data - priceNode := htmlquery.FindOne(block, "//span[@class='hawk-old-price']|//div[contains(concat(' ',normalize-space(@class),' '),' hawk-results-item__options-table-cell--price ')]") - price.Price = priceNode.FirstChild.Data - results = append(results, price) - } - return results, nil -} diff --git a/internal/cardsinfo/prices_test.go b/internal/cardsinfo/prices_test.go deleted file mode 100644 index 9f0e723..0000000 --- a/internal/cardsinfo/prices_test.go +++ /dev/null @@ -1,106 +0,0 @@ -package cardsinfo - -import ( - "net/http" - "os" - "testing" - - "gopkg.in/h2non/gock.v1" - - "github.com/stretchr/testify/assert" -) - -func TestGetPrices_Ok(t *testing.T) { - defer gock.Off() - - file, _ := os.Open("test_data/AcademyRuinsTest.html") - gock.New(scgSearchUrlTemplate + "card").Reply(http.StatusOK).Body(file) - f := &Fetcher{} - prices, err := f.GetPrices("card") - assert.Nil(t, err) - assert.Equal(t, []ScgCardPrice{ - { - Price: "$6.99", - Edition: "Double Masters", - Link: "https://starcitygames.com/academy-ruins-sgl-mtg-2xm-309-enn/?sku=SGL-MTG-2XM-309-ENN1", - }, - { - Price: "$9.99", - Edition: "Double Masters (Foil)", - Link: "https://starcitygames.com/academy-ruins-sgl-mtg-2xm-309-enf/?sku=SGL-MTG-2XM-309-ENF1", - }, - { - Price: "$11.99", - Edition: "Double Masters - Variants", - Link: "https://starcitygames.com/academy-ruins-sgl-mtg-2xm2-369-enn/?sku=SGL-MTG-2XM2-369-ENN1", - }, - { - Price: "$14.99", - Edition: "Double Masters - Variants (Foil)", - Link: "https://starcitygames.com/academy-ruins-sgl-mtg-2xm2-369-enf/?sku=SGL-MTG-2XM2-369-ENF1", - }, - { - Price: "$7.99", - Edition: "Modern Masters: 2013 Edition", - Link: "https://starcitygames.com/academy-ruins-sgl-mtg-mma-219-enn/?sku=SGL-MTG-MMA-219-ENN1", - }, - }, prices) -} - -func TestGetPrices_Unavailable(t *testing.T) { - defer gock.Off() - - gock.New(scgSearchUrlTemplate + "card").Reply(http.StatusBadGateway) - f := &Fetcher{} - _, err := f.GetPrices("card") - assert.NotNil(t, err) -} - -func TestGetPrices_Empty(t *testing.T) { - defer gock.Off() - - file, _ := os.Open("test_data/EmptyTest.html") - gock.New(scgSearchUrlTemplate + "card").Reply(http.StatusOK).Body(file) - f := &Fetcher{} - prices, err := f.GetPrices("card") - assert.Nil(t, err) - assert.Nil(t, prices) -} - -func TestGetPrices_FilterNonCards(t *testing.T) { - defer gock.Off() - - file, _ := os.Open("test_data/NonCards.html") - gock.New(scgSearchUrlTemplate + "card").Reply(http.StatusOK).Body(file) - f := &Fetcher{} - prices, err := f.GetPrices("card") - assert.Nil(t, err) - expected := []ScgCardPrice{ - { - Price: "$72.99", - Edition: "3rd Edition - Black Border", - Link: "https://starcitygames.com/sol-ring-sgl-mtg-3bb-274-frn/?sku=SGL-MTG-3BB-274-FRN3", - }, - { - Price: "$24.99", - Edition: "3rd Edition/Revised", - Link: "https://starcitygames.com/sol-ring-sgl-mtg-3ed-274-enn/?sku=SGL-MTG-3ED-274-ENN1", - }, - { - Price: "$1,999.99", - Edition: "Alpha", - Link: "https://starcitygames.com/sol-ring-sgl-mtg-lea-269-enn/?sku=SGL-MTG-LEA-269-ENN1", - }, - { - Price: "$1,199.99", - Edition: "Beta", - Link: "https://starcitygames.com/sol-ring-sgl-mtg-leb-270-enn/?sku=SGL-MTG-LEB-270-ENN1", - }, - { - Price: "$99.99", - Edition: "Collectors' Edition", - Link: "https://starcitygames.com/sol-ring-sgl-mtg-ced-270-enn/?sku=SGL-MTG-CED-270-ENN1", - }, - } - assert.Equal(t, expected, prices) -} diff --git a/internal/cardsinfo/test_data/AcademyRuinsTest.html b/internal/cardsinfo/test_data/AcademyRuinsTest.html deleted file mode 100644 index 83be959..0000000 --- a/internal/cardsinfo/test_data/AcademyRuinsTest.html +++ /dev/null @@ -1,3005 +0,0 @@ -
- -
-
Academy Ruins at Star City Games
-
-
-
-
- -
-
- -
- - - - - - -
- - - -
- - -
- -
- -
-
- - - - - - - - - -
-
-
- -
- Showing 1 - 8 of 8 -
-
- - -
- -
- -
- -
- - -
- -
- - - - - - Page 1 of 1 -
-
-
- - - - -
- - -
- -
- - - - - - - - -
-
- -
-
- -
-
-
- -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$5.94$6.99
-
QTY: 1
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - -
-
- -
-
- -
-
-
- -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$8.49$9.99
-
QTY: 13
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - -
- - -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$10.19$11.99
-
QTY: 12
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - -
- - -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$12.74$14.99
-
QTY: 0
-
- -
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - - - -
- - -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$6.79$7.99
-
QTY: 6
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Played - English - Flag - -
-
$5.82$6.85
-
QTY: 1
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - -
- - -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$13.59$15.99
-
QTY: 3
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - -
-
- -
-
- -
-
-
- -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$7.64$8.99
-
QTY: 4
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Played - English - Flag - -
-
$6.54$7.69
-
QTY: 39
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- -
- - -
- -
-
- Near Mint - Chinese - Simplified - Flag - -
-
$6.11$7.19
-
-
QTY: 3
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Played - Chinese - Simplified - Flag - -
-
$5.23$6.15
-
-
QTY: 4
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Near Mint - French - Flag - -
-
$5.74$6.75
-
-
QTY: 13
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Played - French - Flag - -
-
$4.92$5.79
-
-
QTY: 4
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Near Mint - Italian - Flag - -
-
$5.74$6.75
-
-
QTY: 7
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Played - Italian - Flag - -
-
$4.92$5.79
-
-
QTY: 8
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Near Mint - Portuguese - Flag - -
-
$5.74$6.75
-
-
QTY: 3
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Played - Portuguese - Flag - -
-
$4.92$5.79
-
-
QTY: 1
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Near Mint - Russian - Flag - -
-
$11.25 -
-
QTY: 3
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Played - Russian - Flag - -
-
$8.15$9.59
-
-
QTY: 5
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Near Mint - Spanish - Flag - -
-
$5.74$6.75
-
-
QTY: 5
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Played - Spanish - Flag - -
-
$4.92$5.79
-
-
QTY: 3
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - - -
-
-
-
- - - - -
-
- -
-
- -
-
-
- -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$63.74$74.99
-
QTY: 0
-
- -
- - -
- -
-
- -
-
- Played - English - Flag - -
-
$42.49$49.99
-
QTY: 3
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- -
- - -
- -
-
- Near Mint - French - Flag - -
-
$50.99$59.99
-
-
QTY: 1
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Played - French - Flag - -
-
$33.99$39.99
-
-
QTY: 2
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Played - Italian - Flag - -
-
$33.99$39.99
-
-
QTY: 3
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Played - Spanish - Flag - -
-
$33.99$39.99
-
-
QTY: 1
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - - -
-
-
-
- - - - - - - - - -
- -
- - -
-
-
- -
- Showing 1 - 8 of 8 -
-
- - -
- -
- -
- -
- - -
- -
- - - - - - Page 1 of 1 -
-
-
- - - - -
- - -
- -
- -
- -
- - -
- -
- -
- - - -
- - - - - - - - - - - - - - - - - - - - - - - - - -
- Narrow Results -
- -
-
- -
-

- In Stock Only - -

-
- - - - -
-
-
-

- Product Type - -

-
- - - - -
-
-
-

- Finish - -

-
- - - - -
-
-
-

- Condition - -

-
- - - - -
-
-
-

- Rarity - -

-
- - - - -
-
-
-

- Language - -

- -
-
-

- Color - -

-
- - - - -
-
- -
-

- Tournament Legality - -

-
- - - - -
-
- -
-
- -
- -
- - -
- - - - - - - -
-
- - - - - - - -
- - -
- - - - - - - -
-
- - - - - - - -
- - -
- - - - - - - -
-
- - - - - - - -
- - -
- - - - - - - -
-
- - - - - - - -
- - -
- - - - - - - -
-
- - - - - - - -
- - - - - -
-

Search Results for Academy Ruins

-
- - - - -
- diff --git a/internal/cardsinfo/test_data/EmptyTest.html b/internal/cardsinfo/test_data/EmptyTest.html deleted file mode 100644 index 556eeb8..0000000 --- a/internal/cardsinfo/test_data/EmptyTest.html +++ /dev/null @@ -1,356 +0,0 @@ -
- -
-
Nosuchcardever at Star City Games
-
-
-
-
- -
-
- -
- - - - - - -
- - - -
- - -
- -
- -
-
- - - - - - - - - -
- -
- - -
- Search was unable to find any results for "nosuchcardever", you may have typed your word incorrectly, or are being too specific. Try using a broader search phrase. -
- - - - -
- -
- - -
- -
- -
- -
- - -
- -
- -
- - - -
- - - - - - - - - - - - - - - - - - - - - - - - - -
- Narrow Results -
- -
-
- - -
-
- -
- -
- - -
- - - - - - - -
-
- - - - - - - -
- - -
- - - - - - - -
-
- - - - - - - -
- - - -
- - - - - - - -
- - -
- - - -

 - Please double check your spelling.
 - Try searching for an item that is less specific.
 - You can always narrow your search results later. 



- - - -
-
- - - - - - - -
- - -
- - - - - - -
-
- - - - -

- - Rathi Dragon -

- SGL-MTG-9ED-210-JAF -

-

- $19.99 - -

-
- -
- -
-
- View Details - - -
-
-
-
- - - -
-
- - - - -

- - Rathi Dragon -

- SGL-MTG-9ED-210-ZSF -

-

- $14.99 - -

-
- -
- -
-
- View Details - - -
-
-
-
- - - -
-
- - - - -

- - Rathi Dragon -

- SGL-MTG-9ED-210-ENN -

-

- $0.49 - -

-
- -
- -
-
- View Details - - -
-
-
-
- - - - - - - - - - - - - - - -
-
- - - - - - - -
- - - - - -
-

Search Results for Nosuchcardever

-
- - - - -
- diff --git a/internal/cardsinfo/test_data/NonCards.html b/internal/cardsinfo/test_data/NonCards.html deleted file mode 100644 index e04ecfd..0000000 --- a/internal/cardsinfo/test_data/NonCards.html +++ /dev/null @@ -1,4139 +0,0 @@ -
- -
-
Sol Ring at Star City Games
-
-
-
-
- -
-
- -
- - - - - - -
- - - -
- - -
- -
- -
-
- - - - - - - - - -
-
-
- -
- Showing 1 - 24 of 36 -
-
- - -
- -
- -
- -
- - -
- -
- - - 1 2 - - Next - - - Last - - Page 1 of 2 -
-
-
- - - - -
- - -
- -
- - - - - - - - -
- - -
-
-
- - - - -
-
-
-
-
- -
-
- - -
-
$39.99
-
QTY: 12
-
- -
- - - - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - -
- - -
-
-
- - - - -
-
-
-
-
- -
-
- Heavily Played - French - Flag - -
-
$72.99
-
QTY: 9
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Heavily Played - German - Flag - -
-
$72.99
-
QTY: 1
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- -
- - -
- -
-
- Played - Italian - Flag - -
-
$87.99 -
-
QTY: 1
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Heavily Played - Italian - Flag - -
-
$72.99 -
-
QTY: 2
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - - -
-
-
-
- - - - -
-
- -
-
- -
-
-
- -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$24.99
-
QTY: 1
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Played - English - Flag - -
-
$21.39
-
QTY: 18
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- -
- - -
- -
-
- Heavily Played - English - Flag - -
-
$12.49 -
-
QTY: 5
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - - -
-
-
-
- - - - -
-
-
-

Sol Ring

-

Alpha

-
-
-
- -
-
-
- -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$1,999.99
-
QTY: 0
-
- -
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - - - -
-
-
-

Sol Ring

-

Beta

-
-
-
- -
-
-
- -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$1,199.99
-
QTY: 0
-
- -
- - -
- -
-
- -
-
- Heavily Played - English - Flag - -
-
$749.99
-
QTY: 1
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - -
- - -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$99.99
-
QTY: 0
-
- -
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - -
-
- -
-
- -
-
-
- -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$3.39$3.99
-
QTY: 0
-
- -
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - -
- - -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$3.39$3.99
-
QTY: 0
-
- -
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - - - -
- - -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$4.24$4.99
-
QTY: 23
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - -
- - -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$21.24$24.99
-
QTY: 0
-
- -
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - -
- - -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$1.99
-
QTY: 2
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - -
- - -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$10.99
-
QTY: 0
-
- -
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - - - -
- - -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$39.99
-
QTY: 2
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - -
-
- -
-
- -
-
-
- -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$3.39$3.99
-
QTY: 9
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Played - English - Flag - -
-
$2.76$3.25
-
QTY: 1
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - -
-
- -
-
- -
-
-
- -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$3.39$3.99
-
QTY: 18
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Played - English - Flag - -
-
$2.76$3.25
-
QTY: 21
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - -
-
- -
-
- -
-
-
- -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$3.39$3.99
-
QTY: 54
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Played - English - Flag - -
-
$2.76$3.25
-
QTY: 18
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - - - -
-
- -
-
- -
-
-
- -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$3.39$3.99
-
QTY: 21
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
-
- Played - English - Flag - -
-
$2.76$3.25
-
QTY: 37
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - -
-
- -
-
- -
-
-
- -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$2.97$3.49
-
QTY: 35
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - -
-
- -
-
- -
-
-
- -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$2.97$3.49
-
QTY: 48
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - -
-
- -
-
- -
-
-
- -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$2.54$2.99
-
QTY: 97
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - - - -
-
- -
-
- -
-
-
- -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$2.54$2.99
-
QTY: 48
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - -
-
- -
-
- -
-
-
- -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$2.54$2.99
-
QTY: 98
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - -
-
- -
-
- -
-
-
- -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$2.12$2.49
-
QTY: 98
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - -
- - -
-
-
- - - - -
-
-
-
-
- -
-
- Near Mint - English - Flag - -
-
$39.99
-
QTY: 1
-
- -
- - - -
- -
- - -
-
- -
-
-
- -
- - - -
-
The qty you entered is invalid. Please try again.
-
-
- -
-
-
-
    -
  • - - -
  • -
-
-
-
-
- - -
- -
-
- -
- - -
- -
- - - -
-
-
-
- - - - - - - - - -
- -
- - -
-
-
- -
- Showing 1 - 24 of 36 -
-
- - -
- -
- -
- -
- - -
- -
- - - 1 2 - - Next - - - Last - - Page 1 of 2 -
-
-
- - - - -
- - -
- -
- -
- -
- - -
- -
- -
- - - -
- - - - - - - - - - - - - - - - - - - - - - - - - -
- Narrow Results -
- -
-
- -
-

- In Stock Only - -

-
- - - - -
-
-
-

- Product Type - -

-
- - - - -
-
-
-

- Finish - -

-
- - - - -
-
-
-

- Condition - -

-
- - - - -
-
-
-

- Rarity - -

-
- - - - -
-
-
-

- Language - -

-
- - - - -
-
-
-

- Color - -

-
- - - - -
-
- -
-

- Supply Type - -

-
- - - - -
-
-
-

- Brand - -

-
- - - - -
-
-
-

- Tournament Legality - -

-
- - - - -
-
- -
-
- -
- -
- - -
- - - - - - - -
-
- - - - - - - -
- - -
- - - - - - - -
-
- - - - - - - -
- - -
- - - - - - - -
-
- - - - - - - -
- - -
- - - - - - - -
-
- - - - - - - -
- - -
- - - - - - - -
-
- - - - - - - -
- - - - - -
-

Search Results for Sol Ring

-
- - - - -
- diff --git a/internal/scenario/scenario.go b/internal/scenario/scenario.go deleted file mode 100644 index a5f6233..0000000 --- a/internal/scenario/scenario.go +++ /dev/null @@ -1,91 +0,0 @@ -package scenario - -import ( - "context" - "errors" - "fmt" - "log" - "strings" - - "gitlab.com/flygrounder/go-mtg-vk/internal/cardsinfo" -) - -const ( - incorrectMessage = "Некорректная команда" - cardNotFoundMessage = "Карта не найдена" - pricesUnavailableMessage = "Цены временно недоступны, попробуйте позже" -) - -type Scenario struct { - Sender Sender - Logger *log.Logger - InfoFetcher CardInfoFetcher - Cache CardCache -} - -type UserMessage struct { - Body string - UserId int64 -} - -type CardCache interface { - Init(ctx context.Context) error - Get(ctx context.Context, cardName string) ([]cardsinfo.ScgCardPrice, error) - Set(ctx context.Context, cardName string, prices []cardsinfo.ScgCardPrice) error -} - -type CardInfoFetcher interface { - GetNameByCardId(set string, number string) string - GetOriginalName(name string) string - GetPrices(name string) ([]cardsinfo.ScgCardPrice, error) -} - -type Sender interface { - Send(userId int64, message string) - SendPrices(userId int64, cardName string, prices []cardsinfo.ScgCardPrice) -} - -func (s *Scenario) HandleSearch(ctx context.Context, msg *UserMessage) { - cardName, err := s.getCardNameByCommand(msg.Body) - if err != nil { - s.Sender.Send(msg.UserId, incorrectMessage) - s.Logger.Printf("[info] Not correct command. Message: %s user input: %s", err.Error(), msg.Body) - } else if cardName == "" { - s.Sender.Send(msg.UserId, cardNotFoundMessage) - s.Logger.Printf("[info] Could not find card. User input: %s", msg.Body) - } else { - prices, err := s.Cache.Get(ctx, cardName) - if err == nil { - s.Sender.SendPrices(msg.UserId, cardName, prices) - return - } - prices, err = s.InfoFetcher.GetPrices(cardName) - if err != nil { - s.Sender.Send(msg.UserId, pricesUnavailableMessage) - s.Logger.Printf("[error] Could not find SCG prices. Message: %s card name: %s", err.Error(), cardName) - return - } - err = s.Cache.Set(ctx, cardName, prices) - if err != nil { - s.Logger.Println(fmt.Errorf("failed add entry in cache: %w", err)) - } - s.Sender.SendPrices(msg.UserId, cardName, prices) - } -} - -func (s *Scenario) getCardNameByCommand(command string) (string, error) { - var name string - switch { - case strings.HasPrefix(command, "!s"): - split := strings.Split(command, " ") - if len(split) < 3 { - return "", errors.New("wrong command") - } - set := split[1] - number := split[2] - name = s.InfoFetcher.GetNameByCardId(set, number) - default: - name = s.InfoFetcher.GetOriginalName(command) - } - return name, nil -} diff --git a/internal/scenario/scenario_test.go b/internal/scenario/scenario_test.go deleted file mode 100644 index 6800449..0000000 --- a/internal/scenario/scenario_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package scenario - -import ( - "context" - "strings" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestScenario_HandleSearch_BadCommand(t *testing.T) { - testCtx := GetTestScenarioCtx() - testCtx.Scenario.HandleSearch(context.Background(), &UserMessage{ - Body: "!s", - UserId: 1, - }) - assert.Equal(t, []testMessage{ - { - userId: 1, - message: incorrectMessage, - }, - }, testCtx.Sender.sent) - assert.True(t, strings.Contains(testCtx.LogBuf.String(), "[info]")) -} - -func TestScenario_HandleSearch_GoodCommand(t *testing.T) { - testCtx := GetTestScenarioCtx() - testCtx.Scenario.HandleSearch(context.Background(), &UserMessage{ - Body: "!s grn 228", - UserId: 1, - }) - assert.Equal(t, []testMessage{ - { - userId: 1, - message: "good", - }, - }, testCtx.Sender.sent) -} - -func TestScenario_HandleSearch_NotFoundCard(t *testing.T) { - testCtx := GetTestScenarioCtx() - testCtx.Scenario.HandleSearch(context.Background(), &UserMessage{ - Body: "absolutely_random_card", - UserId: 1, - }) - assert.Equal(t, []testMessage{ - { - userId: 1, - message: cardNotFoundMessage, - }, - }, testCtx.Sender.sent) - assert.True(t, strings.Contains(testCtx.LogBuf.String(), "[info]")) -} - -func TestScenario_HandleSearch_BadCard(t *testing.T) { - testCtx := GetTestScenarioCtx() - testCtx.Scenario.HandleSearch(context.Background(), &UserMessage{ - Body: "bad", - UserId: 1, - }) - assert.Equal(t, []testMessage{ - { - userId: 1, - message: pricesUnavailableMessage, - }, - }, testCtx.Sender.sent) - assert.True(t, strings.Contains(testCtx.LogBuf.String(), "[error]")) -} -func TestScenario_HandleSearch_Uncached(t *testing.T) { - testCtx := GetTestScenarioCtx() - testCtx.Scenario.HandleSearch(context.Background(), &UserMessage{ - Body: "uncached", - UserId: 1, - }) - assert.Equal(t, []testMessage{ - { - userId: 1, - message: "uncached", - }, - }, testCtx.Sender.sent) - _, err := testCtx.Scenario.Cache.Get(context.Background(), "uncached") - assert.Nil(t, err) -} diff --git a/internal/scenario/test_cache.go b/internal/scenario/test_cache.go deleted file mode 100644 index 7052fcf..0000000 --- a/internal/scenario/test_cache.go +++ /dev/null @@ -1,29 +0,0 @@ -package scenario - -import ( - "context" - "errors" - - "gitlab.com/flygrounder/go-mtg-vk/internal/cardsinfo" -) - -type testCache struct { - table map[string][]cardsinfo.ScgCardPrice -} - -func (t *testCache) Init(ctx context.Context) error { - return nil -} - -func (t *testCache) Get(ctx context.Context, cardName string) ([]cardsinfo.ScgCardPrice, error) { - msg, ok := t.table[cardName] - if !ok { - return nil, errors.New("test") - } - return msg, nil -} - -func (t *testCache) Set(ctx context.Context, cardName string, prices []cardsinfo.ScgCardPrice) error { - t.table[cardName] = prices - return nil -} diff --git a/internal/scenario/test_ctx.go b/internal/scenario/test_ctx.go deleted file mode 100644 index d6ff388..0000000 --- a/internal/scenario/test_ctx.go +++ /dev/null @@ -1,39 +0,0 @@ -package scenario - -import ( - "bytes" - "log" - - "gitlab.com/flygrounder/go-mtg-vk/internal/cardsinfo" -) - -type TestScenarioCtx struct { - Scenario *Scenario - Sender *testSender - LogBuf *bytes.Buffer -} - -func GetTestScenarioCtx() TestScenarioCtx { - sender := &testSender{} - buf := &bytes.Buffer{} - return TestScenarioCtx{ - LogBuf: buf, - Scenario: &Scenario{ - Sender: sender, - Logger: log.New(buf, "", 0), - InfoFetcher: &testInfoFetcher{}, - Cache: &testCache{ - table: map[string][]cardsinfo.ScgCardPrice{ - "good": { - { - Price: "1", - Edition: "alpha", - Link: "scg", - }, - }, - }, - }, - }, - Sender: sender, - } -} diff --git a/internal/scenario/test_info_fetcher.go b/internal/scenario/test_info_fetcher.go deleted file mode 100644 index 88dcd95..0000000 --- a/internal/scenario/test_info_fetcher.go +++ /dev/null @@ -1,27 +0,0 @@ -package scenario - -import ( - "errors" - - "gitlab.com/flygrounder/go-mtg-vk/internal/cardsinfo" -) - -type testInfoFetcher struct{} - -func (t *testInfoFetcher) GetPrices(name string) ([]cardsinfo.ScgCardPrice, error) { - if name == "good" || name == "uncached" { - return nil, nil - } - return nil, errors.New("test") -} - -func (t *testInfoFetcher) GetNameByCardId(_ string, _ string) string { - return "good" -} - -func (t *testInfoFetcher) GetOriginalName(name string) string { - if name == "good" || name == "bad" || name == "uncached" { - return name - } - return "" -} diff --git a/internal/scenario/test_sender.go b/internal/scenario/test_sender.go deleted file mode 100644 index e4a5d8f..0000000 --- a/internal/scenario/test_sender.go +++ /dev/null @@ -1,26 +0,0 @@ -package scenario - -import "gitlab.com/flygrounder/go-mtg-vk/internal/cardsinfo" - -type testSender struct { - sent []testMessage -} - -func (s *testSender) SendPrices(userId int64, cardName string, prices []cardsinfo.ScgCardPrice) { - s.sent = append(s.sent, testMessage{ - userId: userId, - message: cardName, - }) -} - -type testMessage struct { - userId int64 - message string -} - -func (s *testSender) Send(userId int64, message string) { - s.sent = append(s.sent, testMessage{ - userId: userId, - message: message, - }) -} diff --git a/internal/telegram/handler.go b/internal/telegram/handler.go deleted file mode 100644 index e89312f..0000000 --- a/internal/telegram/handler.go +++ /dev/null @@ -1,33 +0,0 @@ -package telegram - -import ( - "context" - - "github.com/gin-gonic/gin" - tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" - "gitlab.com/flygrounder/go-mtg-vk/internal/scenario" -) - -const welcomeMessage = "Здравствуйте, вас приветствует бот для поиска цен на карты MTG, введите название карты, которая вас интересует." - -type Handler struct { - Scenario *scenario.Scenario -} - -func (h *Handler) HandleMessage(c *gin.Context) { - var upd tgbotapi.Update - err := c.Bind(&upd) - if err != nil || upd.Message == nil { - return - } - - if upd.Message.Text == "/start" { - h.Scenario.Sender.Send(upd.Message.Chat.ID, welcomeMessage) - return - } - - h.Scenario.HandleSearch(context.Background(), &scenario.UserMessage{ - Body: upd.Message.Text, - UserId: upd.Message.Chat.ID, - }) -} diff --git a/internal/telegram/sender.go b/internal/telegram/sender.go deleted file mode 100644 index 870db38..0000000 --- a/internal/telegram/sender.go +++ /dev/null @@ -1,41 +0,0 @@ -package telegram - -import ( - "fmt" - "strings" - - tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" - "gitlab.com/flygrounder/go-mtg-vk/internal/cardsinfo" -) - -type Sender struct { - API *tgbotapi.BotAPI -} - -func (s *Sender) SendPrices(userId int64, cardName string, prices []cardsinfo.ScgCardPrice) { - msg := formatCardPrices(cardName, prices) - s.Send(userId, msg) -} - -func (h *Sender) Send(userId int64, message string) { - msg := tgbotapi.NewMessage(userId, message) - msg.DisableWebPagePreview = true - msg.ParseMode = tgbotapi.ModeMarkdown - h.API.Send(msg) -} - -func formatCardPrices(name string, prices []cardsinfo.ScgCardPrice) string { - escapedName := strings.ReplaceAll(name, "_", "\\_") - message := fmt.Sprintf("Оригинальное название: %v\n\n", escapedName) - for i, v := range prices { - message += fmt.Sprintf("%v. %v", i+1, formatPrice(v)) - } - if len(prices) == 0 { - message += "Цен не найдено\n" - } - return message -} - -func formatPrice(s cardsinfo.ScgCardPrice) string { - return fmt.Sprintf("[%v](%v): %v\n", s.Edition, s.Link, s.Price) -} diff --git a/internal/telegram/sender_test.go b/internal/telegram/sender_test.go deleted file mode 100644 index 54552d2..0000000 --- a/internal/telegram/sender_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package telegram - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "gitlab.com/flygrounder/go-mtg-vk/internal/cardsinfo" -) - -func Test_formatCardPrices(t *testing.T) { - prices := []cardsinfo.ScgCardPrice{ - { - Price: "1", - Edition: "Alpha", - Link: "scg1", - }, - { - Price: "2", - Edition: "Beta", - Link: "scg2", - }, - } - result := formatCardPrices("card", prices) - assert.Equal(t, "Оригинальное название: card\n\n1. [Alpha](scg1): 1\n2. [Beta](scg2): 2\n", result) -} - -func Test_formatCardPricesEscapeUnderscore(t *testing.T) { - prices := []cardsinfo.ScgCardPrice{} - result := formatCardPrices("_____", prices) - assert.Equal(t, "Оригинальное название: \\_\\_\\_\\_\\_\n\nЦен не найдено\n", result) -} diff --git a/internal/vk/format.go b/internal/vk/format.go deleted file mode 100644 index c0e73be..0000000 --- a/internal/vk/format.go +++ /dev/null @@ -1,22 +0,0 @@ -package vk - -import ( - "fmt" - - "gitlab.com/flygrounder/go-mtg-vk/internal/cardsinfo" -) - -func formatCardPrices(name string, prices []cardsinfo.ScgCardPrice) string { - message := fmt.Sprintf("Оригинальное название: %v\n\n", name) - for i, v := range prices { - message += fmt.Sprintf("%v. %v", i+1, formatPrice(v)) - } - if len(prices) == 0 { - message += "Цен не найдено\n" - } - return message -} - -func formatPrice(s cardsinfo.ScgCardPrice) string { - return fmt.Sprintf("%v: %v\n%v\n", s.Edition, s.Price, s.Link) -} diff --git a/internal/vk/format_test.go b/internal/vk/format_test.go deleted file mode 100644 index 5a7be6d..0000000 --- a/internal/vk/format_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package vk - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "gitlab.com/flygrounder/go-mtg-vk/internal/cardsinfo" -) - -func TestFormatCardPrices(t *testing.T) { - formatted := formatCardPrices("card", []cardsinfo.ScgCardPrice{ - { - Price: "1.5$", - Edition: "ED", - Link: "scg.com", - }, - }) - assert.Equal(t, "Оригинальное название: card\n\n1. ED: 1.5$\nscg.com\n", formatted) -} diff --git a/internal/vk/handler.go b/internal/vk/handler.go deleted file mode 100644 index c0bf83a..0000000 --- a/internal/vk/handler.go +++ /dev/null @@ -1,70 +0,0 @@ -package vk - -import ( - "context" - "net/http" - - "github.com/gin-gonic/gin" - "gitlab.com/flygrounder/go-mtg-vk/internal/scenario" -) - -type Handler struct { - Scenario *scenario.Scenario - SecretKey string - GroupId int64 - ConfirmationString string - DictPath string -} - -type cardInfoFetcher interface { - GetFormattedCardPrices(name string) (string, error) - GetNameByCardId(set string, number string) string - GetOriginalName(name string) string -} - -type cardCache interface { - Get(cardName string) (string, error) - Set(cardName string, message string) -} - -type messageRequest struct { - Type string `json:"type"` - GroupId int64 `json:"group_id"` - Object userMessage `json:"object"` - Secret string `json:"secret"` -} - -type userMessage struct { - Body string `json:"text"` - UserId int64 `json:"peer_id"` -} - -const ( - incorrectMessage = "Некорректная команда" - cardNotFoundMessage = "Карта не найдена" - pricesUnavailableMessage = "Цены временно недоступны, попробуйте позже" -) - -func (h *Handler) HandleMessage(c *gin.Context) { - var req messageRequest - _ = c.BindJSON(&req) - if req.Secret != h.SecretKey { - return - } - switch req.Type { - case "confirmation": - h.handleConfirmation(c, &req) - case "message_new": - h.Scenario.HandleSearch(context.Background(), &scenario.UserMessage{ - Body: req.Object.Body, - UserId: req.Object.UserId, - }) - c.String(http.StatusOK, "ok") - } -} - -func (h *Handler) handleConfirmation(c *gin.Context, req *messageRequest) { - if (req.Type == "confirmation") && (req.GroupId == h.GroupId) { - c.String(http.StatusOK, h.ConfirmationString) - } -} diff --git a/internal/vk/handler_test.go b/internal/vk/handler_test.go deleted file mode 100644 index acc9395..0000000 --- a/internal/vk/handler_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package vk - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestHandler_HandleMessage_Confirm(t *testing.T) { - testCtx := getTestHandlerCtx() - ctx := getTestRequestCtx(&messageRequest{ - Type: "confirmation", - GroupId: testCtx.handler.GroupId, - Secret: testCtx.handler.SecretKey, - }, testCtx.recorder) - testCtx.handler.HandleMessage(ctx) - assert.Equal(t, testCtx.handler.ConfirmationString, testCtx.recorder.Body.String()) -} - -func TestHandler_HandleMessage_Message(t *testing.T) { - testCtx := getTestHandlerCtx() - ctx := getTestRequestCtx(&messageRequest{ - Type: "message_new", - Secret: testCtx.handler.SecretKey, - }, testCtx.recorder) - testCtx.handler.HandleMessage(ctx) - assert.Equal(t, "ok", testCtx.recorder.Body.String()) -} - -func TestHandler_HandleMessage_NoSecretKey(t *testing.T) { - testCtx := getTestHandlerCtx() - ctx := getTestRequestCtx(&messageRequest{ - Type: "message_new", - }, testCtx.recorder) - testCtx.handler.HandleMessage(ctx) - assert.Equal(t, "", testCtx.recorder.Body.String()) -} - diff --git a/internal/vk/sender.go b/internal/vk/sender.go deleted file mode 100644 index 214de34..0000000 --- a/internal/vk/sender.go +++ /dev/null @@ -1,64 +0,0 @@ -package vk - -import ( - "encoding/json" - "io/ioutil" - "log" - "math/rand" - "net/http" - "net/url" - "strconv" - "strings" - - "gitlab.com/flygrounder/go-mtg-vk/internal/cardsinfo" -) - -const sendMessageUrl = "https://api.vk.com/method/messages.send" - -type sender interface { - send(userId int64, message string) -} - -type ApiSender struct { - Token string - Logger *log.Logger -} - -func (s *ApiSender) SendPrices(userId int64, cardName string, prices []cardsinfo.ScgCardPrice) { - msg := formatCardPrices(cardName, prices) - s.Send(userId, msg) -} - -type sendMessageResponse struct { - Error errorResponse `json:"error"` -} - -type errorResponse struct { - ErrorCode int `json:"error_code"` - ErrorMsg string `json:"error_msg"` -} - -func (s *ApiSender) Send(userId int64, message string) { - randomId := rand.Int63() - params := []string{ - "access_token=" + s.Token, - "peer_id=" + strconv.FormatInt(userId, 10), - "message=" + url.QueryEscape(message), - "v=5.95", - "random_id=" + strconv.FormatInt(randomId, 10), - "dont_parse_links=1", - } - joined := strings.Join(params, "&") - reqUrl := sendMessageUrl + "?" + joined - resp, err := http.Get(reqUrl) - if err != nil || resp.StatusCode != http.StatusOK { - s.Logger.Printf("[error] Could not Send message. User: %d", userId) - return - } - respContent, _ := ioutil.ReadAll(resp.Body) - var unmarshalled sendMessageResponse - _ = json.Unmarshal(respContent, &unmarshalled) - if unmarshalled.Error.ErrorCode != 0 { - s.Logger.Printf("[error] Message was not sent. User: %d error message: %s", userId, unmarshalled.Error.ErrorMsg) - } -} diff --git a/internal/vk/sender_test.go b/internal/vk/sender_test.go deleted file mode 100644 index d7f5876..0000000 --- a/internal/vk/sender_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package vk - -import ( - "bytes" - "log" - "net/http" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "gopkg.in/h2non/gock.v1" -) - -func TestApiSender_Send_OK(t *testing.T) { - defer gock.Off() - - gock.New(sendMessageUrl).MatchParams( - map[string]string{ - "access_token": "token", - "peer_id": "1", - "message": "msg", - "v": "5.95", - "dont_parse_links": "1", - }, - ).ParamPresent("random_id").Reply(http.StatusOK) - - sender := ApiSender{Token: "token"} - sender.Send(1, "msg") - assert.False(t, gock.HasUnmatchedRequest()) -} - -func TestApiSender_Send_NotOK(t *testing.T) { - defer gock.Off() - - gock.New(sendMessageUrl).Reply(http.StatusInternalServerError) - - b := &bytes.Buffer{} - sender := ApiSender{ - Token: "token", - Logger: log.New(b, "", 0), - } - sender.Send(1, "msg") - assert.True(t, strings.Contains(b.String(), "[error]")) -} - -func TestApiSender_Send_ErrorCode(t *testing.T) { - defer gock.Off() - - gock.New(sendMessageUrl).Reply(http.StatusOK).JSON( - map[string]interface{}{ - "error": map[string]interface{}{ - "error_code": 100, - "error_msg": "bad user", - }, - }, - ) - - b := &bytes.Buffer{} - sender := ApiSender{ - Token: "token", - Logger: log.New(b, "", 0), - } - sender.Send(1, "msg") - assert.True(t, strings.Contains(b.String(), "[error]")) -} diff --git a/internal/vk/test_ctx.go b/internal/vk/test_ctx.go deleted file mode 100644 index 9c0e261..0000000 --- a/internal/vk/test_ctx.go +++ /dev/null @@ -1,35 +0,0 @@ -package vk - -import ( - "bytes" - "encoding/json" - "net/http/httptest" - - "github.com/gin-gonic/gin" - "gitlab.com/flygrounder/go-mtg-vk/internal/scenario" -) - -type testCtx struct { - handler *Handler - recorder *httptest.ResponseRecorder -} - -func getTestHandlerCtx() testCtx { - s := scenario.GetTestScenarioCtx() - return testCtx{ - handler: &Handler{ - SecretKey: "sec", - GroupId: 10, - ConfirmationString: "con", - Scenario: s.Scenario, - }, - recorder: httptest.NewRecorder(), - } -} - -func getTestRequestCtx(msgReq *messageRequest, recorder *httptest.ResponseRecorder) *gin.Context { - ctx, _ := gin.CreateTestContext(recorder) - body, _ := json.Marshal(msgReq) - ctx.Request = httptest.NewRequest("POST", "/", bytes.NewReader(body)) - return ctx -} diff --git a/justfile b/justfile new file mode 100644 index 0000000..ba078f7 --- /dev/null +++ b/justfile @@ -0,0 +1,4 @@ +set dotenv-load + +run: + cargo watch -x run diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..51c32ee --- /dev/null +++ b/src/main.rs @@ -0,0 +1,467 @@ +use anyhow::{Context, Result}; +use axum::extract::State; +use axum::http::{HeaderMap, StatusCode}; +use axum::routing::post; +use axum::{Json, Router}; +use regex::Regex; +use scryfall::card::Card; +use serde::{Deserialize, Serialize}; +use std::env; +use std::fmt::Display; +use std::sync::Arc; +use teloxide::payloads::SendMessage; +use teloxide::prelude::*; +use teloxide::requests::JsonRequest; +use teloxide::types::ParseMode; +use teloxide::utils::markdown::{escape, escape_link_url}; +use teloxide::Bot; +use tokio::net::TcpListener; + +#[derive(Clone)] +struct AppState { + vk_client: Arc, + telegram_client: Arc, + price_fetcher: Arc, +} + +async fn get_card_name(query: &str) -> Result { + let number_search_regex = Regex::new(r#"^!s (?\w{3}) (?\d+)$"#) + .context("failed to compile number search regex")?; + let name = if let Some(captures) = number_search_regex.captures(query) { + let set = captures + .name("set") + .context("failed to get 'set' value from capture")? + .as_str() + .to_string(); + let number: usize = captures + .name("number") + .context("failed to get 'number' from capture")? + .as_str() + .parse() + .context("failed to parse collector number")?; + Card::set_and_number(&set, number) + .await + .map(|card| card.name) + .context("failed to get card by set and number")? + } else { + Card::named_fuzzy(query) + .await + .map(|card| card.name) + .context("failed to find card by it's fuzzy name")? + }; + Ok(name) +} + +#[derive(Serialize)] +struct StarCityRequestPayload { + keyword: String, + #[serde(rename = "ClientGuid")] + client_guid: String, + #[serde(rename = "SortBy")] + sort_by: String, + #[serde(rename = "FacetSelections")] + facet_selections: StarCityFacetSelection, +} + +#[derive(Serialize)] +struct StarCityFacetSelection { + product_type: Vec, +} + +#[derive(Deserialize)] +struct StarCityResponse { + #[serde(rename = "Results")] + results: Vec, +} + +#[derive(Deserialize)] +struct StarCityResponseResult { + #[serde(rename = "Document")] + document: StarCityResponseDocument, +} + +#[derive(Deserialize)] +struct StarCityResponseDocument { + set: Vec, + hawk_child_attributes: Vec, +} + +#[derive(Deserialize)] +#[serde(untagged)] +enum StarCityResponseAttributesVariants { + Available(StarCityResponseAttributes), + Unavailable {}, +} + +impl StarCityResponseAttributesVariants { + fn get_card_info(&self, set: &str) -> Option { + if let StarCityResponseAttributesVariants::Available(res) = self { + let condition = res.condition.first().cloned()?; + let price = res.price.first().cloned()?.parse().ok()?; + let url = res.url.first().cloned()?; + if condition == "Near Mint" { + Some(CardInfo { + set: set.to_string(), + price, + url: format!("https://starcitygames.com{url}"), + }) + } else { + None + } + } else { + None + } + } +} + +#[derive(Deserialize)] +struct StarCityResponseAttributes { + price: Vec, + condition: Vec, + url: Vec, +} + +struct CardInfo { + set: String, + price: f32, + url: String, +} + +struct PriceFetcher { + client_guid: String, +} + +impl PriceFetcher { + fn from_env() -> Result { + let client_guid = + env::var("SCG_CLIENT_GUID").context("SCG_CLIENT_GUID env variable is not set")?; + Ok(Self { client_guid }) + } + + async fn get_card_prices(&self, name: &str) -> Result> { + let client = reqwest::ClientBuilder::new().build()?; + let resp = client + .post("https://essearchapi-na.hawksearch.com/api/v2/search") + .json(&StarCityRequestPayload { + keyword: name.to_string(), + client_guid: self.client_guid.clone(), + sort_by: "score".into(), + facet_selections: StarCityFacetSelection { + product_type: vec!["Singles".into()], + }, + }) + .send() + .await + .context("request to SCG failed")?; + + let response: StarCityResponse = resp.json().await.context("SCG returned invalid json")?; + let res = response + .results + .iter() + .flat_map(|result| { + let set = result.document.set.first().cloned()?; + let info = result + .document + .hawk_child_attributes + .iter() + .flat_map(|res| res.get_card_info(&set)) + .collect::>(); + Some(info) + }) + .flatten() + .collect::>(); + Ok(res) + } +} + +struct TelegramClient { + bot: Bot, + secret: String, +} + +impl TelegramClient { + fn from_env() -> Result { + let secret = env::var("TG_SECRET").context("failed to get TG_SECRET env var")?; + Ok(Self { + bot: Bot::from_env(), + secret, + }) + } +} + +#[derive(Deserialize)] +struct TelegramUpdate { + message: TelegramMessage, +} + +#[derive(Deserialize)] +struct TelegramMessage { + text: String, + chat: TelegramChat, +} + +#[derive(Deserialize)] +struct TelegramChat { + id: i64, +} + +async fn telegram( + headers: HeaderMap, + State(state): State, + Json(payload): Json, +) -> Result<&'static str, StatusCode> { + let secret = headers + .get("X-Telegram-Bot-Api-Secret-Token") + .ok_or(StatusCode::FORBIDDEN)? + .to_str() + .map_err(|_| StatusCode::FORBIDDEN)?; + if secret != state.telegram_client.secret { + Err(StatusCode::FORBIDDEN)?; + } + let chat_id = payload.message.chat.id; + let text = &payload.message.text; + let res = handle_telegram_message(&state, chat_id, text).await; + if let Err(err) = res { + report_error(state.telegram_client.as_ref(), chat_id, err).await; + } + Ok("OK") +} + +#[derive(Debug)] +enum ErrorContext { + Scryfall, + Scg, + Telegram, + Vk, +} + +impl Display for ErrorContext { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let text = match self { + Self::Scryfall => "failed to get card from scryfall", + Self::Scg => "failed to get card from SCG", + Self::Telegram => "failed to send message through Telergam", + Self::Vk => "failed to send message through VK", + }; + f.write_str(text) + } +} + +async fn handle_telegram_message(state: &AppState, chat_id: i64, message: &str) -> Result<()> { + let name = get_card_name(message) + .await + .context(ErrorContext::Scryfall)?; + let prices = state + .price_fetcher + .get_card_prices(&name) + .await + .context(ErrorContext::Scg)?; + let content = prices + .iter() + .take(5) + .enumerate() + .map(|(i, info)| { + format!( + "{}\\. [{}]({}): {}", + i + 1, + escape(&info.set), + escape_link_url(&info.url), + escape(&format!("${}", info.price)) + ) + }) + .collect::>() + .join("\n"); + let header = escape(&format!("Оригинальное название: {}", name)); + let response = format!("{header}\n\n{content}"); + let request = SendMessage::new(ChatId(chat_id), response) + .parse_mode(ParseMode::MarkdownV2) + .disable_web_page_preview(true); + JsonRequest::new(state.telegram_client.bot.clone(), request) + .send() + .await + .context(ErrorContext::Telegram)?; + Ok(()) +} + +struct VkClient { + client: reqwest::Client, + token: String, + group_id: i64, + confirmation: String, + secret: String, +} + +impl VkClient { + fn from_env() -> Result { + let client = reqwest::Client::new(); + let token = env::var("VK_TOKEN").context("failed to get VK_TOKEN")?; + let group_id = env::var("VK_GROUP_ID") + .context("failed to get VK_GROUP_ID") + .and_then(|x| x.parse().context("failed to parse VK_GROUP_ID as a number"))?; + let confirmation = + env::var("VK_CONFIRMATION_STRING").context("failed to get VK_CONFIRMATION_STRING")?; + let secret = env::var("VK_SECRET").context("failed to get VK_SECRET")?; + Ok(Self { + client, + token, + group_id, + confirmation, + secret, + }) + } + + async fn send(&self, user_id: i64, message: &str) -> Result<()> { + self.client + .get("https://api.vk.com/method/messages.send") + .query(&[ + ("user_id", user_id.to_string().as_str()), + ("v", "5.131"), + ("access_token", &self.token), + ("random_id", "0"), + ("message", message), + ]) + .send() + .await + .context(ErrorContext::Vk)?; + Ok(()) + } +} + +#[derive(Deserialize)] +#[serde(tag = "type")] +enum VkRequest { + #[serde(rename = "message_new")] + Message(VkMessageRequest), + #[serde(rename = "confirmation")] + Confirmation(VkConfirmationRequest), +} + +#[derive(Deserialize)] +struct VkMessageRequest { + object: VkMessageObject, + secret: String, +} + +#[derive(Deserialize)] +struct VkMessageObject { + from_id: i64, + text: String, +} + +#[derive(Deserialize)] +struct VkConfirmationRequest { + group_id: i64, +} + +async fn vk(State(state): State, Json(request): Json) -> (StatusCode, String) { + match request { + VkRequest::Message(payload) => { + if payload.secret != state.vk_client.secret { + return (StatusCode::FORBIDDEN, "Access denied".into()); + } + let user_id = payload.object.from_id; + let message = &payload.object.text; + let res = handle_vk_message(&state, user_id, message).await; + if let Err(err) = res { + report_error(state.vk_client.as_ref(), user_id, err).await; + } + } + VkRequest::Confirmation(confirmation) => { + if confirmation.group_id != state.vk_client.group_id { + return (StatusCode::FORBIDDEN, "Access denied".into()); + } + return (StatusCode::OK, state.vk_client.confirmation.clone()); + } + } + (StatusCode::OK, "OK".into()) +} + +async fn handle_vk_message(state: &AppState, user_id: i64, message: &str) -> Result<()> { + let name = get_card_name(message) + .await + .context(ErrorContext::Scryfall)?; + let prices = state + .price_fetcher + .get_card_prices(&name) + .await + .context(ErrorContext::Scg)?; + let header = escape(&format!("Оригинальное название: {}", name)); + let content = prices + .iter() + .take(5) + .enumerate() + .map(|(i, info)| format!("{}. {}: ${}\n{}", i + 1, info.set, info.price, info.url)) + .collect::>() + .join("\n"); + let response = format!("{header}\n\n{content}"); + state.vk_client.send(user_id, &response).await +} + +trait MessageSender { + async fn send(&self, user_id: i64, message: &str) -> Result<()>; +} + +impl MessageSender for VkClient { + async fn send(&self, user_id: i64, message: &str) -> Result<()> { + self.send(user_id, message).await?; + Ok(()) + } +} + +impl MessageSender for TelegramClient { + async fn send(&self, user_id: i64, message: &str) -> Result<()> { + self.bot.send_message(ChatId(user_id), message).await?; + Ok(()) + } +} + +async fn report_error(sender: &T, chat_id: i64, err: anyhow::Error) { + if !matches!( + err.downcast_ref::(), + Some(scryfall::Error::ScryfallError(_)) + ) { + println!("error: {:#}", err); + } + let sent = match err.downcast_ref::() { + Some(ErrorContext::Scryfall) => sender + .send(chat_id, "Карта не найдена") + .await + .map(|_| ()) + .context("failed to send error message"), + Some(ErrorContext::Scg) => sender + .send(chat_id, "Цены не найдены") + .await + .map(|_| ()) + .context("failed to send error message"), + _ => Ok(()), + }; + if let Err(err) = sent { + println!("error: {:#}", err); + } +} + +#[tokio::main] +async fn main() -> Result<()> { + let port = env::var("PORT") + .ok() + .and_then(|x| x.parse().ok()) + .unwrap_or(3000); + let addr: (&str, u16) = ("0.0.0.0", port); + let listener = TcpListener::bind(addr) + .await + .context("failed to create tcp listener")?; + let vk_client = VkClient::from_env().context("failed to init vk client")?; + let telegram_client = TelegramClient::from_env().context("failed to init telegram client")?; + let price_fetcher = PriceFetcher::from_env().context("failed to init price fetcher")?; + let state = AppState { + vk_client: Arc::new(vk_client), + telegram_client: Arc::new(telegram_client), + price_fetcher: Arc::new(price_fetcher), + }; + let app = Router::new() + .route("/tg", post(telegram)) + .route("/vk", post(vk)) + .with_state(state); + axum::serve(listener, app).await.unwrap(); + Ok(()) +}