diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..f09fec4 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,6 @@ +# On MacOS, `brew install michaeleisel/zld/zld` +[target.x86_64-apple-darwin] +rustflags = ["-C", "link-arg=-fuse-ld=lld"] + +[target.aarch64-apple-darwin] +rustflags = ["-C", "link-arg=-fuse-ld=lld"] diff --git a/.gitignore b/.gitignore index 0b745e2..5364586 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,17 @@ -/target -.env \ No newline at end of file +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb +.env +.vscode/ +Secrets.toml diff --git a/.sqlx/query-0125034b9591596868a24565e0827028d1a6684dc1dfc67bbdfdb4884fe7ce87.json b/.sqlx/query-0125034b9591596868a24565e0827028d1a6684dc1dfc67bbdfdb4884fe7ce87.json new file mode 100644 index 0000000..b42daba --- /dev/null +++ b/.sqlx/query-0125034b9591596868a24565e0827028d1a6684dc1dfc67bbdfdb4884fe7ce87.json @@ -0,0 +1,34 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, chat_id, user_name\n FROM users\n WHERE chat_id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "chat_id", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "user_name", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false + ] + }, + "hash": "0125034b9591596868a24565e0827028d1a6684dc1dfc67bbdfdb4884fe7ce87" +} diff --git a/.sqlx/query-0dae98bede43705aeb61fd0935c23152a2e027df33e93870ae83aeeca9ef4985.json b/.sqlx/query-0dae98bede43705aeb61fd0935c23152a2e027df33e93870ae83aeeca9ef4985.json new file mode 100644 index 0000000..067ef99 --- /dev/null +++ b/.sqlx/query-0dae98bede43705aeb61fd0935c23152a2e027df33e93870ae83aeeca9ef4985.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO runs (distance, user_id)\n VALUES ($1, $2)\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Float4", + "Int4" + ] + }, + "nullable": [] + }, + "hash": "0dae98bede43705aeb61fd0935c23152a2e027df33e93870ae83aeeca9ef4985" +} diff --git a/.sqlx/query-6100bc13e5ae09fa0ee7de24c9d017da6761440bb56f4de704c6b536eb42c6a0.json b/.sqlx/query-6100bc13e5ae09fa0ee7de24c9d017da6761440bb56f4de704c6b536eb42c6a0.json new file mode 100644 index 0000000..dc65349 --- /dev/null +++ b/.sqlx/query-6100bc13e5ae09fa0ee7de24c9d017da6761440bb56f4de704c6b536eb42c6a0.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE runs\n SET distance = $1\n WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Float4", + "Int4" + ] + }, + "nullable": [] + }, + "hash": "6100bc13e5ae09fa0ee7de24c9d017da6761440bb56f4de704c6b536eb42c6a0" +} diff --git a/.sqlx/query-8d3aa9a674037d772157fda5e3ba14240017aefd9841188eaec4693f078c1f9b.json b/.sqlx/query-8d3aa9a674037d772157fda5e3ba14240017aefd9841188eaec4693f078c1f9b.json new file mode 100644 index 0000000..f0e106c --- /dev/null +++ b/.sqlx/query-8d3aa9a674037d772157fda5e3ba14240017aefd9841188eaec4693f078c1f9b.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM runs\n WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int4" + ] + }, + "nullable": [] + }, + "hash": "8d3aa9a674037d772157fda5e3ba14240017aefd9841188eaec4693f078c1f9b" +} diff --git a/.sqlx/query-a70093e25212a95b80a4ffc376aa29d86bbb0b248e20435b7baa28227c23c10a.json b/.sqlx/query-a70093e25212a95b80a4ffc376aa29d86bbb0b248e20435b7baa28227c23c10a.json new file mode 100644 index 0000000..5562d54 --- /dev/null +++ b/.sqlx/query-a70093e25212a95b80a4ffc376aa29d86bbb0b248e20435b7baa28227c23c10a.json @@ -0,0 +1,41 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT *\n FROM runs\n WHERE user_id = ANY($1)\n ORDER BY run_datetime DESC\n LIMIT $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "distance", + "type_info": "Float4" + }, + { + "ordinal": 2, + "name": "user_id", + "type_info": "Int4" + }, + { + "ordinal": 3, + "name": "run_datetime", + "type_info": "Timestamp" + } + ], + "parameters": { + "Left": [ + "Int4Array", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + true + ] + }, + "hash": "a70093e25212a95b80a4ffc376aa29d86bbb0b248e20435b7baa28227c23c10a" +} diff --git a/.sqlx/query-f1987324ab30e2d14e32e5c8d1963114486c92d13547c43b98419dcbd18957e8.json b/.sqlx/query-f1987324ab30e2d14e32e5c8d1963114486c92d13547c43b98419dcbd18957e8.json new file mode 100644 index 0000000..a6a7d8f --- /dev/null +++ b/.sqlx/query-f1987324ab30e2d14e32e5c8d1963114486c92d13547c43b98419dcbd18957e8.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO users (chat_id, user_name) \n VALUES ($1, $2)\n ON CONFLICT (chat_id, user_name) DO NOTHING", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "f1987324ab30e2d14e32e5c8d1963114486c92d13547c43b98419dcbd18957e8" +} diff --git a/.sqlx/query-f6e57469ae6306d7cc7ceeacdad3b3ee6876ebf4727a89badcd62ade36215e82.json b/.sqlx/query-f6e57469ae6306d7cc7ceeacdad3b3ee6876ebf4727a89badcd62ade36215e82.json new file mode 100644 index 0000000..27c5aae --- /dev/null +++ b/.sqlx/query-f6e57469ae6306d7cc7ceeacdad3b3ee6876ebf4727a89badcd62ade36215e82.json @@ -0,0 +1,34 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT user_name, COUNT(*), SUM(distance)\n FROM runs\n JOIN users ON users.id = runs.user_id\n WHERE user_id = ANY($1)\n GROUP BY user_name", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "user_name", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "count", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "sum", + "type_info": "Float4" + } + ], + "parameters": { + "Left": [ + "Int4Array" + ] + }, + "nullable": [ + false, + null, + null + ] + }, + "hash": "f6e57469ae6306d7cc7ceeacdad3b3ee6876ebf4727a89badcd62ade36215e82" +} diff --git a/.sqlx/query-fa077bf85599d24f2961d5ab08221ba5ccfb2f24b16be8717e9c6c99e27a004a.json b/.sqlx/query-fa077bf85599d24f2961d5ab08221ba5ccfb2f24b16be8717e9c6c99e27a004a.json new file mode 100644 index 0000000..8632299 --- /dev/null +++ b/.sqlx/query-fa077bf85599d24f2961d5ab08221ba5ccfb2f24b16be8717e9c6c99e27a004a.json @@ -0,0 +1,35 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, chat_id, user_name\n FROM users\n WHERE user_name = $1 AND chat_id = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "chat_id", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "user_name", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + false + ] + }, + "hash": "fa077bf85599d24f2961d5ab08221ba5ccfb2f24b16be8717e9c6c99e27a004a" +} diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index e55ee53..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,1675 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" -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.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" -dependencies = [ - "memchr", -] - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "aquamarine" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a941c39708478e8eea39243b5983f1c42d2717b3620ee91f4a52115fd02ac43f" -dependencies = [ - "itertools", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base64" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" - -[[package]] -name = "bumpalo" -version = "3.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" - -[[package]] -name = "bytes" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" - -[[package]] -name = "cc" -version = "1.0.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" -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.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" -dependencies = [ - "android-tzdata", - "num-traits", -] - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" - -[[package]] -name = "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 = "dotenv" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" - -[[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.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "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.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "fastrand" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-executor" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-macro" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.29", -] - -[[package]] -name = "futures-sink" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-util" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" - -[[package]] -name = "h2" -version = "0.3.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[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.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" - -[[package]] -name = "http" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error", -] - -[[package]] -name = "hyper" -version = "0.14.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2 0.4.9", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "ipnet" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" - -[[package]] -name = "itertools" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" - -[[package]] -name = "js-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" -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.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" - -[[package]] -name = "linux-raw-sys" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" - -[[package]] -name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[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.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" -dependencies = [ - "libc", - "wasi", - "windows-sys", -] - -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "never" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91" - -[[package]] -name = "num-traits" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" -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 0.3.2", - "libc", -] - -[[package]] -name = "object" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "openssl" -version = "0.10.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729b745ad4a5575dd06a3e1af1414bd330ee561c01b3899eb584baeaa8def17e" -dependencies = [ - "bitflags 1.3.2", - "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.29", -] - -[[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.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" - -[[package]] -name = "pin-project" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.29", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - -[[package]] -name = "pretty_env_logger" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" -dependencies = [ - "env_logger", - "log", -] - -[[package]] -name = "proc-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.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quote" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" -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.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "regex" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" - -[[package]] -name = "reqwest" -version = "0.11.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "mime_guess", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "serde_urlencoded", - "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.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" -dependencies = [ - "bitflags 2.4.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", -] - -[[package]] -name = "ryu" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[package]] -name = "schannel" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "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.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" - -[[package]] -name = "serde" -version = "1.0.183" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.183" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.29", -] - -[[package]] -name = "serde_json" -version = "1.0.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_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.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" -dependencies = [ - "autocfg", -] - -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "socket2" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" -dependencies = [ - "libc", - "windows-sys", -] - -[[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.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[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 = "tele-run" -version = "0.1.0" -dependencies = [ - "dotenv", - "log", - "pretty_env_logger", - "teloxide", - "tokio", -] - -[[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", - "teloxide-macros", - "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 = "teloxide-macros" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f1d653b093dba5e44cada57a516f572167df37b8a619443e59c8c517bb6d804" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "tempfile" -version = "3.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651" -dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall", - "rustix", - "windows-sys", -] - -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thiserror" -version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.29", -] - -[[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.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "pin-project-lite", - "signal-hook-registry", - "socket2 0.5.3", - "tokio-macros", - "windows-sys", -] - -[[package]] -name = "tokio-macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.29", -] - -[[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.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "unicase" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - -[[package]] -name = "unicode-ident" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" - -[[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.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "uuid" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" -dependencies = [ - "getrandom", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.29", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.29", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" - -[[package]] -name = "wasm-streams" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bbae3363c08332cadccd13b67db371814cd214c2524020932f0804b8cf7c078" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "web-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.48.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92ecb8ae0317859f509f17b19adc74b0763b0fa3b085dea8ed01085c8dac222" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d14b0ee96970be7108701212f097ce67ca772fd84cb0ffbc86d26a94e77ba929" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1332277d49f440c8fc6014941e320ee47ededfcce10cb272728470f56cc092c9" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d992130ac399d56f02c20564e9975ac5ba08cb25cb832849bbc0d736a101abe5" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "962e96d0fa4b4773c63977977ea6564f463fb10e34a6e07360428b53ae7a3f71" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30652a53018a48a9735fbc2986ff0446c37bc8bed0d3f98a0ed4d04cdb80027e" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5bb3f0331abfe1a95af56067f1e64b3791b55b5373b03869560b6025de809bf" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd1df36d9fd0bbe4849461de9b969f765170f4e0f90497d580a235d515722b10" - -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] diff --git a/Cargo.toml b/Cargo.toml index 3d45394..6b3baef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,23 @@ [package] -name = "tele-run" +name = "telerunbot" version = "0.1.0" edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +publish = false [dependencies] -teloxide = { version = "0.12", features = ["macros"] } log = "0.4" pretty_env_logger = "0.4" -tokio = { version = "1.8", features = ["rt-multi-thread", "macros"] } -dotenv = "0.15.0" +shuttle-runtime = "0.24.0" +tokio = { version = "1.26.0" } +sqlx = { version = "0.7.1", features = [ + "runtime-tokio-native-tls", + "postgres", + "macros", + "chrono", +] } +teloxide = { version = "0.12.0", features = ["macros"] } +shuttle-shared-db = { version = "0.24.0", features = ["postgres", "sqlx"] } +shuttle-secrets = "0.24.0" +reqwest = "0.11.18" +askama = "0.12.0" +tracing = "0.1.37" diff --git a/migrations/20230818165230_schema.sql b/migrations/20230818165230_schema.sql new file mode 100644 index 0000000..bc4cb7a --- /dev/null +++ b/migrations/20230818165230_schema.sql @@ -0,0 +1,12 @@ +-- Add migration script here +CREATE TABLE IF NOT EXISTS users ( + id serial PRIMARY KEY, + chat_id bigserial, + user_name varchar(32) NOT NULL +); +CREATE TABLE IF NOT EXISTS runs ( + id serial PRIMARY KEY, + distance real NOT NULL, + medals integer NOT NULL, + user_id serial REFERENCES users(id) +); \ No newline at end of file diff --git a/migrations/20230819101838_schema.sql b/migrations/20230819101838_schema.sql new file mode 100644 index 0000000..ce4ff17 --- /dev/null +++ b/migrations/20230819101838_schema.sql @@ -0,0 +1,3 @@ +-- Add migration script here +ALTER TABLE users +ALTER COLUMN chat_id TYPE VARCHAR; \ No newline at end of file diff --git a/migrations/20230819143240_schema.sql b/migrations/20230819143240_schema.sql new file mode 100644 index 0000000..344238d --- /dev/null +++ b/migrations/20230819143240_schema.sql @@ -0,0 +1,3 @@ +-- Add migration script here +ALTER TABLE users +ADD CONSTRAINT unique_user_chat UNIQUE(chat_id, user_name); \ No newline at end of file diff --git a/migrations/20230819143740_schema.sql b/migrations/20230819143740_schema.sql new file mode 100644 index 0000000..9a3a410 --- /dev/null +++ b/migrations/20230819143740_schema.sql @@ -0,0 +1,2 @@ +-- Add migration script here +ALTER TABLE runs DROP COLUMN medals; \ No newline at end of file diff --git a/migrations/20230820141626_schema.sql b/migrations/20230820141626_schema.sql new file mode 100644 index 0000000..f4e597b --- /dev/null +++ b/migrations/20230820141626_schema.sql @@ -0,0 +1,6 @@ +-- Add migration script here +ALTER TABLE runs +ADD COLUMN run_datetime TIMESTAMP; +ALTER TABLE runs +ALTER COLUMN run_datetime +SET DEFAULT now(); \ No newline at end of file diff --git a/src/bot.rs b/src/bot.rs new file mode 100644 index 0000000..0031af1 --- /dev/null +++ b/src/bot.rs @@ -0,0 +1,155 @@ +use crate::{ + database::*, + message::{display_tally, list_runs, list_users}, +}; +use sqlx::PgPool; +use teloxide::{prelude::*, utils::command::BotCommands}; +use tracing::error; + +pub struct BotService { + pub bot: Bot, + pub postgres: PgPool, +} + +#[shuttle_runtime::async_trait] +impl shuttle_runtime::Service for BotService { + async fn bind(self, _addr: std::net::SocketAddr) -> Result<(), shuttle_runtime::Error> { + // Start your service and bind to the socket address + self.start() + .await + .expect("An error occured while using the bot!"); + + Ok(()) + } +} + +impl BotService { + async fn start(&self) -> Result<(), shuttle_runtime::CustomError> { + let bot = self.bot.clone(); + let db_connection = self.postgres.clone(); + + Command::repl(bot, move |bot, msg, cmd| { + answer(bot, msg, cmd, db_connection.clone()) + }) + .await; + + Ok(()) + } +} + +#[derive(BotCommands, Clone)] +#[command( + rename_rule = "lowercase", + description = "The following commands are supported:" +)] +enum Command { + #[command(description = "display this text")] + Help, + #[command(description = "Show users registered on telerun within the chat.")] + Show, + #[command( + description = "Add run data to database. Format is /add %distance (km)% %username%", + parse_with = "split" + )] + Add { distance: f32, user_name: String }, + #[command(description = "Edit data for a run.", parse_with = "split")] + Edit { run_id: i32, distance: f32 }, + #[command(description = "Remove a run from database.")] + Delete { run_id: i32 }, + #[command(description = "Tallies current medals and distances.")] + Tally, + #[command(description = "Lists recent runs. Number of runs to display must be specified.")] + List { limit: u32 }, +} + +async fn answer(bot: Bot, msg: Message, cmd: Command, db_connection: PgPool) -> ResponseResult<()> { + match cmd { + Command::Help => { + bot.send_message(msg.chat.id, Command::descriptions().to_string()) + .await?; + } + Command::Show => { + let users = get_users_in_chat(msg.chat.id, &db_connection).await; + if let Ok(users) = users { + let show_message = list_users(users); + bot.send_message(msg.chat.id, show_message) + .await + .map_err(|error| error!("Unable to send Show message: {:?}", error)) + .ok(); + } else { + error!("Unable to retrieve items required for Show."); + } + } + Command::Add { + distance, + user_name, + } => { + let add_result = + add_run_wrapper(distance, user_name.as_str(), msg.chat.id, &db_connection).await; + if add_result.is_ok() { + bot.send_message( + msg.chat.id, + format!("{} ran {}km added to database.", user_name, distance), + ) + .await + .map_err(|error| error!("Unable to send Add message: {:?}", error)) + .ok(); + } else { + error!("Unable to Add run information."); + } + } + Command::Edit { run_id, distance } => { + let update_outcome = update_run(run_id, distance, &db_connection).await; + if update_outcome.is_ok() { + bot.send_message( + msg.chat.id, + format!( + "Run {} successfully updated with distance {}km.", + run_id, distance + ), + ) + .await + .map_err(|error| error!("Unable to send update message: {:?}", error)) + .ok(); + } else { + error!("Unable to update database entry for run_id: {}", run_id); + } + } + Command::Delete { run_id } => { + let delete_outcome = delete_run(run_id, &db_connection).await; + if delete_outcome.is_ok() { + bot.send_message(msg.chat.id, format!("Run {} successfully deleted!", run_id)) + .await + .map_err(|error| error!("Unable to send delete message: {:?}", error)) + .ok(); + } else { + error!("Unable to delete entry from database."); + } + } + Command::Tally => { + let tally = get_tally(msg.chat.id, &db_connection).await; + if let Ok(tally) = tally { + let tally_message = display_tally(tally); + bot.send_message(msg.chat.id, tally_message) + .await + .map_err(|err| error!("Unable to send Tally message: {:?}", err)) + .ok(); + } else { + error!("Unable to retrieve tally from database."); + } + } + Command::List { limit } => { + let runs = get_runs(msg.chat.id, limit.into(), &db_connection).await; + if let Ok(runs) = runs { + let run_message = list_runs(runs); + bot.send_message(msg.chat.id, run_message) + .await + .map_err(|err| error!("Unable to send List message: {:?}", err)) + .ok(); + } else { + error!("Unable to retrieve runs from database."); + } + } + } + Ok(()) +} diff --git a/src/database.rs b/src/database.rs new file mode 100644 index 0000000..9abde55 --- /dev/null +++ b/src/database.rs @@ -0,0 +1,195 @@ +use crate::models::{Run, Score, User}; +use sqlx::PgPool; +use teloxide::types::ChatId; +use tracing::error; + +type DBResult = Result; + +pub async fn create_user(user_name: &str, chat_id: ChatId, connection: &PgPool) -> DBResult<()> { + sqlx::query!( + "INSERT INTO users (chat_id, user_name) + VALUES ($1, $2) + ON CONFLICT (chat_id, user_name) DO NOTHING", + chat_id.to_string(), + user_name + ) + .execute(connection) + .await?; + + Ok(()) +} + +async fn get_user(user_name: &str, chat_id: ChatId, connection: &PgPool) -> DBResult> { + let user: Option = sqlx::query_as!( + User, + "SELECT id, chat_id, user_name + FROM users + WHERE user_name = $1 AND chat_id = $2", + user_name, + chat_id.to_string() + ) + .fetch_optional(connection) + .await?; + + Ok(user) +} + +pub async fn get_users_in_chat( + chat_id: ChatId, + connection: &PgPool, +) -> DBResult>> { + let users: Vec = sqlx::query!( + "SELECT id, chat_id, user_name + FROM users + WHERE chat_id = $1", + chat_id.to_string() + ) + .fetch_all(connection) + .await? + .iter() + .map(|user_row| User { + id: user_row.id, + chat_id: user_row.chat_id.clone(), + user_name: user_row.user_name.clone(), + }) + .collect(); + + if !users.is_empty() { + Ok(Some(users)) + } else { + Ok(None) + } +} + +// TODO: refactor in the future +pub async fn add_run_wrapper( + distance: f32, + user_name: &str, + chat_id: ChatId, + connection: &PgPool, +) -> DBResult<()> { + let user = get_user(user_name, chat_id, connection).await?; + + if let Some(user) = user { + add_run(distance, user.id, connection).await?; + } else { + create_user(user_name, chat_id, connection).await?; + let user = get_user(user_name, chat_id, connection).await?; + if let Some(user) = user { + add_run(distance, user.id, connection).await?; + } else { + error!("Unable to add run to database."); + } + } + + Ok(()) +} + +async fn add_run(distance: f32, user_id: i32, connection: &PgPool) -> DBResult<()> { + sqlx::query!( + "INSERT INTO runs (distance, user_id) + VALUES ($1, $2) + ", + distance, + user_id, + ) + .execute(connection) + .await?; + + Ok(()) +} + +pub async fn get_runs( + chat_id: ChatId, + limit: i64, + connection: &PgPool, +) -> DBResult>> { + let users_in_chat = get_users_in_chat(chat_id, connection).await?; + if let Some(users) = users_in_chat { + let user_ids: Vec = users.iter().map(|user| user.id).collect(); + let runs: Vec<_> = sqlx::query!( + "SELECT * + FROM runs + WHERE user_id = ANY($1) + ORDER BY run_datetime DESC + LIMIT $2", + &user_ids[..], + limit + ) + .fetch_all(connection) + .await? + .iter() + .map(|row| Run { + id: row.id, + distance: row.distance, + run_datetime: row.run_datetime, + user_id: row.user_id, + }) + .collect(); + + if !runs.is_empty() { + Ok(Some(runs)) + } else { + Ok(None) + } + } else { + Ok(None) + } +} + +pub async fn update_run(run_id: i32, distance: f32, connection: &PgPool) -> DBResult<()> { + sqlx::query!( + "UPDATE runs + SET distance = $1 + WHERE id = $2", + distance, + run_id, + ) + .execute(connection) + .await?; + + Ok(()) +} + +pub async fn delete_run(run_id: i32, connection: &PgPool) -> DBResult<()> { + sqlx::query!( + "DELETE FROM runs + WHERE id = $1", + run_id, + ) + .execute(connection) + .await?; + + Ok(()) +} + +pub async fn get_tally(chat_id: ChatId, connection: &PgPool) -> DBResult>> { + let users = get_users_in_chat(chat_id, connection).await?; + + if let Some(users) = users { + let user_ids: Vec = users.iter().map(|user| user.id).collect(); + let tally = sqlx::query!( + "SELECT user_name, COUNT(*), SUM(distance) + FROM runs + JOIN users ON users.id = runs.user_id + WHERE user_id = ANY($1) + GROUP BY user_name", + &user_ids[..], + ) + .fetch_all(connection) + .await?; + + let scores: Vec = tally + .iter() + .map(|tally| Score { + user_name: tally.user_name.clone(), + medals: tally.count.unwrap() as u32, + distance: tally.sum.unwrap(), + }) + .collect(); + + return Ok(Some(scores)); + } + + Ok(None) +} diff --git a/src/main.rs b/src/main.rs index bb4b934..2fadd9d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,29 @@ -use dotenv::dotenv; -use teloxide::{dispatching::dialogue::InMemStorage, prelude::*}; +mod bot; +mod database; +mod message; +mod models; -type MyDialogue = Dialogue>; -type HandlerResult = Result<(), Box>; +use bot::BotService; +use shuttle_secrets::SecretStore; +use sqlx::PgPool; +use teloxide::prelude::*; -#[derive(Clone, Default)] -pub enum State { - #[default] - Start, -} +#[shuttle_runtime::main] +async fn shuttle_main( + #[shuttle_secrets::Secrets] secrets: SecretStore, + #[shuttle_shared_db::Postgres] postgres: PgPool, +) -> Result { + sqlx::migrate!() + .run(&postgres) + .await + .expect("ERROR: Could not carry out migrations!"); -fn main() { - dotenv().ok(); // Load env variables + let teloxide_key = secrets + .get("TELOXIDE_TOKEN") + .expect("TELOXIDE_TOKEN needs to be set."); - let telebot_api_token = std::env::var("TELOXIDE_TOKEN").expect("TELOXIDE_TOKEN must set."); + Ok(BotService { + bot: Bot::new(teloxide_key), + postgres, + }) } diff --git a/src/message.rs b/src/message.rs new file mode 100644 index 0000000..b590f67 --- /dev/null +++ b/src/message.rs @@ -0,0 +1,168 @@ +use crate::models::{Run, Score, User}; +use askama::Template; +use std::fmt; +use std::ops; + +struct RunDisplay(Run); + +impl ops::Deref for RunDisplay { + type Target = Run; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl fmt::Display for RunDisplay { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // No longer need to access via self.0.id, since we deref + // a RunDisplay into a Run! + write!( + f, + "{} {} {} {}", + self.id, + self.distance, + self.run_datetime + .map(|x| x.to_string()) + .unwrap_or("NULL".to_string()), + self.user_id + ) + } +} + +impl fmt::Display for User { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} {}", self.id, self.user_name) + } +} + +impl fmt::Display for Score { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} {} {}", self.user_name, self.medals, self.distance) + } +} + +#[derive(Template)] +#[template(path = "list_runs.j2")] +struct ListRunTemplate<'a> { + runs: &'a Vec, +} + +pub fn list_runs(runs: Option>) -> String { + if let Some(runs) = runs { + let run_displays: Vec = runs.into_iter().map(RunDisplay).collect(); + let run_template = ListRunTemplate { + runs: &run_displays, + }; + + run_template.render().unwrap().to_string() + } else { + "No runs in database.".into() + } +} + +#[derive(Template)] +#[template(path = "list_users.j2")] +struct ListUserTemplate<'a> { + users: &'a Vec, +} + +pub fn list_users(users: Option>) -> String { + if let Some(users) = users { + let user_template = ListUserTemplate { users: &users }; + + user_template.render().unwrap().to_string() + } else { + "No users in database.".into() + } +} + +#[derive(Template)] +#[template(path = "list_tally.j2")] +struct ListTallyTemplate<'a> { + scores: &'a Vec, +} + +pub fn display_tally(scores: Option>) -> String { + if let Some(scores) = scores { + let tally_template = ListTallyTemplate { scores: &scores }; + + tally_template.render().unwrap().to_string() + } else { + "Cannot generate tally.".into() + } +} + +#[cfg(test)] +mod tests { + use std::vec; + + use sqlx::types::chrono; + + use super::*; + + #[test] + fn list_runs_template() { + let runs = vec![ + Run { + id: 1, + distance: 1., + run_datetime: chrono::NaiveDateTime::from_timestamp_opt(61, 0), + user_id: 1, + }, + Run { + id: 2, + distance: 2., + run_datetime: chrono::NaiveDateTime::from_timestamp_opt(82, 0), + user_id: 2, + }, + ]; + let render = list_runs(Some(runs)); + // TODO: i actually dont want this kind of html templates anyway + let ans = " + 1. 1 1 1970-01-01 00:01:01 1 + + 2. 2 2 1970-01-01 00:01:22 2 +"; + assert_eq!(render, ans); + } + + #[test] + fn list_empty_runs_template() { + let runs: Option> = None; + let render = list_runs(runs); + let ans = "No runs in database."; + assert_eq!(render, ans); + } + + #[test] + fn list_users_template() { + let users = vec![ + User { + id: 1, + chat_id: "chat1".into(), + user_name: "meme".into(), + }, + User { + id: 2, + chat_id: "chat1".into(), + user_name: "youyou".into(), + }, + ]; + let render = list_users(Some(users)); + let ans = " + 1. 1 meme + + 2. 2 youyou +"; + assert_eq!(render, ans); + } + + #[test] + fn list_empty_users_template() { + let users: Option> = None; + let render = list_users(users); + let ans = "No users in database."; + assert_eq!(render, ans); + } +} diff --git a/src/models.rs b/src/models.rs new file mode 100644 index 0000000..0a111af --- /dev/null +++ b/src/models.rs @@ -0,0 +1,22 @@ +use sqlx::types::chrono; + +#[derive(sqlx::FromRow)] +pub struct User { + pub id: i32, + pub chat_id: String, + pub user_name: String, +} + +#[derive(sqlx::FromRow)] +pub struct Run { + pub id: i32, + pub distance: f32, + pub run_datetime: Option, + pub user_id: i32, +} + +pub struct Score { + pub user_name: String, + pub medals: u32, + pub distance: f32, +} diff --git a/templates/list_runs.j2 b/templates/list_runs.j2 new file mode 100644 index 0000000..66f9e35 --- /dev/null +++ b/templates/list_runs.j2 @@ -0,0 +1,3 @@ +{% for run in runs %} + {{ loop.index }}. {{ run }} +{% endfor %} diff --git a/templates/list_tally.j2 b/templates/list_tally.j2 new file mode 100644 index 0000000..ed46c15 --- /dev/null +++ b/templates/list_tally.j2 @@ -0,0 +1,3 @@ +{% for score in scores %} + {{ loop.index }}. {{ score }} +{% endfor %} \ No newline at end of file diff --git a/templates/list_users.j2 b/templates/list_users.j2 new file mode 100644 index 0000000..751c619 --- /dev/null +++ b/templates/list_users.j2 @@ -0,0 +1,3 @@ +{% for user in users %} + {{ loop.index }}. {{ user }} +{% endfor %}