Index: Cargo.lock ================================================================== --- Cargo.lock +++ Cargo.lock @@ -11,13 +11,13 @@ "gimli", ] [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -101,13 +101,13 @@ "pin-project-lite", ] [[package]] name = "async-compression" -version = "0.4.23" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b37fc50485c4f3f736a4fb14199f6d5f5ba008d7f28fe710306c92780f004c07" +checksum = "d615619615a650c571269c00dca41db04b9210037fa76ed8239f70404ab56985" dependencies = [ "brotli", "flate2", "futures-core", "memchr", @@ -228,28 +228,39 @@ "pin-project-lite", "pin-utils", "slab", "wasm-bindgen-futures", ] + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] [[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.101", -] - [[package]] name = "atoi" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" @@ -345,35 +356,10 @@ "futures-io", "futures-lite 2.6.0", "piper", ] -[[package]] -name = "bon" -version = "3.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced38439e7a86a4761f7f7d5ded5ff009135939ecb464a24452eaa4c1696af7d" -dependencies = [ - "bon-macros", - "rustversion", -] - -[[package]] -name = "bon-macros" -version = "3.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce61d2d3844c6b8d31b2353d9f66cf5e632b3e9549583fe3cac2f4f6136725e" -dependencies = [ - "darling", - "ident_case", - "prettyplease", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.101", -] - [[package]] name = "brotli" version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d" @@ -393,13 +379,13 @@ "alloc-stdlib", ] [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -411,22 +397,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.25" +version = "1.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951" +checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" dependencies = [ "shlex", ] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "cfg_aliases" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -567,11 +553,11 @@ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "darling_macro" version = "0.20.11" @@ -578,11 +564,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "der" version = "0.7.10" @@ -610,21 +596,41 @@ checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "derive_builder_macro" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.101", + "syn 2.0.103", +] + +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", ] [[package]] name = "digest" version = "0.10.7" @@ -652,11 +658,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "dotenvy" version = "0.15.7" @@ -750,13 +756,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "flate2" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", "miniz_oxide", ] @@ -805,28 +811,10 @@ checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] -[[package]] -name = "frankenstein" -version = "0.41.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8d49b6a362726062969e5184f0b6b7fdeae7aa48ba7fa9508de3e3078a6c3f7" -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" checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" @@ -918,11 +906,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "futures-sink" version = "0.3.31" @@ -970,11 +958,11 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] [[package]] name = "getrandom" @@ -1027,13 +1015,13 @@ "tracing", ] [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" dependencies = [ "allocator-api2", "equivalent", "foldhash", ] @@ -1059,13 +1047,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hermit-abi" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1158,13 +1146,13 @@ "want", ] [[package]] name = "hyper-rustls" -version = "0.27.6" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a01595e11bdcec50946522c32dde3fc6914743000a68b93000965f2f02406d" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ "http", "hyper", "hyper-util", "rustls", @@ -1434,13 +1422,13 @@ "spin", ] [[package]] name = "libc" -version = "0.2.172" +version = "0.2.173" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb" [[package]] name = "libm" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1497,26 +1485,10 @@ name = "lru-slab" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" -[[package]] -name = "macro_rules_attribute" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65049d7923698040cd0b1ddcced9b0eb14dd22c5f86ae59c3740eab64a676520" -dependencies = [ - "macro_rules_attribute-proc_macro", - "paste", -] - -[[package]] -name = "macro_rules_attribute-proc_macro" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670fdfda89751bc4a84ac13eaa63e205cf0fd22b4c9a5fbfa085b63c1f1d3a30" - [[package]] name = "md-5" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" @@ -1525,13 +1497,13 @@ "digest", ] [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "mime" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1547,13 +1519,13 @@ "unicase", ] [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] @@ -1561,11 +1533,11 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.59.0", ] [[package]] name = "native-tls" @@ -1673,11 +1645,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "openssl-probe" version = "0.1.6" @@ -1723,16 +1695,10 @@ "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" @@ -1824,11 +1790,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.5.1", + "hermit-abi 0.5.2", "pin-project-lite", "rustix 1.0.7", "tracing", "windows-sys 0.59.0", ] @@ -1849,20 +1815,10 @@ checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy", ] -[[package]] -name = "prettyplease" -version = "0.2.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dee91521343f4c5c6a63edd65e54f31f5c92fe8978c40a4282f8372194c6a7d" -dependencies = [ - "proc-macro2", - "syn 2.0.101", -] - [[package]] name = "proc-macro2" version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" @@ -1892,11 +1848,11 @@ "quinn-proto", "quinn-udp", "rustc-hash", "rustls", "socket2 0.5.10", - "thiserror 2.0.12", + "thiserror", "tokio", "tracing", "web-time", ] @@ -1913,11 +1869,11 @@ "ring", "rustc-hash", "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.12", + "thiserror", "tinyvec", "tracing", "web-time", ] @@ -2009,13 +1965,13 @@ "getrandom 0.3.3", ] [[package]] name = "redox_syscall" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" dependencies = [ "bitflags 2.9.1", ] [[package]] @@ -2047,13 +2003,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.19" +version = "0.12.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f8e5513d63f2e5b386eb5106dc67eaf3f84e95258e210489136b8b92ad6119" +checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813" dependencies = [ "async-compression", "base64", "bytes", "encoding_rs", @@ -2065,17 +2021,15 @@ "http-body-util", "hyper", "hyper-rustls", "hyper-tls", "hyper-util", - "ipnet", "js-sys", "log", "mime", "mime_guess", "native-tls", - "once_cell", "percent-encoding", "pin-project-lite", "quinn", "rustls", "rustls-pki-types", @@ -2084,11 +2038,10 @@ "serde_urlencoded", "sync_wrapper", "tokio", "tokio-native-tls", "tokio-rustls", - "tokio-socks", "tokio-util", "tower", "tower-http", "tower-service", "url", @@ -2145,34 +2098,34 @@ "quick-xml", ] [[package]] name = "rsstg" -version = "0.3.4" +version = "0.4.0" dependencies = [ "anyhow", "async-std", "atom_syndication", "chrono", "config", - "frankenstein", "futures", "futures-util", "lazy_static", "regex", "reqwest", "rss", "sedregex", "sqlx", - "thiserror 2.0.12", + "tgbot", + "thiserror", ] [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" [[package]] name = "rustc-hash" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2314,11 +2267,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "serde_json" version = "1.0.140" @@ -2331,13 +2284,13 @@ "serde", ] [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] [[package]] @@ -2352,29 +2305,29 @@ "serde", ] [[package]] name = "serde_with" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +checksum = "bf65a400f8f66fb7b0552869ad70157166676db75ed8181f8104ea91cf9d0b42" dependencies = [ "serde", "serde_derive", "serde_with_macros", ] [[package]] name = "serde_with_macros" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +checksum = "81679d9ed988d5e9a5e6531dc3f2c28efbd639cbd1dfb628df08edea6004da77" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "sha1" version = "0.10.6" @@ -2394,10 +2347,20 @@ dependencies = [ "cfg-if", "cpufeatures", "digest", ] + +[[package]] +name = "shellwords" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e515aa4699a88148ed5ef96413ceef0048ce95b43fbc955a33bde0a70fcae6" +dependencies = [ + "lazy_static", + "regex", +] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2422,13 +2385,13 @@ "autocfg", ] [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" dependencies = [ "serde", ] [[package]] @@ -2512,11 +2475,11 @@ "rustls", "serde", "serde_json", "sha2", "smallvec", - "thiserror 2.0.12", + "thiserror", "tracing", "url", "webpki-roots 0.26.11", ] @@ -2528,11 +2491,11 @@ dependencies = [ "proc-macro2", "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "sqlx-macros-core" version = "0.8.6" @@ -2552,11 +2515,11 @@ "sha2", "sqlx-core", "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.101", + "syn 2.0.103", "url", ] [[package]] name = "sqlx-mysql" @@ -2594,11 +2557,11 @@ "sha1", "sha2", "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.12", + "thiserror", "tracing", "whoami", ] [[package]] @@ -2632,11 +2595,11 @@ "serde_json", "sha2", "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.12", + "thiserror", "tracing", "whoami", ] [[package]] @@ -2657,11 +2620,11 @@ "log", "percent-encoding", "serde", "serde_urlencoded", "sqlx-core", - "thiserror 2.0.12", + "thiserror", "tracing", "url", ] [[package]] @@ -2704,13 +2667,13 @@ "unicode-ident", ] [[package]] name = "syn" -version = "2.0.101" +version = "2.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] @@ -2730,11 +2693,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "system-configuration" version = "0.6.1" @@ -2768,36 +2731,38 @@ "rustix 1.0.7", "windows-sys 0.59.0", ] [[package]] -name = "thiserror" -version = "1.0.69" +name = "tgbot" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +checksum = "05ba13ab4b7a3241c72cbbd997f95dce32bbb3e8c49b09813a151ba8c68c1b08" dependencies = [ - "thiserror-impl 1.0.69", + "async-stream", + "bytes", + "derive_more", + "futures-util", + "log", + "mime", + "mime_guess", + "reqwest", + "serde", + "serde_json", + "serde_with", + "shellwords", + "tokio", + "tokio-util", ] [[package]] name = "thiserror" version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.12", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", + "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "2.0.12" @@ -2804,11 +2769,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "tinystr" version = "0.8.1" @@ -2867,22 +2832,10 @@ dependencies = [ "rustls", "tokio", ] -[[package]] -name = "tokio-socks" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" -dependencies = [ - "either", - "futures-util", - "thiserror 1.0.69", - "tokio", -] - [[package]] name = "tokio-util" version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" @@ -2894,34 +2847,34 @@ "tokio", ] [[package]] name = "toml" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned", "toml_datetime", "toml_edit", ] [[package]] name = "toml_datetime" -version = "0.6.9" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.26" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", @@ -2985,24 +2938,24 @@ "tracing-core", ] [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", ] [[package]] @@ -3106,13 +3059,13 @@ "try-lock", ] [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3147,11 +3100,11 @@ dependencies = [ "bumpalo", "log", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" @@ -3182,11 +3135,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] @@ -3300,11 +3253,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "windows-interface" version = "0.59.1" @@ -3311,18 +3264,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-registry" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3499,13 +3452,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" dependencies = [ "memchr", ] [[package]] @@ -3541,11 +3494,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "synstructure", ] [[package]] name = "zerocopy" @@ -3562,11 +3515,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "zerofrom" version = "0.1.6" @@ -3582,11 +3535,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "synstructure", ] [[package]] name = "zeroize" @@ -3622,7 +3575,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] Index: Cargo.toml ================================================================== --- Cargo.toml +++ Cargo.toml @@ -1,18 +1,18 @@ [package] name = "rsstg" -version = "0.3.4" +version = "0.4.0" authors = ["arcade"] edition = "2021" [dependencies] 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.41.0", features = [ "client-reqwest" ] } +tgbot = "0.36.1" 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" ]} Index: src/command.rs ================================================================== --- src/command.rs +++ src/command.rs @@ -1,76 +1,77 @@ use crate::core::Core; -use std::borrow::Cow; - use anyhow::{ + anyhow, bail, Context, - Result -}; -use frankenstein::{ - methods::{ - GetChatAdministratorsParams, - GetChatParams, - }, - types::{ - ChatId, - ChatMember, - }, - AsyncTelegramApi, - ParseMode, + Result, }; use lazy_static::lazy_static; use regex::Regex; use sedregex::ReplaceCommand; +use tgbot::types::{ + ChatMember, + ChatUsername, + GetChat, + GetChatAdministrators, + Message, + ParseMode::MarkdownV2, +}; 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(); } -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: &mut Core, sender: i64) -> Result<()> { - let msg = core.list(sender).await?; - core.send(msg, Some(sender), Some(ParseMode::MarkdownV2)).await?; - Ok(()) -} - -pub async fn command(core: &mut Core, sender: i64, command: Vec<&str>) -> Result<()> { - let mut conn = core.db.begin().await?; - 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 - .context("Channel check failed.")?.into(), - "/clean" => conn.clean(*number, sender).await?, - "/enable" => conn.enable(*number, sender).await?.into(), - "/delete" => conn.delete(*number, sender).await?, - "/disable" => conn.disable(*number, sender).await?.into(), - _ => bail!("Command {} not handled.", &command[0]), - }, - }; - core.send(msg, Some(sender), None).await?; - } else { - core.send("This command needs a number.", Some(sender), None).await?; - } - Ok(()) -} - -pub async fn update(core: &mut Core, sender: i64, command: Vec<&str>) -> Result<()> { +pub async fn start(core: &Core, msg: &Message) -> Result<()> { + core.send("We are open\\. Probably\\. Visit [channel](https://t.me/rsstg_bot_help/3) for details\\.", + Some(msg.chat.get_id()), Some(MarkdownV2)).await?; + Ok(()) +} + +pub async fn list(core: &Core, msg: &Message) -> Result<()> { + let sender = msg.sender.get_user_id() + .ok_or(anyhow!("Ignoring unreal users."))?; + let reply = core.list(sender).await?; + core.send(reply, Some(msg.chat.get_id()), Some(MarkdownV2)).await?; + Ok(()) +} + +pub async fn command (core: &Core, msg: &Message, command: &[String]) -> Result<()> { + let mut conn = core.db.begin().await?; + let sender = msg.sender.get_user_id() + .ok_or(anyhow!("Ignoring unreal users."))?; + let reply = if command.len() >= 2 { + match command[1].parse::() { + Err(err) => format!("I need a number.\n{}", &err).into(), + Ok(number) => match &command[0][..] { + "/check" => core.check(number, false).await + .context("Channel check failed.")?.into(), + "/clean" => conn.clean(number, sender).await?, + "/enable" => conn.enable(number, sender).await?.into(), + "/delete" => conn.delete(number, sender).await?, + "/disable" => conn.disable(number, sender).await?.into(), + _ => bail!("Command {} not handled.", &command[0]), + }, + } + } else { + "This command needs a number.".into() + }; + core.send(reply, Some(msg.chat.get_id()), None).await?; + Ok(()) +} + +pub async fn update (core: &Core, msg: &Message, command: &[String]) -> Result<()> { + let sender = msg.sender.get_user_id() + .ok_or(anyhow!("Ignoring unreal users."))?; 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 { + match first_word.as_ref() { "/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}."))?); }, @@ -88,11 +89,11 @@ if ! RE_LINK.is_match(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 { + match hash.as_ref() { "-" => None, thing => { if ! RE_IV_HASH.is_match(thing) { bail!("IV hash should be 14 hex digits.\nNot {thing:?}"); }; @@ -102,42 +103,42 @@ }, None => None, }; let url_re = match url_re { Some(re) => { - match *re { + match re.as_ref() { "-" => None, thing => { let _url_rex = ReplaceCommand::new(thing).context("Regexp parsing error:")?; Some(thing) } } }, None => None, }; - 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 chat_id = ChatUsername::from(channel.clone()); + let channel_id = core.tg.execute(GetChat::new(chat_id.clone())).await?.id; + let chan_adm = core.tg.execute(GetChatAdministrators::new(chat_id)).await + .context("Sorry, I have no access to that chat.")?; let (mut me, mut user) = (false, false); for admin in chan_adm { let member_id = match admin { ChatMember::Creator(member) => member.user.id, ChatMember::Administrator(member) => member.user.id, ChatMember::Left(_) | ChatMember::Kicked(_) - | ChatMember::Member(_) + | ChatMember::Member{..} | ChatMember::Restricted(_) => continue, - } as i64; - if member_id == core.me.id as i64 { + }; + if member_id == core.me.id { me = true; - }; + } 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."); }; let mut conn = core.db.begin().await?; - core.send(conn.update(source_id, channel, channel_id, url, iv_hash, url_re, sender).await?, Some(sender), None).await?; + core.send(conn.update(source_id, channel, channel_id, url, iv_hash, url_re, sender).await?, Some(msg.chat.get_id()), None).await?; Ok(()) } Index: src/core.rs ================================================================== --- src/core.rs +++ src/core.rs @@ -21,25 +21,25 @@ bail, Result, }; use async_std::task; use chrono::DateTime; -use frankenstein::{ - AsyncTelegramApi, - Error as FrankError, - ParseMode, - client_reqwest::Bot, - methods::{ - GetUpdatesParams, - SendMessageParams - }, +use tgbot::{ + api::Client, + handler::UpdateHandler, types::{ - AllowedUpdate, - MessageEntityType, - User, + Bot, + ChatPeerId, + Command, + GetBot, + Message, + ParseMode, + SendMessage, + Update, + UpdateType, + UserPeerId, }, - updates::UpdateContent, }; use thiserror::Error; #[derive(Error, Debug)] pub enum RssError { @@ -49,43 +49,42 @@ Int(#[from] TryFromIntError), } #[derive(Clone)] pub struct Core { - owner_chat: i64, - max_delay: u16, - pub tg: Bot, - pub me: User, + owner_chat: ChatPeerId, + // max_delay: u16, + pub tg: Client, + pub me: Bot, pub db: Db, sources: Arc>>>, http_client: reqwest::Client, } impl Core { pub async fn new(settings: config::Config) -> Result { - let owner_chat = settings.get_int("owner")?; + let owner_chat = ChatPeerId::from(settings.get_int("owner")?); let api_key = settings.get_string("api_key")?; - let tg = Bot::new(&api_key); + let tg = Client::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 me = tg.execute(GetBot).await?; let core = Core { tg, me, owner_chat, db: Db::new(&settings.get_string("pg")?)?, sources: Arc::new(Mutex::new(HashSet::new())), http_client, - max_delay: 60, + // max_delay: 60, }; - let mut 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 { @@ -99,11 +98,12 @@ } }); Ok(core) } - pub async fn stream(&mut self) -> Result<()> { + /* + pub async fn stream(&self) -> Result<()> { let mut offset: i64 = 0; let mut params = GetUpdatesParams { offset: None, limit: Some(100), timeout: Some(300), @@ -151,71 +151,47 @@ }; }; } } } + */ - pub async fn send (&self, msg: S, target: Option, mode: Option) -> Result<()> + 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(); - loop { - match self.tg.send_message(&send).await { - Ok(_) => break, - Err(err) => match err { - FrankError::Api(ref resp) => { - if resp.error_code == 429 { - let mut my_delay = self.max_delay; - if let Some(params) = resp.parameters { - if let Some(delay) = params.retry_after { - if delay < my_delay { - my_delay = delay; - } - } - } - task::sleep(std::time::Duration::from_secs(my_delay.into())).await; - } else { - return Err(err.into()); - } - }, - _ => return Err(err.into()), - }, - } - } - Ok(()) - } - - pub async fn check (&mut self, id: &i32, owner: i64, real: bool) -> Result { + Ok(self.tg.execute( + SendMessage::new(target, msg) + .with_parse_mode(mode) + ).await?) + } + + pub async fn check (&self, id: i32, real: bool) -> Result { let mut posted: i32 = 0; let mut conn = self.db.begin().await?; let id = { let mut set = self.sources.lock().unwrap(); - match set.get(id) { + match set.get(&id) { Some(id) => id.clone(), None => { - let id = Arc::new(*id); + let id = Arc::new(id); set.insert(id.clone()); id.clone() }, } }; let count = Arc::strong_count(&id); if count == 2 { - let source = conn.get_source(*id, owner).await?; + let source = conn.get_source(*id, self.owner_chat).await?; conn.set_scrape(*id).await?; - let destination = match real { + let destination = ChatPeerId::from(match real { 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?; let status = response.status(); @@ -274,24 +250,27 @@ posts.clear(); }; Ok(format!("Posted: {posted}")) } - async fn autofetch(&mut self) -> Result { + async fn autofetch(&self) -> Result { let mut delay = chrono::Duration::minutes(1); let now = chrono::Local::now(); - let mut conn = self.db.begin().await?; - for row in conn.get_queue().await? { + let queue = { + let mut conn = self.db.begin().await?; + conn.get_queue().await? + }; + for row in queue { 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 mut clone = Core { - owner_chat: owner, + let clone = Core { + owner_chat: ChatPeerId::from(owner), ..self.clone() }; task::spawn(async move { - if let Err(err) = clone.check(&source_id, owner, true).await { + if let Err(err) = clone.check(source_id, true).await { if let Err(err) = clone.send(&format!("šŸ›‘ {err:?}"), None, None).await { eprintln!("Check error: {err:?}"); // clone.disable(&source_id, owner).await.unwrap(); }; }; @@ -303,11 +282,11 @@ } }; Ok(delay.to_std()?) } - pub async fn list (&mut self, owner: i64) -> Result { + pub async fn list (&self, owner: UserPeerId) -> Result { let mut reply: Vec> = vec![]; reply.push("Channels:".into()); let mut conn = self.db.begin().await?; for row in conn.get_list(owner).await? { reply.push(format!("\n\\#ļøāƒ£ {} \\*ļøāƒ£ `{}` {}\nšŸ”— `{}`", row.source_id, row.channel, @@ -323,5 +302,31 @@ } }; Ok(reply.join("\n")) } } + +impl UpdateHandler for Core { + async fn handle (&self, update: Update) { + if let UpdateType::Message(msg) = update.update_type { + if let Ok(cmd) = Command::try_from(msg) { + let msg = cmd.get_message(); + let words = cmd.get_args(); + let res = match cmd.get_name() { + "/check" | "/clean" | "/enable" | "/delete" | "/disable" => command::command(self, msg, words).await, + "/start" => command::start(self, msg).await, + "/list" => command::list(self, msg).await, + "/add" | "/update" => command::update(self, msg, words).await, + any => Err(anyhow!("Unknown command: {any}")), + }; + if let Err(err) = res { + if let Err(err2) = self.send(format!("\\#error\n```\n{err:?}\n```"), + Some(msg.chat.get_id()), + Some(ParseMode::MarkdownV2) + ).await{ + dbg!(err2); + }; + } + }; + }; + } +} Index: src/main.rs ================================================================== --- src/main.rs +++ src/main.rs @@ -6,18 +6,19 @@ mod command; mod core; mod sql; use anyhow::Result; +use tgbot::handler::LongPoll; #[async_std::main] async fn main() -> Result<()> { let settings = config::Config::builder() .add_source(config::File::with_name("rsstg")) .build()?; - let mut core = core::Core::new(settings).await?; + let core = core::Core::new(settings).await?; - core.stream().await?; + LongPoll::new(core.tg.clone(), core).run().await; Ok(()) } Index: src/sql.rs ================================================================== --- src/sql.rs +++ src/sql.rs @@ -1,6 +1,12 @@ -use std::borrow::Cow; +use std::{ + borrow::Cow, + sync::{ + Arc, + Mutex, + }, +}; use anyhow::{ Result, bail, }; @@ -8,11 +14,10 @@ DateTime, FixedOffset, Local, }; use sqlx::{ - Pool, Postgres, Row, postgres::PgPoolOptions, pool::PoolConnection, }; @@ -43,96 +48,102 @@ pub owner: Option, } #[derive(Clone)] pub struct Db { - pool: sqlx::Pool, + pool: Arc>>>, } pub struct Conn{ conn: PoolConnection, } impl Db { pub fn new (pguri: &str) -> Result { Ok(Db{ - pool: PgPoolOptions::new() + pool: Arc::new(Mutex::new(Arc::new(PgPoolOptions::new() .max_connections(5) .acquire_timeout(std::time::Duration::new(300, 0)) .idle_timeout(std::time::Duration::new(60, 0)) - .connect_lazy(pguri)?, + .connect_lazy(pguri)?))), }) } - pub async fn begin(&mut self) -> Result { - Conn::new(&mut self.pool).await + pub async fn begin(&self) -> Result { + let pool = self.pool.lock().unwrap().clone(); + let conn = Conn::new(pool.acquire().await?).await?; + Ok(conn) } } impl Conn { - pub async fn new (pool: &mut Pool) -> Result { - let conn = pool.acquire().await?; + pub async fn new (conn: PoolConnection) -> Result { Ok(Conn{ conn, }) } - pub async fn add_post (&mut self, id: i32, date: &DateTime, post_url: &str) -> Result<()> { + pub async fn add_post (&mut self, source_id: i32, date: &DateTime, post_url: &str) -> Result<()> { sqlx::query("insert into rsstg_post (source_id, posted, url) values ($1, $2, $3);") - .bind(id) + .bind(source_id) .bind(date) .bind(post_url) .execute(&mut *self.conn).await?; Ok(()) } - pub async fn clean (&mut self, source_id: i32, owner: i64) -> Result> { + pub async fn clean (&mut self, source_id: i32, owner: I) -> Result> + where I: Into { 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;") .bind(source_id) - .bind(owner) + .bind(owner.into()) .execute(&mut *self.conn).await?.rows_affected() { 0 => { Ok("No data found found.".into()) }, x => { Ok(format!("{x} posts purged.").into()) }, } } - pub async fn delete (&mut self, source_id: i32, owner: i64) -> Result> { + pub async fn delete (&mut self, source_id: i32, owner: I) -> Result> + where I: Into { match sqlx::query("delete from rsstg_source where source_id = $1 and owner = $2;") .bind(source_id) - .bind(owner) + .bind(owner.into()) .execute(&mut *self.conn).await?.rows_affected() { 0 => { Ok("No data found found.".into()) }, x => { Ok(format!("{} sources removed.", x).into()) }, } } - pub async fn disable (&mut self, source_id: i32, owner: i64) -> Result<&str> { + pub async fn disable (&mut self, source_id: i32, owner: I) -> Result<&str> + where I: Into { match sqlx::query("update rsstg_source set enabled = false where source_id = $1 and owner = $2") .bind(source_id) - .bind(owner) + .bind(owner.into()) .execute(&mut *self.conn).await?.rows_affected() { 1 => { Ok("Source disabled.") }, 0 => { Ok("Source not found.") }, _ => { bail!("Database error.") }, } } - pub async fn enable (&mut self, source_id: i32, owner: i64) -> Result<&str> { + pub async fn enable (&mut self, source_id: i32, owner: I) -> Result<&str> + where I: Into { match sqlx::query("update rsstg_source set enabled = true where source_id = $1 and owner = $2") .bind(source_id) - .bind(owner) + .bind(owner.into()) .execute(&mut *self.conn).await?.rows_affected() { 1 => { Ok("Source enabled.") }, 0 => { Ok("Source not found.") }, _ => { bail!("Database error.") }, } } - pub async fn exists (&mut self, post_url: &str, id: i32) -> Result> { + pub async fn exists (&mut self, post_url: &str, id: I) -> Result> + where I: Into { let row = sqlx::query("select exists(select true from rsstg_post where url = $1 and source_id = $2) as exists;") .bind(post_url) - .bind(id) + .bind(id.into()) .fetch_one(&mut *self.conn).await?; let exists: Option = row.try_get("exists")?; Ok(exists) } @@ -140,33 +151,37 @@ let block: Vec = sqlx::query_as("select source_id, next_fetch, owner from rsstg_order natural left join rsstg_source where next_fetch < now() + interval '1 minute';") .fetch_all(&mut *self.conn).await?; Ok(block) } - pub async fn get_list (&mut self, owner: i64) -> Result> { + pub async fn get_list (&mut self, owner: I) -> Result> + where I: Into { let source: Vec = sqlx::query_as("select source_id, channel, enabled, url, iv_hash, url_re from rsstg_source where owner = $1 order by source_id") - .bind(owner) + .bind(owner.into()) .fetch_all(&mut *self.conn).await?; Ok(source) } - pub async fn get_source (&mut self, id: i32, owner: i64) -> Result { + pub async fn get_source (&mut self, id: i32, owner: I) -> Result + where I: Into { let source: Source = sqlx::query_as("select channel_id, url, iv_hash, owner, url_re from rsstg_source where source_id = $1 and owner = $2") .bind(id) - .bind(owner) + .bind(owner.into()) .fetch_one(&mut *self.conn).await?; Ok(source) } - pub async fn set_scrape (&mut self, id: i32) -> Result<()> { + pub async fn set_scrape (&mut self, id: I) -> Result<()> + where I: Into { sqlx::query("update rsstg_source set last_scrape = now() where source_id = $1;") - .bind(id) + .bind(id.into()) .execute(&mut *self.conn).await?; Ok(()) } - pub async fn update (&mut self, update: Option, channel: &str, channel_id: i64, url: &str, iv_hash: Option<&str>, url_re: Option<&str>, owner: i64) -> Result<&str> { + pub async fn update (&mut self, update: Option, channel: &str, channel_id: i64, url: &str, iv_hash: Option<&str>, url_re: Option<&str>, owner: I) -> Result<&str> + where I: Into { 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") .bind(id) }, @@ -175,11 +190,11 @@ }, } .bind(channel_id) .bind(url) .bind(iv_hash) - .bind(owner) + .bind(owner.into()) .bind(channel) .bind(url_re) .execute(&mut *self.conn).await { Ok(_) => Ok(match update {