Index: Cargo.lock ================================================================== --- Cargo.lock +++ Cargo.lock @@ -66,24 +66,10 @@ name = "anyhow" version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" -[[package]] -name = "aquamarine" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f50776554130342de4836ba542aa85a4ddb361690d7e8df13774d7284c3d5c2" -dependencies = [ - "include_dir", - "itertools", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "async-attributes" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" @@ -247,10 +233,21 @@ [[package]] name = "async-task" version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] [[package]] name = "atoi" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -346,10 +343,35 @@ "async-task", "futures-io", "futures-lite 2.6.0", "piper", ] + +[[package]] +name = "bon" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94054366e2ff97b455acdd4fdb03913f717febc57b7bbd1741b2c3b87efae030" +dependencies = [ + "bon-macros", + "rustversion", +] + +[[package]] +name = "bon-macros" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "542a990e676ce0a0a895ae54b2d94afd012434f2228a85b186c6bc1a7056cdc6" +dependencies = [ + "darling", + "ident_case", + "prettyplease", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.100", +] [[package]] name = "brotli" version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -374,16 +396,10 @@ name = "bumpalo" version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" -[[package]] -name = "bytemuck" -version = "1.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" - [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" @@ -575,20 +591,10 @@ "const-oid", "pem-rfc7468", "zeroize", ] -[[package]] -name = "deranged" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" -dependencies = [ - "powerfmt", - "serde", -] - [[package]] name = "derive_builder" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" @@ -616,31 +622,10 @@ dependencies = [ "derive_builder_core", "syn 2.0.100", ] -[[package]] -name = "derive_more" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" -dependencies = [ - "derive_more-impl", -] - -[[package]] -name = "derive_more-impl" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", - "unicode-xid", -] - [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" @@ -675,19 +660,10 @@ name = "dotenvy" version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" -[[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.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" @@ -708,20 +684,10 @@ name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" -[[package]] -name = "erasable" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "437cfb75878119ed8265685c41a115724eae43fb7cc5a0bf0e4ecc3b803af1c4" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "errno" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" @@ -837,10 +803,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] + +[[package]] +name = "frankenstein" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb8f97b9b2fefaa34071c0ccb9df53d7d15d5c4a375c07cd701e133dac048af" +dependencies = [ + "async-trait", + "bon", + "macro_rules_attribute", + "paste", + "reqwest", + "serde", + "serde_json", + "serde_with", + "thiserror 2.0.12", + "tokio", +] [[package]] name = "futures" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1033,23 +1017,17 @@ "bytes", "fnv", "futures-core", "futures-sink", "http", - "indexmap 2.9.0", + "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 = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" @@ -1063,11 +1041,11 @@ name = "hashlink" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.15.2", + "hashbrown", ] [[package]] name = "heck" version = "0.5.0" @@ -1400,49 +1378,18 @@ dependencies = [ "icu_normalizer", "icu_properties", ] -[[package]] -name = "include_dir" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" -dependencies = [ - "include_dir_macros", -] - -[[package]] -name = "include_dir_macros" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - [[package]] name = "indexmap" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.2", - "serde", + "hashbrown", ] [[package]] name = "instant" version = "0.1.13" @@ -1467,19 +1414,10 @@ name = "ipnet" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" -[[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.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" @@ -1575,10 +1513,26 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" dependencies = [ "value-bag", ] +[[package]] +name = "macro_rules_attribute" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a82271f7bc033d84bbca59a3ce3e4159938cb08a9c3aebbe54d215131518a13" +dependencies = [ + "macro_rules_attribute-proc_macro", + "paste", +] + +[[package]] +name = "macro_rules_attribute-proc_macro" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dd856d451cc0da70e2ef2ce95a18e39a93b7558bedf10201ad28503f918568" + [[package]] name = "md-5" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" @@ -1667,16 +1621,10 @@ "rand 0.8.5", "smallvec", "zeroize", ] -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - [[package]] name = "num-integer" version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" @@ -1791,10 +1739,16 @@ "redox_syscall", "smallvec", "windows-targets 0.52.6", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "pathdiff" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" @@ -1812,30 +1766,10 @@ 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.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" @@ -1913,16 +1847,10 @@ "rustix 0.38.44", "tracing", "windows-sys 0.59.0", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" @@ -1929,28 +1857,17 @@ dependencies = [ "zerocopy", ] [[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", +name = "prettyplease" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" +dependencies = [ + "proc-macro2", + "syn 2.0.100", ] [[package]] name = "proc-macro2" version = "1.0.95" @@ -1958,19 +1875,10 @@ checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] -[[package]] -name = "psm" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f58e5423e24c18cc840e1c98370b3993c6649cd1678b4d24318bcf0a083cbe88" -dependencies = [ - "cc", -] - [[package]] name = "quick-xml" version = "0.37.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4ce8c88de324ff838700f36fb6ab86c96df0e3c4ab6ef3a9b2044465cce1369" @@ -2105,19 +2013,10 @@ checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ "getrandom 0.3.2", ] -[[package]] -name = "rc-box" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897fecc9fac6febd4408f9e935e86df739b0023b625e610e0357535b9c8adad0" -dependencies = [ - "erasable", -] - [[package]] name = "redox_syscall" version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" @@ -2206,19 +2105,10 @@ "web-sys", "webpki-roots", "windows-registry", ] -[[package]] -name = "rgb" -version = "0.8.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" -dependencies = [ - "bytemuck", -] - [[package]] name = "ring" version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" @@ -2270,19 +2160,19 @@ "anyhow", "async-std", "atom_syndication", "chrono", "config", + "frankenstein", "futures", "futures-util", "lazy_static", "regex", "reqwest", "rss", "sedregex", "sqlx", - "teloxide", "thiserror 2.0.12", ] [[package]] name = "rustc-demangle" @@ -2495,20 +2385,13 @@ name = "serde_with" version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" dependencies = [ - "base64", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.9.0", "serde", "serde_derive", - "serde_json", "serde_with_macros", - "time", ] [[package]] name = "serde_with_macros" version = "3.12.0" @@ -2547,19 +2430,10 @@ name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "signal-hook-registry" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" -dependencies = [ - "libc", -] - [[package]] name = "signature" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" @@ -2655,13 +2529,13 @@ "event-listener 5.4.0", "futures-core", "futures-intrusive", "futures-io", "futures-util", - "hashbrown 0.15.2", + "hashbrown", "hashlink", - "indexmap 2.9.0", + "indexmap", "log", "memchr", "once_cell", "percent-encoding", "rustls", @@ -2824,23 +2698,10 @@ name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "stacker" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601f9201feb9b09c00266478bf459952b9ef9a6b94edb2f21eba14ab681a60a9" -dependencies = [ - "cc", - "cfg-if", - "libc", - "psm", - "windows-sys 0.59.0", -] - [[package]] name = "stringprep" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" @@ -2923,92 +2784,10 @@ 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.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cb7c03a9217286fe11021dc72d5a674acbb0d3b24ba38d04f7efe7920e4948" -dependencies = [ - "aquamarine", - "bytes", - "derive_more", - "dptree", - "either", - "futures", - "log", - "mime", - "pin-project", - "serde", - "serde_json", - "teloxide-core", - "teloxide-macros", - "thiserror 2.0.12", - "tokio", - "tokio-stream", - "tokio-util", - "url", -] - -[[package]] -name = "teloxide-core" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2f70a3cd58c2b31ca899691b99573a40c6da713ab230bb78bbb4fb0b5c751a" -dependencies = [ - "bitflags 2.9.0", - "bytes", - "chrono", - "derive_more", - "either", - "futures", - "log", - "mime", - "once_cell", - "pin-project", - "rc-box", - "reqwest", - "rgb", - "serde", - "serde_json", - "serde_with", - "stacker", - "take_mut", - "takecell", - "thiserror 2.0.12", - "tokio", - "tokio-util", - "url", - "uuid", -] - -[[package]] -name = "teloxide-macros" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3118a980ed2ec11f73d9495a6606905bd74726e3ffe95a42fbeb187ded8fdbf4" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "tempfile" version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" @@ -3058,41 +2837,10 @@ "proc-macro2", "quote", "syn 2.0.100", ] -[[package]] -name = "time" -version = "0.3.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" - -[[package]] -name = "time-macros" -version = "0.2.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "tinystr" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" @@ -3125,11 +2873,10 @@ "backtrace", "bytes", "libc", "mio", "pin-project-lite", - "signal-hook-registry", "socket2 0.5.9", "windows-sys 0.52.0", ] [[package]] @@ -3162,21 +2909,10 @@ "futures-util", "thiserror 1.0.69", "tokio", ] -[[package]] -name = "tokio-stream" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - [[package]] name = "tokio-util" version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" @@ -3213,11 +2949,11 @@ name = "toml_edit" version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ - "indexmap 2.9.0", + "indexmap", "serde", "serde_spanned", "toml_datetime", "winnow", ] @@ -3324,16 +3060,10 @@ name = "unicode-properties" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "untrusted" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" @@ -3345,11 +3075,10 @@ checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", "percent-encoding", - "serde", ] [[package]] name = "utf16_iter" version = "1.0.5" @@ -3360,19 +3089,10 @@ name = "utf8_iter" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" -[[package]] -name = "uuid" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" -dependencies = [ - "getrandom 0.3.2", -] - [[package]] name = "value-bag" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" Index: Cargo.toml ================================================================== --- Cargo.toml +++ Cargo.toml @@ -8,20 +8,19 @@ anyhow = "1.0.86" async-std = { version = "1.12.0", features = [ "attributes", "tokio1" ] } atom_syndication = { version = "0.12.4", features = [ "with-serde" ] } chrono = "0.4.38" config = { version = "0.15", default-features = false, features = [ "toml" ] } +frankenstein = { version = "0.40.0", features = [ "client-reqwest" ] } futures = "0.3.30" futures-util = "0.3.30" lazy_static = "1.5.0" regex = "1.10.6" reqwest = { version = "0.12.7", features = [ "brotli", "socks", "deflate" ]} rss = "2.0.9" sedregex = "0.2.5" sqlx = { version = "0.8", features = [ "postgres", "runtime-async-std-rustls", "chrono", "macros" ], default-features = false } -#telegram-bot = { git = "https://github.com/kworr/telegram-bot", features = [ "rustls" ], default-features = false } -teloxide = { version = "0.15.0", default-features = false, features = [ "ctrlc_handler", "macros", "rustls" ] } thiserror = "2.0.0" [profile.release] lto = true codegen-units = 1 Index: src/command.rs ================================================================== --- src/command.rs +++ src/command.rs @@ -1,64 +1,48 @@ use crate::core::Core; +use std::borrow::Cow; + use anyhow::{ bail, Context, Result +}; +use frankenstein::{ + methods::{ + GetChatAdministratorsParams, + GetChatParams, + }, + types::{ + ChatId, + ChatMember, + }, + AsyncTelegramApi, + ParseMode, }; use lazy_static::lazy_static; use regex::Regex; use sedregex::ReplaceCommand; -use std::borrow::Cow; -use teloxide::{ - Bot, - dispatching::dialogue::GetChatId, - payloads::GetChatAdministrators, - requests::{ - Requester, - ResponseResult - }, - types::{ - Message, - UserId, - }, - utils::command::BotCommands, -}; lazy_static! { static ref RE_USERNAME: Regex = Regex::new(r"^@[a-zA-Z][a-zA-Z0-9_]+$").unwrap(); static ref RE_LINK: Regex = Regex::new(r"^https?://[a-zA-Z.0-9-]+/[-_a-zA-Z.:0-9/?=]+$").unwrap(); static ref RE_IV_HASH: Regex = Regex::new(r"^[a-f0-9]{14}$").unwrap(); } -#[derive(BotCommands, Clone)] -#[command(rename_rule = "lowercase", description = "Supported commands:")] -enum Command { - #[command(description = "display this help.")] - Help, - #[command(description = "Does nothing.")] - Start, - #[commant(description = "List active subscriptions.")] - List, -} - -pub async fn cmd_handler(bot: Bot, msg: Message, cmd: Command) -> ResponseResult<()> { - match cmd { - Command::Help => bot.send_message(msg.chat.id, Command::descriptions().to_string()).await?, - Command::Start => bot.send_message(msg.chat.id, - "We are open. Probably. Visit [channel](https://t.me/rsstg_bot_help/3) for details.").await?, - Command::List => bot.send_message(msg.chat.id, core.list(msg.from).await?).await?, - }; - Ok(()) -} - -pub async fn list(core: &Core, sender: UserId) -> Result<()> { - core.send(core.list(sender).await?, Some(sender), Some(telegram_bot::types::ParseMode::MarkdownV2)).await?; - Ok(()) -} - -pub async fn command(core: &Core, sender: telegram_bot::UserId, command: Vec<&str>) -> Result<()> { +pub async fn start(core: &Core, chat_id: i64) -> Result<()> { + core.send("We are open\\. Probably\\. Visit [channel](https://t.me/rsstg_bot_help/3) for details\\.", + Some(chat_id), Some(ParseMode::MarkdownV2)).await?; + Ok(()) +} + +pub async fn list(core: &Core, sender: i64) -> Result<()> { + core.send(core.list(sender).await?, Some(sender), Some(ParseMode::MarkdownV2)).await?; + Ok(()) +} + +pub async fn command(core: &Core, sender: i64, command: Vec<&str>) -> Result<()> { if command.len() >= 2 { let msg: Cow = match &command[1].parse::() { Err(err) => format!("I need a number.\n{}", &err).into(), Ok(number) => match command[0] { "/check" => core.check(number, sender, false).await @@ -75,42 +59,42 @@ core.send("This command needs a number.", Some(sender), None).await?; } Ok(()) } -pub async fn update(core: &Core, sender: telegram_bot::UserId, command: Vec<&str>) -> Result<()> { +pub async fn update(core: &Core, sender: i64, command: Vec<&str>) -> Result<()> { let mut source_id: Option = None; let at_least = "Requires at least 3 parameters."; let mut i_command = command.iter(); let first_word = i_command.next().context(at_least)?; match *first_word { "/update" => { let next_word = i_command.next().context(at_least)?; source_id = Some(next_word.parse::() - .context(format!("I need a number, but got {}.", next_word))?); + .context(format!("I need a number, but got {next_word}."))?); }, "/add" => {}, - _ => bail!("Passing {} is not possible here.", first_word), + _ => bail!("Passing {first_word} is not possible here."), }; let (channel, url, iv_hash, url_re) = ( i_command.next().context(at_least)?, i_command.next().context(at_least)?, i_command.next(), i_command.next()); if ! RE_USERNAME.is_match(channel) { - bail!("Usernames should be something like \"@\\[a\\-zA\\-Z]\\[a\\-zA\\-Z0\\-9\\_]+\", aren't they?\nNot {:?}", &channel); + bail!("Usernames should be something like \"@\\[a\\-zA\\-Z]\\[a\\-zA\\-Z0\\-9\\_]+\", aren't they?\nNot {channel:?}"); }; if ! RE_LINK.is_match(url) { - bail!("Link should be a link to atom/rss feed, something like \"https://domain/path\".\nNot {:?}", &url); + bail!("Link should be a link to atom/rss feed, something like \"https://domain/path\".\nNot {url:?}"); } let iv_hash = match iv_hash { Some(hash) => { match *hash { "-" => None, thing => { if ! RE_IV_HASH.is_match(thing) { - bail!("IV hash should be 14 hex digits.\nNot {:?}", thing); + bail!("IV hash should be 14 hex digits.\nNot {thing:?}"); }; Some(thing) }, } }, @@ -126,22 +110,31 @@ } } }, None => None, }; - let channel_id = i64::from(core.request(telegram_bot::GetChat::new(telegram_bot::ChatRef::ChannelUsername(channel.to_string()))).await?.id()); - let chan_adm = core.request(telegram_bot::GetChatAdministrators::new(telegram_bot::ChatRef::ChannelUsername(channel.to_string()))).await - .context("Sorry, I have no access to that chat.")?; + let chat_id = ChatId::String((*channel).into()); + let channel_id = core.tg.get_chat(&GetChatParams { chat_id: chat_id.clone() }).await?.result.id; + let chan_adm = core.tg.get_chat_administrators(&GetChatAdministratorsParams { chat_id }).await + .context("Sorry, I have no access to that chat.")?.result; let (mut me, mut user) = (false, false); for admin in chan_adm { - if admin.user.id == core.my.id { + let member_id = match admin { + ChatMember::Creator(member) => member.user.id, + ChatMember::Administrator(member) => member.user.id, + ChatMember::Left(_) + | ChatMember::Kicked(_) + | ChatMember::Member(_) + | ChatMember::Restricted(_) => continue, + } as i64; + if member_id == core.me.id as i64 { me = true; }; - if admin.user.id == sender { + if member_id == sender { user = true; }; }; if ! me { bail!("I need to be admin on that channel."); }; if ! user { bail!("You should be admin on that channel."); }; core.send(core.update(source_id, channel, channel_id, url, iv_hash, url_re, sender).await?, Some(sender), None).await?; Ok(()) } Index: src/core.rs ================================================================== --- src/core.rs +++ src/core.rs @@ -1,19 +1,6 @@ -use anyhow::{anyhow, bail, Context, Result}; -use async_std::task; -use chrono::DateTime; -use sqlx::postgres::PgPoolOptions; -use teloxide::{ - Bot, - payloads::SendMessage, - requests::Requester, - types::{ - Me, - UserId, - }, -}; -use thiserror::Error; +use crate::command; use std::{ borrow::Cow, collections::{ BTreeMap, @@ -24,10 +11,35 @@ Arc, Mutex }, }; +use anyhow::{ + anyhow, + bail, + Result, +}; +use async_std::task; +use chrono::DateTime; +use frankenstein::{ + client_reqwest::Bot, + methods::{ + GetUpdatesParams, + SendMessageParams + }, + types::{ + AllowedUpdate, + MessageEntityType, + User, + }, + updates::UpdateContent, + AsyncTelegramApi, + ParseMode, +}; +use sqlx::postgres::PgPoolOptions; +use thiserror::Error; + #[derive(Error, Debug)] pub enum RssError { // #[error(transparent)] // Tg(#[from] TgError), #[error(transparent)] @@ -34,105 +46,128 @@ Int(#[from] TryFromIntError), } #[derive(Clone)] pub struct Core { - owner_chat: UserId, + owner_chat: i64, pub tg: Bot, - pub my: Me, + pub me: User, pool: sqlx::Pool, sources: Arc>>>, http_client: reqwest::Client, } impl Core { - pub fn new(settings: config::Config) -> Result> { - let owner: u64 = settings.get_int("owner")?.try_into()?; + pub async fn new(settings: config::Config) -> Result> { + let owner_chat = settings.get_int("owner")?; let api_key = settings.get_string("api_key")?; - let tg = Bot::new(api_key); - let tg_cloned = tg.clone(); + let tg = Bot::new(&api_key); let mut client = reqwest::Client::builder(); if let Ok(proxy) = settings.get_string("proxy") { let proxy = reqwest::Proxy::all(proxy)?; client = client.proxy(proxy); } let http_client = client.build()?; + let me = tg.get_me().await?; + let me = me.result; let core = Arc::new(Core { tg, - my: task::block_on(async { - tg_cloned.get_me().await - })?, - owner_chat: UserId(owner), + me, + owner_chat, pool: PgPoolOptions::new() .max_connections(5) .acquire_timeout(std::time::Duration::new(300, 0)) .idle_timeout(std::time::Duration::new(60, 0)) .connect_lazy(&settings.get_string("pg")?)?, sources: Arc::new(Mutex::new(HashSet::new())), http_client, }); - /* let clone = core.clone(); + let clone = core.clone(); task::spawn(async move { loop { let delay = match &clone.autofetch().await { Err(err) => { - if let Err(err) = clone.send(format!("🛑 {:?}", err), None, None).await { - eprintln!("Autofetch error: {}", err); + if let Err(err) = clone.send(format!("🛑 {err:?}"), None, None).await { + eprintln!("Autofetch error: {err:?}"); }; std::time::Duration::from_secs(60) }, Ok(time) => *time, }; task::sleep(delay).await; } - }); */ + }); Ok(core) } - pub fn stream(&self) -> Result<()> { - let mut last_update: Option = None; - loop { - let updates = self.tg.get_updates(last_update, None, 300, Some(vec!["message"])); - } - Ok(()) - } - - /* - pub async fn send<'a, S>(&self, msg: S, target: Option, mode: Option) -> Result<()> - where S: Into> { - let mode = mode.unwrap_or(telegram_bot::types::ParseMode::Html); - let target = target.unwrap_or(self.owner_chat); - self.request(telegram_bot::SendMessage::new(target, msg).parse_mode(mode)).await?; - Ok(()) - } */ - - /* pub async fn request (&self, req: Req) -> Result<::Type, RssError> { - loop { - let res = self.tg.send(&req).await; - match res { - Ok(_) => return Ok(res?), - Err(err) => { - match &err { - TgError::Raw(TgrError::TelegramError { description: _, parameters: Some(params) }) => { - if let Some(delay) = params.retry_after { - println!("Throttled, waiting {} senconds.", delay); - task::sleep(std::time::Duration::from_secs(delay.try_into()?)).await; - } else { - return Err(err.into()); - } - }, - _ => return Err(err.into()), - } - }, - }; - } - } */ - - /* pub async fn check(&self, id: &i32, owner: S, real: bool) -> Result> - where S: Into { - let owner = owner.into(); + pub async fn stream(&self) -> Result<()> { + let mut offset: i64 = 0; + let mut params = GetUpdatesParams { + offset: None, + limit: Some(100), + timeout: Some(300), + allowed_updates: Some(vec![AllowedUpdate::Message]), + }; + loop { + let updates = self.tg.get_updates(¶ms).await?.result; + if updates.is_empty() { + offset = 0; + params.offset = None; + continue; + } + for update in updates { + if i64::from(update.update_id) >= offset { + offset = i64::from(update.update_id) + 1; + params.offset = Some(offset); + } + if let UpdateContent::Message(msg) = update.content { + if let Some(text) = msg.text { + if let Some(entities) = msg.entities { + let chars: Vec = text.encode_utf16().collect(); + for entity in entities { + if entity.type_field == MessageEntityType::BotCommand && entity.offset != 0 { + bail!("commands should be at message start"); + }; + let cmd = String::from_utf16_lossy(&chars[entity.offset as usize..entity.length as usize]); + let words: Vec<&str> = text.split_whitespace().collect(); + match cmd.as_ref() { + "/check" | "/clean" | "/enable" | "/delete" | "/disable" => { command::command(self, msg.chat.id, words).await? }, + "/start" => { command::start(self, msg.chat.id).await?; }, + "/list" => { command::list(self, msg.chat.id).await?; }, + "/add" | "/update" => { command::update(self, msg.chat.id, words).await?; }, + any => { + self.send(format!("\\#error\n```\nUnknown command: {any}\n```"), + Some(msg.chat.id), + Some(ParseMode::MarkdownV2) + ).await?; + }, + }; + }; + }; + }; + }; + } + } + } + + pub async fn send (&self, msg: S, target: Option, mode: Option) -> Result<()> + where S: Into { + let msg = msg.into(); + + let mode = mode.unwrap_or(ParseMode::Html); + let target = target.unwrap_or(self.owner_chat); + let send = SendMessageParams::builder() + .chat_id(target) + .text(msg) + .parse_mode(mode) + .build(); + self.tg.send_message(&send).await?; + Ok(()) + } + + pub async fn check (&self, id: &i32, owner: i64, real: bool) -> Result> { let mut posted: i32 = 0; let mut conn = self.pool.acquire().await?; let id = { let mut set = self.sources.lock().unwrap(); @@ -148,12 +183,12 @@ let count = Arc::strong_count(&id); if count == 2 { let source = sqlx::query!("select source_id, channel_id, url, iv_hash, owner, url_re from rsstg_source where source_id = $1 and owner = $2", *id, owner).fetch_one(&mut *conn).await?; let destination = match real { - true => telegram_bot::UserId::new(source.channel_id), - false => telegram_bot::UserId::new(source.owner), + true => source.channel_id, + false => source.owner, }; let mut this_fetch: Option> = None; let mut posts: BTreeMap, String> = BTreeMap::new(); let response = self.http_client.get(&source.url).send().await?; @@ -181,16 +216,16 @@ let url = item.links()[0].href(); posts.insert(*date, url.to_string()); }; }, Err(err) => { - bail!("Unsupported or mangled content:\n{:?}\n{:#?}\n{:#?}\n", &source.url, err, status) + bail!("Unsupported or mangled content:\n{:?}\n{err:#?}\n{status:#?}\n", &source.url) }, } }, rss::Error::Eof => (), - _ => bail!("Unsupported or mangled content:\n{:?}\n{:#?}\n{:#?}\n", &source.url, err, status) + _ => bail!("Unsupported or mangled content:\n{:?}\n{err:#?}\n{status:#?}\n", &source.url) } }; for (date, url) in posts.iter() { let post_url: Cow = match source.url_re { Some(ref x) => sedregex::ReplaceCommand::new(x)?.execute(url), @@ -200,15 +235,14 @@ &post_url, *id).fetch_one(&mut *conn).await?.exists { if ! exists { if this_fetch.is_none() || *date > this_fetch.unwrap() { this_fetch = Some(*date); }; - self.request( match &source.iv_hash { - Some(hash) => telegram_bot::SendMessage::new(destination, format!(" {0}", &post_url, hash)), - None => telegram_bot::SendMessage::new(destination, format!("{}", post_url)), - }.parse_mode(telegram_bot::types::ParseMode::Html)).await - .context("Can't post message:")?; + self.send( match &source.iv_hash { + Some(hash) => format!(" {post_url}"), + None => format!("{post_url}"), + }, Some(destination), Some(ParseMode::Html)).await?; sqlx::query!("insert into rsstg_post (source_id, posted, url) values ($1, $2, $3);", *id, date, &post_url).execute(&mut *conn).await?; }; }; posted += 1; @@ -215,62 +249,48 @@ }; posts.clear(); }; sqlx::query!("update rsstg_source set last_scrape = now() where source_id = $1;", *id).execute(&mut *conn).await?; - Ok(format!("Posted: {}", &posted).into()) - } */ + Ok(format!("Posted: {posted}").into()) + } - /* pub async fn delete(&self, source_id: &i32, owner: S) -> Result> - where S: Into { - let owner = owner.into(); - + pub async fn delete (&self, source_id: &i32, owner: i64) -> Result> { match sqlx::query!("delete from rsstg_source where source_id = $1 and owner = $2;", source_id, owner).execute(&mut *self.pool.acquire().await?).await?.rows_affected() { 0 => { Ok("No data found found.".into()) }, x => { Ok(format!("{} sources removed.", x).into()) }, } - } */ + } - /* pub async fn clean(&self, source_id: &i32, owner: S) -> Result> - where S: Into { - let owner = owner.into(); - + pub async fn clean (&self, source_id: &i32, owner: i64) -> Result> { match sqlx::query!("delete from rsstg_post p using rsstg_source s where p.source_id = $1 and owner = $2 and p.source_id = s.source_id;", source_id, owner).execute(&mut *self.pool.acquire().await?).await?.rows_affected() { 0 => { Ok("No data found found.".into()) }, - x => { Ok(format!("{} posts purged.", x).into()) }, - } - } */ - - /* pub async fn enable(&self, source_id: &i32, owner: S) -> Result<&str> - where S: Into { - let owner = owner.into(); - + x => { Ok(format!("{x} posts purged.").into()) }, + } + } + + pub async fn enable (&self, source_id: &i32, owner: i64) -> Result<&str> { match sqlx::query!("update rsstg_source set enabled = true where source_id = $1 and owner = $2", source_id, owner).execute(&mut *self.pool.acquire().await?).await?.rows_affected() { 1 => { Ok("Source enabled.") }, 0 => { Ok("Source not found.") }, _ => { Err(anyhow!("Database error.")) }, } - } */ + } - /* pub async fn disable(&self, source_id: &i32, owner: S) -> Result<&str> - where S: Into { - let owner = owner.into(); - + pub async fn disable (&self, source_id: &i32, owner: i64) -> Result<&str> { match sqlx::query!("update rsstg_source set enabled = false where source_id = $1 and owner = $2", source_id, owner).execute(&mut *self.pool.acquire().await?).await?.rows_affected() { 1 => { Ok("Source disabled.") }, 0 => { Ok("Source not found.") }, _ => { Err(anyhow!("Database error.")) }, } - } */ + } - /* pub async fn update(&self, update: Option, channel: &str, channel_id: i64, url: &str, iv_hash: Option<&str>, url_re: Option<&str>, owner: S) -> Result<&str> - where S: Into { - let owner = owner.into(); + pub async fn update (&self, update: Option, channel: &str, channel_id: i64, url: &str, iv_hash: Option<&str>, url_re: Option<&str>, owner: i64) -> Result<&str> { let mut conn = self.pool.acquire().await?; match match update { Some(id) => { sqlx::query!("update rsstg_source set channel_id = $2, url = $3, iv_hash = $4, owner = $5, channel = $6, url_re = $7 where source_id = $1", @@ -297,11 +317,11 @@ Ok("No database error extracted.") }, } }, Err(err) => { - bail!("Sorry, unknown error:\n{:#?}\n", err); + bail!("Sorry, unknown error:\n{err:#?}\n"); }, } } async fn autofetch(&self) -> Result { @@ -312,17 +332,17 @@ for row in queue.iter() { if let Some(next_fetch) = row.next_fetch { if next_fetch < now { if let (Some(owner), Some(source_id)) = (row.owner, row.source_id) { let clone = Core { - owner_chat: telegram_bot::UserId::new(owner), + owner_chat: owner, ..self.clone() }; task::spawn(async move { if let Err(err) = clone.check(&source_id, owner, true).await { - if let Err(err) = clone.send(&format!("🛑 {:?}", err), None, None).await { - dbg!("Check error: {}", err); + if let Err(err) = clone.send(&format!("🛑 {err:?}"), None, None).await { + eprintln!("Check error: {err:?}"); // clone.disable(&source_id, owner).await.unwrap(); }; }; }); } @@ -333,14 +353,11 @@ }; queue.clear(); Ok(delay.to_std()?) } - pub async fn list(&self, owner: S) -> Result - where S: Into { - let owner = owner.into(); - + pub async fn list (&self, owner: i64) -> Result { let mut reply: Vec> = vec![]; reply.push("Channels:".into()); let rows = sqlx::query!("select source_id, channel, enabled, url, iv_hash, url_re from rsstg_source where owner = $1 order by source_id", owner).fetch_all(&mut *self.pool.acquire().await?).await?; for row in rows.iter() { @@ -348,14 +365,14 @@ match row.enabled { true => "🔄 enabled", false => "â›” disabled", }, row.url).into()); if let Some(hash) = &row.iv_hash { - reply.push(format!("IV: `{}`", hash).into()); + reply.push(format!("IV: `{hash}`").into()); } if let Some(re) = &row.url_re { - reply.push(format!("RE: `{}`", re).into()); + reply.push(format!("RE: `{re}`").into()); } }; Ok(reply.join("\n")) - } */ + } } Index: src/main.rs ================================================================== --- src/main.rs +++ src/main.rs @@ -5,58 +5,18 @@ mod command; mod core; use anyhow::Result; -use async_std::task; -use async_std::stream::StreamExt; #[async_std::main] async fn main() -> Result<()> { let settings = config::Config::builder() .add_source(config::File::with_name("rsstg")) .build()?; - let core = core::Core::new(settings)?; - - let mut stream = core.stream(); - stream.allowed_updates(&[telegram_bot::AllowedUpdate::Message]); - - task::block_on(async { - let mut reply_to: Option; - loop { - reply_to = None; - match stream.next().await { - Some(update) => { - if let Err(err) = handle(update?, &core, &reply_to).await { - core.send(&format!("🛑 {err:?}"), reply_to, None).await?; - }; - }, - None => { - core.send("🛑 None error.", None, None).await?; - } - }; - } - }) -} - -async fn handle(update: telegram_bot::Update, core: &core::Core, mut _reply_to: &Option) -> Result<()> { - if let telegram_bot::UpdateKind::Message(message) = update.kind { - if let Some(from) = message.from { - if let telegram_bot::MessageKind::Text{ ref data, .. } = message.kind { - let sender = from.id; - let words: Vec<&str> = data.split_whitespace().collect(); - if let Err(err) = match words[0] { - "/check" | "/clean" | "/enable" | "/delete" | "/disable" => command::command(core, sender, words).await, - "/start" => command::start(core, sender).await, - "/list" => command::list(core, sender).await, - "/add" | "/update" => command::update(core, sender, words).await, - _ => Ok(()), - } { - core.send(format!("🛑 {:?}", err), Some(sender), None).await?; - }; - }; - }; - }; + let core = core::Core::new(settings).await?; + + core.stream().await?; Ok(()) }