diff --git a/.forgejo/workflows/build+release.yml b/.forgejo/workflows/build+release.yml index a21f697..6b63dad 100644 --- a/.forgejo/workflows/build+release.yml +++ b/.forgejo/workflows/build+release.yml @@ -137,7 +137,7 @@ jobs: run: rm release_blobs/build.env - name: Release New Version - uses: actions/forgejo-release@v2 + uses: actions/forgejo-release@v1 with: direction: upload url: https://forgejo.neshweb.net diff --git a/Cargo.lock b/Cargo.lock index c26d591..1a765e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 4 +version = 3 [[package]] name = "addr2line" @@ -41,18 +41,9 @@ dependencies = [ "libc", ] -[[package]] -name = "anyhow" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" -dependencies = [ - "backtrace", -] - [[package]] name = "aob-lemmy-bot" -version = "3.2.2" +version = "2.2.0" dependencies = [ "async-trait", "chrono", @@ -60,7 +51,6 @@ dependencies = [ "lemmy_api_common", "lemmy_db_schema", "log", - "notify", "once_cell", "reqwest", "serde", @@ -69,26 +59,15 @@ dependencies = [ "strum_macros", "systemd-journal-logger", "tokio", - "toml", + "toml 0.8.8", "url", ] -[[package]] -name = "async-lock" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" -dependencies = [ - "event-listener", - "event-listener-strategy", - "pin-project-lite", -] - [[package]] name = "async-trait" -version = "0.1.81" +version = "0.1.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", @@ -118,9 +97,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.22.1" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "bitflags" @@ -140,12 +119,6 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "bytes" version = "1.5.0" @@ -161,17 +134,6 @@ dependencies = [ "libc", ] -[[package]] -name = "cfb" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" -dependencies = [ - "byteorder", - "fnv", - "uuid", -] - [[package]] name = "cfg-if" version = "1.0.0" @@ -180,9 +142,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", @@ -190,50 +152,21 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.0", -] - -[[package]] -name = "clearurls" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e291c00af89ac0a5b400d9ba46a682e38015ae3cd8926dbbe85b3b864d550be3" -dependencies = [ - "linkify", - "percent-encoding", - "regex", - "serde", - "serde_json", - "url", -] - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", + "windows-targets 0.48.5", ] [[package]] name = "confy" -version = "0.6.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45b1f4c00870f07dc34adcac82bb6a72cc5aabca8536ba1797e01df51d2ce9a0" +checksum = "e37668cb35145dcfaa1931a5f37fde375eeae8068b4c0d2f289da28a270b2d2c" dependencies = [ "directories", "serde", "thiserror", - "toml", + "toml 0.5.11", ] -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - [[package]] name = "core-foundation" version = "0.9.4" @@ -250,30 +183,6 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" -[[package]] -name = "crossbeam-channel" -version = "0.5.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" - [[package]] name = "darling" version = "0.20.3" @@ -319,47 +228,24 @@ dependencies = [ "serde", ] -[[package]] -name = "derive-new" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "directories" -version = "5.0.1" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" dependencies = [ "dirs-sys", ] [[package]] name = "dirs-sys" -version = "0.4.1" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", - "option-ext", "redox_users", - "windows-sys 0.48.0", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "winapi", ] [[package]] @@ -407,45 +293,12 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "event-listener" -version = "5.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" -dependencies = [ - "event-listener", - "pin-project-lite", -] - [[package]] name = "fastrand" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" -[[package]] -name = "filetime" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.4.1", - "windows-sys 0.52.0", -] - [[package]] name = "fnv" version = "1.0.7" @@ -476,15 +329,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fsevent-sys" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" -dependencies = [ - "libc", -] - [[package]] name = "futures-channel" version = "0.3.29" @@ -492,26 +336,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", - "futures-sink", ] [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", @@ -520,26 +363,25 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures-core", "futures-io", "futures-macro", - "futures-sink", "futures-task", "memchr", "pin-project-lite", @@ -549,9 +391,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "js-sys", @@ -568,9 +410,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "h2" -version = "0.4.4" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" dependencies = [ "bytes", "fnv", @@ -599,9 +441,9 @@ checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "heck" -version = "0.5.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -617,9 +459,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "http" -version = "1.1.0" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" dependencies = [ "bytes", "fnv", @@ -628,24 +470,12 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.0" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", "pin-project-lite", ] @@ -656,59 +486,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] -name = "hyper" -version = "1.3.1" +name = "httpdate" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[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", - "smallvec", + "socket2 0.4.10", "tokio", + "tower-service", + "tracing", "want", ] [[package]] name = "hyper-tls" -version = "0.6.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "http-body-util", "hyper", - "hyper-util", "native-tls", "tokio", "tokio-native-tls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "pin-project-lite", - "socket2", - "tokio", - "tower", - "tower-service", - "tracing", ] [[package]] @@ -734,124 +551,6 @@ dependencies = [ "cc", ] -[[package]] -name = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" - -[[package]] -name = "icu_properties" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locid_transform", - "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" - -[[package]] -name = "icu_provider" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -860,23 +559,12 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "1.0.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" -dependencies = [ - "icu_normalizer", - "icu_properties", + "unicode-bidi", + "unicode-normalization", ] [[package]] @@ -901,35 +589,6 @@ dependencies = [ "serde", ] -[[package]] -name = "infer" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc150e5ce2330295b8616ce0e3f53250e53af31759a9dbedad1621ba29151847" -dependencies = [ - "cfb", -] - -[[package]] -name = "inotify" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" -dependencies = [ - "bitflags 1.3.2", - "inotify-sys", - "libc", -] - -[[package]] -name = "inotify-sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" -dependencies = [ - "libc", -] - [[package]] name = "ipnet" version = "2.9.0" @@ -951,26 +610,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "kqueue" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" -dependencies = [ - "kqueue-sys", - "libc", -] - -[[package]] -name = "kqueue-sys" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" -dependencies = [ - "bitflags 1.3.2", - "libc", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -979,44 +618,36 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lemmy_api_common" -version = "0.19.10" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06a7554d0a71b37c1f666125918a72fe92103a5f254668a988aaa31ed80d4b7b" +checksum = "bed97d80c606745807e7a41d2e7a54912e6dc73b17f847453717cfa1e6693b31" dependencies = [ - "anyhow", "chrono", "enum-map", "getrandom", - "infer", "lemmy_db_schema", "lemmy_db_views", "lemmy_db_views_actor", "lemmy_db_views_moderator", - "lemmy_utils", - "mime_guess", - "moka", "regex", "serde", "serde_with", "url", - "urlencoding", ] [[package]] name = "lemmy_db_schema" -version = "0.19.10" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5454e0df45ec4831b7c6502d965cb9865ecbe2cb181a20c616ad8235444c06e8" +checksum = "5a17c613a84befef3daadc179e2069e02848b83dfd80d7955ca825bdca92869b" dependencies = [ - "anyhow", "async-trait", "chrono", - "derive-new", "futures-util", - "moka", "serde", "serde_with", "strum", + "strum_macros", "tracing", "typed-builder", "url", @@ -1025,11 +656,10 @@ dependencies = [ [[package]] name = "lemmy_db_views" -version = "0.19.10" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4d348095887630617ef02b823887d422ae2e93dfff7a13a127e7524eec51d99" +checksum = "e360aaee508cb057c6df0160b27aeb8b845a3725127b2d7e4e4c877397418539" dependencies = [ - "chrono", "lemmy_db_schema", "serde", "serde_with", @@ -1037,46 +667,34 @@ dependencies = [ [[package]] name = "lemmy_db_views_actor" -version = "0.19.10" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7a42bf16a72d6f313f59116538e1333e6ce5f2efbeb9b834ddb147b2cdf986" +checksum = "606962be1df35bf9214a22cea819cec181ecd4e71dfe693b92428291b69a7cd6" dependencies = [ "chrono", "lemmy_db_schema", "serde", "serde_with", "strum", + "strum_macros", ] [[package]] name = "lemmy_db_views_moderator" -version = "0.19.10" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b6a1a841c4ccac1947f7edfbb52b3113d32f218a2eede769ec5dce4a039cd43" +checksum = "0e64669a695c855143be2a9d4a662d481d235d7470621793db0607f60190be80" dependencies = [ "lemmy_db_schema", "serde", "serde_with", ] -[[package]] -name = "lemmy_utils" -version = "0.19.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee28dc456fac8d5c7ae67b4f596a1e9e2a0a9a6e1c4f8a024bdfb34cf25dfa5" -dependencies = [ - "cfg-if", - "clearurls", - "rosetta-build", - "serde", - "strum", -] - [[package]] name = "libc" -version = "0.2.155" +version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "libredox" @@ -1086,16 +704,7 @@ checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ "bitflags 2.4.1", "libc", - "redox_syscall 0.4.1", -] - -[[package]] -name = "linkify" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1dfa36d52c581e9ec783a7ce2a5e0143da6237be5811a0b3153fedfdbe9f780" -dependencies = [ - "memchr", + "redox_syscall", ] [[package]] @@ -1104,22 +713,6 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" -[[package]] -name = "litemap" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" - -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.20" @@ -1141,16 +734,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - [[package]] name = "miniz_oxide" version = "0.7.1" @@ -1167,35 +750,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", - "log", "wasi", "windows-sys 0.48.0", ] -[[package]] -name = "moka" -version = "0.12.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32cf62eb4dd975d2dde76432fb1075c49e3ee2331cf36f1f8fd4b66550d32b6f" -dependencies = [ - "async-lock", - "async-trait", - "crossbeam-channel", - "crossbeam-epoch", - "crossbeam-utils", - "event-listener", - "futures-util", - "once_cell", - "parking_lot", - "quanta", - "rustc_version", - "smallvec", - "tagptr", - "thiserror", - "triomphe", - "uuid", -] - [[package]] name = "native-tls" version = "0.2.11" @@ -1214,31 +772,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "notify" -version = "6.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" -dependencies = [ - "bitflags 2.4.1", - "crossbeam-channel", - "filetime", - "fsevent-sys", - "inotify", - "kqueue", - "libc", - "log", - "mio", - "walkdir", - "windows-sys 0.48.0", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - [[package]] name = "num-traits" version = "0.2.17" @@ -1317,67 +850,12 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "parking" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" - -[[package]] -name = "parking_lot" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.5.3", - "smallvec", - "windows-targets 0.52.0", -] - [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "pin-project" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "pin-project-lite" version = "0.2.13" @@ -1411,21 +889,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "quanta" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" -dependencies = [ - "crossbeam-utils", - "libc", - "once_cell", - "raw-cpuid", - "wasi", - "web-sys", - "winapi", -] - [[package]] name = "quote" version = "1.0.35" @@ -1435,15 +898,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "raw-cpuid" -version = "11.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e29830cbb1290e404f24c73af91c5d8d631ce7e128691e9477556b540cd01ecd" -dependencies = [ - "bitflags 2.4.1", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -1453,15 +907,6 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "redox_syscall" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" -dependencies = [ - "bitflags 2.4.1", -] - [[package]] name = "redox_users" version = "0.4.4" @@ -1475,9 +920,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.5" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", @@ -1487,9 +932,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", @@ -1504,23 +949,20 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.12.4" +version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ "base64", "bytes", "encoding_rs", - "futures-channel", "futures-core", "futures-util", "h2", "http", "http-body", - "http-body-util", "hyper", "hyper-tls", - "hyper-util", "ipnet", "js-sys", "log", @@ -1529,11 +971,9 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", "system-configuration", "tokio", "tokio-native-tls", @@ -1545,35 +985,12 @@ dependencies = [ "winreg", ] -[[package]] -name = "rosetta-build" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24191a1fed7ae7a5d89f816366f5c03ebce70a7a4d7158534afdf8dd719749fe" -dependencies = [ - "convert_case", - "lazy_static", - "proc-macro2", - "quote", - "regex", - "tinyjson", -] - [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - [[package]] name = "rustix" version = "0.38.28" @@ -1587,22 +1004,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rustls-pemfile" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" -dependencies = [ - "base64", - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" - [[package]] name = "rustversion" version = "1.0.14" @@ -1615,15 +1016,6 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - [[package]] name = "schannel" version = "0.1.22" @@ -1633,12 +1025,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[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" @@ -1662,26 +1048,20 @@ dependencies = [ "libc", ] -[[package]] -name = "semver" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" - [[package]] name = "serde" -version = "1.0.204" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", @@ -1690,12 +1070,11 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", - "memchr", "ryu", "serde", ] @@ -1723,9 +1102,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.9.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857" +checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" dependencies = [ "base64", "chrono", @@ -1733,7 +1112,6 @@ dependencies = [ "indexmap 1.9.3", "indexmap 2.1.0", "serde", - "serde_derive", "serde_json", "serde_with_macros", "time", @@ -1741,9 +1119,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.9.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" +checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" dependencies = [ "darling", "proc-macro2", @@ -1761,10 +1139,14 @@ dependencies = [ ] [[package]] -name = "smallvec" -version = "1.13.2" +name = "socket2" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] [[package]] name = "socket2" @@ -1776,12 +1158,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "strsim" version = "0.10.0" @@ -1790,18 +1166,15 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "strum" -version = "0.26.3" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros", -] +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" [[package]] name = "strum_macros" -version = "0.26.4" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck", "proc-macro2", @@ -1821,23 +1194,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "synstructure" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "system-configuration" version = "0.5.1" @@ -1869,12 +1225,6 @@ dependencies = [ "rustix", ] -[[package]] -name = "tagptr" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" - [[package]] name = "tempfile" version = "3.8.1" @@ -1883,7 +1233,7 @@ checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.4.1", + "redox_syscall", "rustix", "windows-sys 0.48.0", ] @@ -1910,13 +1260,12 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ "deranged", "itoa", - "num-conv", "powerfmt", "serde", "time-core", @@ -1931,35 +1280,33 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" dependencies = [ - "num-conv", "time-core", ] [[package]] -name = "tinyjson" -version = "2.5.1" +name = "tinyvec" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ab95735ea2c8fd51154d01e39cf13912a78071c2d89abc49a7ef102a7dd725a" - -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ - "displaydoc", - "zerovec", + "tinyvec_macros", ] [[package]] -name = "tokio" -version = "1.37.0" +name = "tinyvec_macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" dependencies = [ "backtrace", "bytes", @@ -1967,7 +1314,7 @@ dependencies = [ "mio", "num_cpus", "pin-project-lite", - "socket2", + "socket2 0.5.5", "tokio-macros", "windows-sys 0.48.0", ] @@ -2007,6 +1354,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + [[package]] name = "toml" version = "0.8.8" @@ -2041,28 +1397,6 @@ dependencies = [ "winnow", ] -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "pin-project", - "pin-project-lite", - "tokio", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-layer" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" - [[package]] name = "tower-service" version = "0.3.2" @@ -2075,7 +1409,6 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2101,12 +1434,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "triomphe" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" - [[package]] name = "try-lock" version = "0.2.5" @@ -2115,18 +1442,18 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typed-builder" -version = "0.19.1" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06fbd5b8de54c5f7c91f6fe4cebb949be2125d7758e630bb58b1d831dbce600" +checksum = "e47c0496149861b7c95198088cbf36645016b1a0734cf350c50e2a38e070f38a" dependencies = [ "typed-builder-macro", ] [[package]] name = "typed-builder-macro" -version = "0.19.1" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9534daa9fd3ed0bd911d462a37f172228077e7abf18c18a5f67199d959205f8" +checksum = "982ee4197351b5c9782847ef5ec1fdcaf50503fb19d68f9771adae314e72b492" dependencies = [ "proc-macro2", "quote", @@ -2134,10 +1461,10 @@ dependencies = [ ] [[package]] -name = "unicase" -version = "2.8.1" +name = "unicode-bidi" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-ident" @@ -2146,10 +1473,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] -name = "url" -version = "2.5.4" +name = "unicode-normalization" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", @@ -2157,29 +1493,11 @@ dependencies = [ "serde", ] -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - [[package]] name = "uuid" -version = "1.10.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" dependencies = [ "getrandom", "serde", @@ -2197,16 +1515,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - [[package]] name = "want" version = "0.3.1" @@ -2314,15 +1622,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" -dependencies = [ - "windows-sys 0.52.0", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -2481,89 +1780,10 @@ dependencies = [ [[package]] name = "winreg" -version = "0.52.0" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", "windows-sys 0.48.0", ] - -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - -[[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - -[[package]] -name = "yoke" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zerovec" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] diff --git a/Cargo.toml b/Cargo.toml index 382aa18..3bab39f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["Neshura"] name = "aob-lemmy-bot" -version = "3.2.2" +version = "2.2.0" edition = "2021" description = "Bot for automatically posting new chapters of 'Ascendance of a Bookworm' released by J-Novel Club" license = "GPL-3.0-or-later" @@ -16,20 +16,19 @@ systemd-units = { enable = false } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -chrono = "^0.4" -lemmy_api_common = "0.19.9" -lemmy_db_schema = "0.19.9" -once_cell = "^1.19" -reqwest = { version = "^0.12", features = ["blocking", "json"] } -serde = "^1.0" -serde_derive = "^1.0" -serde_json = "^1.0" -strum_macros = "^0.26" -tokio = { version = "^1.37", features = ["rt", "rt-multi-thread", "macros"] } -url = "^2.5" -confy = "^0.6" -toml = "^0.8" +chrono = "^0.4.26" +lemmy_api_common = "0.19.1" +lemmy_db_schema = "0.19.1" +once_cell = "^1.18.0" +reqwest = { version = "^0.11.18", features = ["blocking", "json"] } +serde = "^1.0.164" +serde_derive = "^1.0.164" +serde_json = "^1.0.97" +strum_macros = "^0.25.0" +tokio = { version = "^1.32.0", features = ["rt", "rt-multi-thread", "macros"] } +url = "^2.4.0" +confy = "^0.5.1" +toml = "^0.8.8" systemd-journal-logger = "^2.1.1" -log = "^0.4" -async-trait = "^0.1" -notify = "6.1.1" \ No newline at end of file +log = "^0.4.20" +async-trait = "^0.1.77" \ No newline at end of file diff --git a/src/bot.rs b/src/bot.rs index ba0e8b9..eef7e6a 100644 --- a/src/bot.rs +++ b/src/bot.rs @@ -1,136 +1,273 @@ -use crate::{config::{Config}, HTTP_CLIENT}; -use crate::lemmy::{Lemmy}; -use crate::post_history::{SeriesHistory}; +use crate::config::{Config, PostBody, SeriesConfig}; +use crate::fetchers::jnovel::JPostInfo; +use crate::lemmy::{Lemmy, PostInfo}; +use crate::post_history::SeriesHistory; +use crate::{fetchers::{jnovel}, lemmy, write_error, write_info, write_warn, SharedData}; use chrono::{DateTime, Duration, Utc}; -use std::sync::{Arc, RwLock}; -use notify::{Event, EventKind, event::{AccessKind, AccessMode}, RecursiveMode, Watcher}; +use lemmy_api_common::post::CreatePost; +use lemmy_db_schema::newtypes::{CommunityId, LanguageId}; +use lemmy_db_schema::PostFeatureType; +use std::collections::HashMap; +use std::sync::Arc; +use tokio::sync::RwLock; use tokio::time::sleep; -use systemd_journal_logger::connected_to_journal; +use crate::fetchers::Fetcher; -macro_rules! debug { - ($msg:tt) => { - match connected_to_journal() { - true => log::debug!("[DEBUG] {}", $msg), - false => println!("[DEBUG] {}", $msg), - } - }; -} +pub(crate) async fn run(data: Arc<RwLock<SharedData>>) { + let mut last_reload: DateTime<Utc>; + let mut lemmy: Lemmy; + let mut login_error: bool; + let mut communities; + { + let mut write = data.write().await; -macro_rules! info { - ($msg:tt) => { - match connected_to_journal() { - true => log::info!("[INFO] {}", $msg), - false => println!("[INFO] {}", $msg), - } - }; -} - -macro_rules! error { - ($msg:tt) => { - match connected_to_journal() { - true => log::error!("[ERROR] {}", $msg), - false => eprintln!("[ERROR] {}", $msg), - } - }; -} - -pub(crate) struct Bot { - shared_config: Arc<RwLock<Config>>, - history: SeriesHistory, - run_start_time: DateTime<Utc> -} - -enum Wait { - Absolute, - Buffer -} - -impl Bot { - pub(crate) fn new() -> Self { - let config = Config::load(); - let shared_config: Arc<RwLock<Config>> = Arc::new(RwLock::new(config)); - - let shared_config_copy = shared_config.clone(); - let mut watcher = notify::recommended_watcher(move |res: Result<Event, notify::Error>| { - match res { - Ok(event) => { - if event.kind == EventKind::Access(AccessKind::Close(AccessMode::Write)) { - let mut write = shared_config_copy.write().expect("Write Lock Failed"); - let new_config = Config::load(); - write.series = new_config.series; - write.instance = new_config.instance; - write.protected_communities = new_config.protected_communities; - write.status_post_url = new_config.status_post_url; - info!("Reloaded Configuration"); - } - }, - Err(e) => { - let msg = format!("Error watching files: {e}"); - error!(msg); - } - } - }).expect("Watcher Error"); - - watcher.watch(&Config::get_path(), RecursiveMode::NonRecursive).expect("Error in watcher"); - - let history: SeriesHistory = SeriesHistory::load_history(); - - Bot { shared_config, history, run_start_time: Utc::now() } - } - pub(crate) async fn run(&mut self) { - loop { - let mut lemmy = match Lemmy::new(&self.shared_config).await { - Ok(data) => data, - Err(_) => continue, - }; - - lemmy.get_communities().await; - - self.history = SeriesHistory::load_history(); - - let start: DateTime<Utc> = Utc::now(); - while Utc::now() - start <= Duration::minutes(60) { - self.run_start_time = Utc::now(); - self.ping_status().await; - let read_copy = self.shared_config.read().expect("Read Lock Failed").clone(); - for series in read_copy.series { - series.update(&mut self.history, &lemmy, &self.shared_config).await; - debug!("Done Updating Series"); - self.wait(1, Wait::Absolute).await; - } - debug!("Awaiting Timeout"); - self.wait(30, Wait::Buffer).await; - debug!("Pinging Server"); - self.ping_status().await; - debug!("Awaiting Timeout 2"); - self.wait(30, Wait::Absolute).await; - } - - lemmy.logout().await; - } + // Errors during bot init are likely unrecoverable and therefore should panic the bot + // Does not really matter since the bot will get restarted anyway but this way the uptime url logs a downtime + write.config = Config::load(); + last_reload = Utc::now(); } - async fn ping_status(&self) { - let read_config = &self.shared_config.read().expect("Read Lock Failed").clone(); - if let Some(status_url) = &read_config.status_post_url { - match HTTP_CLIENT.get(status_url).send().await { - Ok(_) => {}, - Err(e) => { - let err_msg = format!("While pinging status URL: {e}"); - error!(err_msg); - } - } - } - } - - async fn wait(&self, seconds: i64, start_time: Wait) { - let duration: Duration = Duration::seconds(seconds); - let start_time: DateTime<Utc> = match start_time { - Wait::Absolute => Utc::now(), - Wait::Buffer => self.run_start_time, + { + let read = data.read().await; + lemmy = match lemmy::login(&read.config).await { + Ok(data) => data, + Err(_) => panic!(), }; - while Utc::now() - start_time < duration { - sleep(Duration::milliseconds(100).to_std().unwrap()).await + login_error = false; + + communities = match lemmy.get_communities().await { + Ok(data) => data, + Err(_) => panic!(), + }; + } + + { + let mut write = data.write().await; + write.start = Utc::now(); + } + + let info_msg = "Bot init successful, starting normal operations".to_owned(); + write_info(info_msg); + + loop { + idle(&data).await; + + { + let mut write = data.write().await; + + write.start = Utc::now(); + + if write.start - last_reload >= Duration::seconds(write.config.config_reload_seconds as i64) { + write.config = Config::load(); + let message = "Config reloaded".to_owned(); + write_info(message); + } } + + { + let read = data.read().await; + if login_error { + lemmy = match lemmy::login(&read.config).await { + Ok(data) => data, + Err(_) => continue, + }; + login_error = false; + } + } + + { + let read = data.read().await; + if read.start - last_reload >= Duration::seconds(read.config.config_reload_seconds as i64) { + communities = match lemmy.get_communities().await { + Ok(data) => data, + Err(_) => { + login_error = true; + continue; + } + }; + let message = "Communities reloaded".to_owned(); + write_info(message); + last_reload = Utc::now(); + } + } + + { + let mut write = data.write().await; + write.post_history = SeriesHistory::load_history(); + } + + { + let read = data.read().await; + let series = read.config.series.clone(); + drop(read); + for series in series { + if handle_series(&series, &communities, &lemmy, &data) + .await + .is_err() + { + login_error = true; + continue; + }; + } + } + + idle(&data).await; } } + +async fn idle(data: &Arc<RwLock<SharedData>>) { + let read = data.read().await; + let mut sleep_duration = Duration::seconds(30); + if Utc::now() - read.start > sleep_duration { + sleep_duration = Duration::seconds(60); + } + + if let Some(status_url) = read.config.status_post_url.clone() { + match reqwest::get(status_url).await { + Ok(_) => {} + Err(e) => { + let err_msg = format!("{e}"); + write_error(err_msg); + } + } + }; + + while Utc::now() - read.start < sleep_duration { + sleep(Duration::milliseconds(100).to_std().unwrap()).await; + } +} + +async fn handle_series(series: &SeriesConfig, communities: &HashMap<String, CommunityId>, lemmy: &Lemmy, data: &Arc<RwLock<SharedData>>) -> Result<(), ()> { + let jnc = jnovel::JFetcherOptions::new(series.slug.clone(), series.parted); + let post_list = match jnc.check_feed().await { + Ok(data) => data, + Err(_) => return Err(()), + }; + + for post_info in post_list.clone().iter() { + // todo .clone() likely not needed + let post_part_info = post_info.get_part_info(); + let post_lemmy_info = post_info.get_info(); + + { + let read = data.read().await; + if read.post_history.check_for_post( + series.slug.as_str(), + post_part_info.as_string().as_str(), + post_lemmy_info.title.as_str(), + ) { + continue; + } + } + + let post_series_config = match post_info { + JPostInfo::Chapter { .. } => &series.prepub_community, + JPostInfo::Volume { .. } => &series.volume_community, + }; + + let community_id = *communities + .get(post_series_config.name.as_str()) + .expect("Given community is invalid"); + + let post_body = match &post_series_config.post_body { + PostBody::None => None, + PostBody::Description => post_info.get_description(), + PostBody::Custom(text) => Some(text.clone()), + }; + + let post_data = CreatePost { + name: post_lemmy_info.title.clone(), + community_id, + url: Some(post_lemmy_info.url), + body: post_body, + honeypot: None, + nsfw: None, + language_id: Some(LanguageId(37)), // TODO get this id once every few hours per API request, the ordering of IDs suggests that the EN Id might change in the future + }; + + let info = format!( + "Posting '{}' to {}", + post_lemmy_info.title.as_str(), + post_series_config.name.as_str() + ); + write_info(info); + let post_id = lemmy.post(post_data).await?; + + { + let read = data.read().await; + if post_series_config.pin_settings.pin_new_post_community + && !read + .config + .protected_communities + .contains(&post_series_config.name) + { + let info = format!( + "Pinning '{}' to {}", + post_lemmy_info.title, + post_series_config.name.as_str() + ); + write_info(info); + let pinned_posts = lemmy.get_community_pinned(community_id).await?; + if !pinned_posts.is_empty() { + let community_pinned_post = &pinned_posts[0]; + lemmy + .unpin(community_pinned_post.post.id, PostFeatureType::Community) + .await?; + } + lemmy.pin(post_id, PostFeatureType::Community).await?; + } else if read + .config + .protected_communities + .contains(&post_series_config.name) + { + let message = format!( + "Community '{}' for Series '{}' is protected. Is this intended?", + &post_series_config.name, series.slug + ); + write_warn(message); + } + } + + let read = data.read().await; + if post_series_config.pin_settings.pin_new_post_local { + let info = format!("Pinning '{}' to Instance", post_lemmy_info.title); + write_info(info); + let pinned_posts = lemmy.get_local_pinned().await?; + if !pinned_posts.is_empty() { + for pinned_post in pinned_posts { + if read + .config + .protected_communities + .contains(&pinned_post.community.name) + { + continue; + } else { + let community_pinned_post = &pinned_post; + lemmy + .unpin(community_pinned_post.post.id, PostFeatureType::Local) + .await?; + break; + } + } + } + lemmy.pin(post_id, PostFeatureType::Local).await?; + } + + let mut series_history = read.post_history.get_series(series.slug.as_str()); + let mut part_history = series_history.get_part(post_part_info.as_string().as_str()); + drop(read); + + match post_info { + JPostInfo::Chapter { .. } => part_history.chapter = post_info.get_info().title, + JPostInfo::Volume { .. } => part_history.volume = post_info.get_info().title, + } + + series_history.set_part(post_part_info.as_string().as_str(), part_history); + let mut write = data.write().await; + write + .post_history + .set_series(series.slug.as_str(), series_history); + write.post_history.save_history(); + } + Ok(()) +} diff --git a/src/config.rs b/src/config.rs index d392db0..776d868 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,57 +1,12 @@ -use std::path::PathBuf; -use std::sync::{Arc, RwLock}; -use chrono::{Timelike, Utc}; use crate::config::PostBody::Description; -use lemmy_db_schema::PostFeatureType; -use lemmy_db_schema::sensitive::SensitiveString; +use lemmy_api_common::sensitive::Sensitive; use serde_derive::{Deserialize, Serialize}; -use crate::lemmy::{Lemmy, PartInfo, PostType}; -use crate::post_history::{SeriesHistory}; -use systemd_journal_logger::connected_to_journal; -use crate::fetchers::{FetcherTrait, Fetcher}; -use crate::fetchers::jnovel::{JNovelFetcher}; - -macro_rules! debug { - ($msg:tt) => { - match connected_to_journal() { - true => log::debug!("[DEBUG] {}", $msg), - false => println!("[DEBUG] {}", $msg), - } - }; -} - -macro_rules! info { - ($msg:tt) => { - match connected_to_journal() { - true => log::info!("[INFO] {}", $msg), - false => println!("[INFO] {}", $msg), - } - }; -} - -macro_rules! warn { - ($msg:tt) => { - match connected_to_journal() { - true => log::warn!("[WARN] {}", $msg), - false => println!("[WARN] {}", $msg), - } - }; -} - -macro_rules! error { - ($msg:tt) => { - match connected_to_journal() { - true => log::error!("[ERROR] {}", $msg), - false => eprintln!("[ERROR] {}", $msg), - } - }; -} #[derive(Serialize, Deserialize, Clone, Debug)] pub(crate) struct Config { pub(crate) instance: String, - username: SensitiveString, - password: SensitiveString, + username: String, + password: String, pub(crate) status_post_url: Option<String>, pub(crate) config_reload_seconds: u32, pub(crate) protected_communities: Vec<String>, @@ -85,16 +40,12 @@ impl Config { cfg } - pub(crate) fn get_path() -> PathBuf { - confy::get_configuration_file_path(env!("CARGO_PKG_NAME"), "config").expect("Application will not without confy") + pub(crate) fn get_username(&self) -> Sensitive<String> { + Sensitive::new(self.username.clone()) } - pub(crate) fn get_username(&self) -> SensitiveString { - self.username.clone() - } - - pub(crate) fn get_password(&self) -> SensitiveString { - self.password.clone() + pub(crate) fn get_password(&self) -> Sensitive<String> { + Sensitive::new(self.password.clone()) } } @@ -102,8 +53,8 @@ impl Default for Config { fn default() -> Self { Config { instance: "".to_owned(), - username: SensitiveString::from("".to_owned()), - password: SensitiveString::from("".to_owned()), + username: "".to_owned(), + password: "".to_owned(), status_post_url: None, config_reload_seconds: 21600, protected_communities: vec![], @@ -118,168 +69,6 @@ pub(crate) struct SeriesConfig { pub(crate) parted: bool, pub(crate) prepub_community: PostConfig, pub(crate) volume_community: PostConfig, - pub(crate) fetcher: Fetcher -} - -impl SeriesConfig { - pub(crate) async fn update(&self, history: &mut SeriesHistory, lemmy: &Lemmy, config: &Arc<RwLock<Config>>) { - let info_msg = format!("Checking {} for Updates", self.slug); - info!(info_msg); - - let mut fetcher: Fetcher = match &self.fetcher { - Fetcher::Jnc(_) => { - Fetcher::Jnc(JNovelFetcher::new()) - }, - /*default => { - let err_msg = format!("Fetcher {default} not implemented"); - error!(err_msg); - return; - }*/ - }; - - match fetcher { - Fetcher::Jnc(ref mut jnc) => { - jnc.set_series(self.slug.clone()); - jnc.set_part_option(self.parted); - } - } - - let post_list = match fetcher.check_feed().await { - Ok(data) => data, - Err(_) => { - let err_msg = format!("While checking feed for {}", self.slug); - error!(err_msg); - return; - } - }; - - if post_list.is_empty() && Utc::now().minute() % 10 == 0 { - let info_msg = "No Updates found"; - info!(info_msg); - } - - for post_info in post_list.iter() { - if history.check_for_post( - self.slug.as_str(), - post_info.get_part_info().unwrap_or(PartInfo::NoParts).as_string().as_str(), - post_info.get_info().title.as_str() - ) { - continue - } - - let post_data = post_info.get_post_data(self, lemmy); - - let info = format!( - "Posting '{}' to {}", - post_info.get_info().title.as_str(), - post_info.get_post_config(self).name.as_str() - ); - info!(info); - - let post_id = match lemmy.post(post_data.clone()).await { - Some(data) => data, - None => { - error!("Error posting chapter, applying fix for Issue #27"); - match lemmy.check_community_for_post(post_data).await { - Some(data) => data, - None => { - error!("Unable to find Post via API"); - return; - } - } - } - }; - - let read_config = config.read().expect("Read Lock Failed").clone(); - - if post_info.get_post_config(self).pin_settings.pin_new_post_community - && !read_config - .protected_communities - .contains(&post_info.get_post_config(self).name) - { - let info = format!( - "Pinning '{}' to {}", - post_info.get_info().title, - post_info.get_post_config(self).name.as_str() - ); - info!(info); - let pinned_posts = lemmy.get_community_pinned(lemmy.get_community_id(&post_info.get_post_config(self).name)).await.unwrap_or_else(|| { - error!("Pinning of Post to community failed"); - vec![] - }); - if !pinned_posts.is_empty() { - let community_pinned_post = &pinned_posts[0]; - if lemmy.unpin(community_pinned_post.post.id, PostFeatureType::Community).await.is_none() { - error!("Error un-pinning post"); - } - } - if lemmy.pin(post_id, PostFeatureType::Community).await.is_none() { - error!("Error pinning post"); - } - } else if read_config - .protected_communities - .contains(&post_info.get_post_config(self).name) - { - let message = format!( - "Community '{}' for Series '{}' is protected. Is this intended?", - &post_info.get_post_config(self).name, self.slug - ); - warn!(message); - } - - if post_info.get_post_config(self).pin_settings.pin_new_post_local { - let info = format!("Pinning '{}' to Instance", post_info.get_info().title); - info!(info); - let pinned_posts = match lemmy.get_local_pinned().await { - Some(data) => {data} - None => { - error!("Error fetching pinned posts"); - vec![] - } - }; - - if !pinned_posts.is_empty() { - for pinned_post in pinned_posts { - if read_config - .protected_communities - .contains(&pinned_post.community.name) - { - continue; - } else { - let community_pinned_post = &pinned_post; - if lemmy.unpin(community_pinned_post.post.id, PostFeatureType::Local).await.is_none() { - error!("Error pinning post"); - continue; - } - break; - } - } - } - if lemmy.pin(post_id, PostFeatureType::Local).await.is_none() { - error!("Error pinning post"); - }; - } - - let mut series_history = history.get_series(self.slug.as_str()); - let mut part_history = series_history.get_part(post_info.get_part_info().unwrap_or(PartInfo::NoParts).as_string().as_str()); - - match post_info.post_type { - Some(post_type) => { - match post_type { - PostType::Chapter => part_history.chapter = post_info.get_info().title, - PostType::Volume => part_history.volume = post_info.get_info().title, - } - } - None => part_history.chapter = post_info.get_info().title, - } - - series_history.set_part(post_info.get_part_info().unwrap_or(PartInfo::NoParts).as_string().as_str(), part_history); - history - .set_series(self.slug.as_str(), series_history); - debug!("Saving History"); - history.save_history(); - } - } } #[derive(Debug, Serialize, Deserialize, Clone)] diff --git a/src/fetchers/jnovel.rs b/src/fetchers/jnovel.rs index c5a0a42..6bebc7d 100644 --- a/src/fetchers/jnovel.rs +++ b/src/fetchers/jnovel.rs @@ -1,37 +1,21 @@ -use crate::{HTTP_CLIENT}; +use crate::{write_error, HTTP_CLIENT, lemmy}; use chrono::{DateTime, Duration, Utc}; use serde_derive::{Deserialize, Serialize}; +use std::cmp::Ordering; use std::collections::HashMap; use std::ops::Sub; use async_trait::async_trait; -use crate::fetchers::{FetcherTrait}; -use crate::lemmy::{PartInfo, PostInfo, PostInfoInner, PostType}; -use systemd_journal_logger::connected_to_journal; -use crate::lemmy::PartInfo::{NoParts, Part}; - -macro_rules! error { - ($msg:tt) => { - match connected_to_journal() { - true => log::error!("[ERROR] {}", $msg), - false => eprintln!("[ERROR] {}", $msg), - } - }; -} - -macro_rules! info { - ($msg:tt) => { - match connected_to_journal() { - true => log::info!("[INFO] {}", $msg), - false => println!("[INFO] {}", $msg), - } - }; -} +use url::Url; +use crate::fetchers::Fetcher; +use crate::fetchers::jnovel::JPostInfo::{Chapter, Volume}; +use crate::fetchers::jnovel::PartInfo::{NoParts, Part}; +use crate::lemmy::{PostInfo, PostInfoInner}; static PAST_DAYS_ELIGIBLE: u8 = 4; macro_rules! api_url { () => { - "https://labs.j-novel.club/app/v2".to_owned() + "https://labs.j-novel.club/app/v1".to_owned() }; } @@ -88,41 +72,191 @@ pub(crate) struct ChapterDetail { pub(crate) cover: Option<Cover>, } -#[derive(Deserialize, Serialize, Debug, Clone)] -pub(crate) struct JNovelFetcher { +#[derive(Debug, Copy, Clone)] +pub(crate) enum PartInfo { + NoParts, + Part(u8), +} + +impl PartInfo { + pub(crate) fn as_u8(&self) -> u8 { + match self { + Part(number) => *number, + NoParts => 0, + } + } + + pub(crate) fn as_string(&self) -> String { + self.as_u8().to_string() + } +} + +impl PartialEq for PartInfo { + fn eq(&self, other: &Self) -> bool { + let self_numeric = self.as_u8(); + let other_numeric = other.as_u8(); + self_numeric == other_numeric + } +} + +impl PartialOrd for PartInfo { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + if self.gt(other) { + Some(Ordering::Greater) + } else if self.eq(other) { + Some(Ordering::Equal) + } else { + Some(Ordering::Less) + } + } + + fn lt(&self, other: &Self) -> bool { + let self_numeric = self.as_u8(); + let other_numeric = other.as_u8(); + + self_numeric < other_numeric + } + + fn le(&self, other: &Self) -> bool { + !self.gt(other) + } + + fn gt(&self, other: &Self) -> bool { + let self_numeric = self.as_u8(); + let other_numeric = other.as_u8(); + + self_numeric > other_numeric + } + + fn ge(&self, other: &Self) -> bool { + !self.lt(other) + } +} + +#[derive(Debug, Clone)] +pub(crate) enum JPostInfo { + Chapter { + part: PartInfo, + lemmy_info: PostInfoInner, + }, + Volume { + part: PartInfo, + description: String, + lemmy_info: PostInfoInner, + }, +} + +impl JPostInfo { + pub(crate) fn get_part_info(&self) -> PartInfo { + match self { + Chapter { + part: part_info, .. + } => *part_info, + Volume { + part: part_info, .. + } => *part_info, + } + } +} + +impl PostInfo for JPostInfo { + fn get_info(&self) -> PostInfoInner { + match self { + Chapter { lemmy_info, .. } => lemmy_info.clone(), + Volume { lemmy_info, .. } => lemmy_info.clone(), + } + } + + fn get_description(&self) -> Option<String> { + match self { + Chapter { .. } => None, + Volume { description, .. } => Some(description.clone()), + } + } +} + +impl PartialEq for JPostInfo { + fn eq(&self, other: &Self) -> bool { + let self_part = match self { + Chapter { part, .. } => part, + Volume { part, .. } => part, + }; + + let other_part = match other { + Chapter { part, .. } => part, + Volume { part, .. } => part, + }; + + self_part.eq(other_part) + } +} + +impl PartialOrd for JPostInfo { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + if self.gt(other) { + Some(Ordering::Greater) + } else if self.eq(other) { + Some(Ordering::Equal) + } else { + Some(Ordering::Less) + } + } + + fn lt(&self, other: &Self) -> bool { + let self_part = match self { + Chapter { part, .. } => part, + Volume { part, .. } => part, + }; + + let other_part = match other { + Chapter { part, .. } => part, + Volume { part, .. } => part, + }; + + self_part < other_part + } + + fn le(&self, other: &Self) -> bool { + !self.gt(other) + } + + fn gt(&self, other: &Self) -> bool { + let self_part = match self { + Chapter { part, .. } => part, + Volume { part, .. } => part, + }; + + let other_part = match other { + Chapter { part, .. } => part, + Volume { part, .. } => part, + }; + + self_part > other_part + } + + fn ge(&self, other: &Self) -> bool { + !self.lt(other) + } +} + +pub(crate) struct JFetcherOptions { series_slug: String, series_has_parts: bool } -impl Default for JNovelFetcher { - fn default() -> Self { - Self { - series_slug: "".to_owned(), - series_has_parts: false, +impl JFetcherOptions { + pub(crate) fn new(series_slug: String, series_has_parts: bool) -> Self { + JFetcherOptions { + series_slug, + series_has_parts } } } -impl JNovelFetcher { - pub(crate) fn set_series(&mut self, series: String) { - self.series_slug = series; - } - - pub(crate) fn set_part_option(&mut self, has_parts: bool) { - self.series_has_parts = has_parts; - } -} - #[async_trait] -impl FetcherTrait for JNovelFetcher { - fn new() -> Self { - JNovelFetcher { - series_slug: "".to_owned(), - series_has_parts: false - } - } - - async fn check_feed(&self) -> Result<Vec<PostInfo>, ()> { +impl Fetcher for JFetcherOptions { + type Return = JPostInfo; + async fn check_feed(&self) -> Result<Vec<Self::Return>, ()> { let response = match HTTP_CLIENT .get(api_url!() + "/series/" + self.series_slug.as_str() + "/volumes?format=json") .send() @@ -131,14 +265,14 @@ impl FetcherTrait for JNovelFetcher { Ok(data) => match data.text().await { Ok(data) => data, Err(e) => { - let err_msg = format!("While checking feed: {e}"); - error!(err_msg); + let err_msg = format!("{e}"); + write_error(err_msg); return Err(()); } }, Err(e) => { let err_msg = format!("{e}"); - error!(err_msg); + write_error(err_msg); return Err(()); } }; @@ -147,15 +281,15 @@ impl FetcherTrait for JNovelFetcher { Ok(data) => data, Err(e) => { let err_msg = format!("{e}"); - error!(err_msg); + write_error(err_msg); return Err(()); } }; volume_brief_data.volumes.reverse(); // Makes breaking out of the volume loop easier // If no parts just use 0 as Part indicator as no Series with Parts has a Part 0 - let mut volume_map: HashMap<u8, PostInfo> = HashMap::new(); - let mut prepub_map: HashMap<u8, PostInfo> = HashMap::new(); + let mut volume_map: HashMap<u8, JPostInfo> = HashMap::new(); + let mut prepub_map: HashMap<u8, JPostInfo> = HashMap::new(); for volume in volume_brief_data.volumes.iter() { let publishing_date = DateTime::parse_from_rfc3339(&volume.publishing).unwrap(); @@ -184,7 +318,7 @@ impl FetcherTrait for JNovelFetcher { match part_number { Some(number) => new_part_info = Part(number), None => { - info!("No Part found, assuming 1"); + println!("No Part found, assuming 1"); new_part_info = Part(1); } } @@ -198,16 +332,14 @@ impl FetcherTrait for JNovelFetcher { self.series_slug.as_str(), volume.number ); - let post_details = PostInfoInner { + let post_details = lemmy::PostInfoInner { title: volume.title.clone(), - url: post_url.clone(), - thumbnail: Some(volume.cover.thumbnail.clone()) + url: Url::parse(&post_url).unwrap(), }; - let new_post_info = PostInfo { - post_type: Some(PostType::Volume), - part: Some(new_part_info), - description: Some(volume.short_description.clone()), + let new_post_info = Volume { + part: new_part_info, + description: volume.short_description.clone(), lemmy_info: post_details, }; @@ -224,12 +356,10 @@ impl FetcherTrait for JNovelFetcher { .or_insert(new_post_info); } - if let Some(prepub_info) = get_latest_prepub(&volume.slug).await { - let prepub_post_info = PostInfo { - post_type: Some(PostType::Chapter), - part: Some(new_part_info), + if let Some(prepub_info) = get_latest_prepub(&volume.slug).await? { + let prepub_post_info = Chapter { + part: new_part_info, lemmy_info: prepub_info, - description: None, }; prepub_map @@ -243,8 +373,8 @@ impl FetcherTrait for JNovelFetcher { } } - let mut result_vec: Vec<PostInfo> = volume_map.values().cloned().collect(); - let mut prepub_vec: Vec<PostInfo> = prepub_map.values().cloned().collect(); + let mut result_vec: Vec<JPostInfo> = volume_map.values().cloned().collect(); + let mut prepub_vec: Vec<JPostInfo> = prepub_map.values().cloned().collect(); result_vec.append(&mut prepub_vec); Ok(result_vec) @@ -252,7 +382,7 @@ impl FetcherTrait for JNovelFetcher { } -async fn get_latest_prepub(volume_slug: &str) -> Option<PostInfoInner> { +async fn get_latest_prepub(volume_slug: &str) -> Result<Option<lemmy::PostInfoInner>, ()> { let response = match HTTP_CLIENT .get(api_url!() + "/volumes/" + volume_slug + "/parts?format=json") .send() @@ -261,15 +391,15 @@ async fn get_latest_prepub(volume_slug: &str) -> Option<PostInfoInner> { Ok(data) => match data.text().await { Ok(data) => data, Err(e) => { - let err_msg = format!("While getting latest PrePub: {e}"); - error!(err_msg); - return None; + let err_msg = format!("{e}"); + write_error(err_msg); + return Err(()); } }, Err(e) => { let err_msg = format!("{e}"); - error!(err_msg); - return None; + write_error(err_msg); + return Err(()); } }; @@ -277,13 +407,13 @@ async fn get_latest_prepub(volume_slug: &str) -> Option<PostInfoInner> { Ok(data) => data, Err(e) => { let err_msg = format!("{e}"); - error!(err_msg); - return None; + write_error(err_msg); + return Err(()); } }; volume_prepub_parts_data.parts.reverse(); // Makes breaking out of the parts loop easier - let mut post_details: Option<PostInfoInner> = None; + let mut post_details: Option<lemmy::PostInfoInner> = None; for prepub_part in volume_prepub_parts_data.parts.iter() { let publishing_date = DateTime::parse_from_rfc3339(&prepub_part.launch).unwrap(); @@ -293,15 +423,12 @@ async fn get_latest_prepub(volume_slug: &str) -> Option<PostInfoInner> { continue; } - let thumbnail = prepub_part.cover.as_ref().map(|cover| cover.thumbnail.clone()); - let post_url = format!("{}/read/{}", jnc_base_url!(), prepub_part.slug); - post_details = Some(PostInfoInner { + post_details = Some(lemmy::PostInfoInner { title: prepub_part.title.clone(), - url: post_url.clone(), - thumbnail + url: Url::parse(&post_url).unwrap(), }); } - post_details + Ok(post_details) } diff --git a/src/fetchers/mod.rs b/src/fetchers/mod.rs index 0f2eacc..fd143fb 100644 --- a/src/fetchers/mod.rs +++ b/src/fetchers/mod.rs @@ -1,33 +1,10 @@ use async_trait::async_trait; -use serde_derive::{Deserialize, Serialize}; -use strum_macros::Display; -use crate::fetchers::Fetcher::Jnc; -use crate::fetchers::jnovel::JNovelFetcher; -use crate::lemmy::{PostInfo}; pub mod jnovel; +mod tobooks; #[async_trait] -pub(crate) trait FetcherTrait { - fn new() -> Self where Self: Sized; - async fn check_feed(&self) -> Result<Vec<PostInfo>, ()>; -} - -impl Fetcher { - pub(crate) async fn check_feed(&self) -> Result<Vec<PostInfo>, ()> { - match self { - Jnc(fetcher) => fetcher.check_feed().await, - /*default => { - let err_msg = format!("Fetcher {default} is not implemented"); - error!(err_msg); - Err(()) - }*/ - } - } -} - -#[derive(Deserialize, Serialize, Debug, Clone, Display)] -pub(crate) enum Fetcher { - #[serde(rename = "jnc")] - Jnc(#[serde(skip)] JNovelFetcher) +pub(crate) trait Fetcher { + type Return; + async fn check_feed(&self) -> Result<Vec<Self::Return>, ()>; } diff --git a/src/fetchers/tobooks.rs b/src/fetchers/tobooks.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/lemmy.rs b/src/lemmy.rs index ccfb2d2..a07b901 100644 --- a/src/lemmy.rs +++ b/src/lemmy.rs @@ -1,304 +1,153 @@ -use std::cmp::Ordering; -use crate::config::{Config, PostBody, PostConfig, SeriesConfig}; -use crate::{HTTP_CLIENT}; +use crate::config::Config; +use crate::{write_error, HTTP_CLIENT}; use lemmy_api_common::community::{ListCommunities, ListCommunitiesResponse}; use lemmy_api_common::lemmy_db_views::structs::PostView; use lemmy_api_common::person::{Login, LoginResponse}; use lemmy_api_common::post::{CreatePost, FeaturePost, GetPosts, GetPostsResponse}; -use lemmy_db_schema::newtypes::{CommunityId, LanguageId, PostId}; -use lemmy_db_schema::{ListingType, PostFeatureType, SortType}; +use lemmy_api_common::sensitive::Sensitive; +use lemmy_db_schema::newtypes::{CommunityId, PostId}; +use lemmy_db_schema::{ListingType, PostFeatureType}; use reqwest::StatusCode; use std::collections::HashMap; -use std::sync::{RwLock}; -use lemmy_db_schema::sensitive::SensitiveString; -use serde::{Deserialize, Serialize}; -use systemd_journal_logger::connected_to_journal; - -macro_rules! debug { - ($msg:tt) => { - match connected_to_journal() { - true => log::debug!("[DEBUG] {}", $msg), - false => println!("[DEBUG] {}", $msg), - } - }; -} - -macro_rules! info { - ($msg:tt) => { - match connected_to_journal() { - true => log::info!("[INFO] {}", $msg), - false => println!("[INFO] {}", $msg), - } - }; -} - -macro_rules! error { - ($msg:tt) => { - match connected_to_journal() { - true => log::error!("[ERROR] {}", $msg), - false => eprintln!("[ERROR] {}", $msg), - } - }; -} +use url::Url; pub(crate) struct Lemmy { - jwt_token: SensitiveString, + jwt_token: Sensitive<String>, instance: String, - communities: HashMap<String, CommunityId>, } #[derive(Debug, Clone)] pub(crate) struct PostInfoInner { pub(crate) title: String, - pub(crate) url: String, - pub(crate) thumbnail: Option<String> + pub(crate) url: Url, } -#[derive(Debug, Copy, Clone)] -pub(crate) enum PartInfo { - NoParts, - Part(u8), +pub(crate) trait PostInfo { + fn get_info(&self) -> PostInfoInner; + + fn get_description(&self) -> Option<String>; } -impl PartInfo { - pub(crate) fn as_u8(&self) -> u8 { - match self { - PartInfo::Part(number) => *number, - PartInfo::NoParts => 0, +pub(crate) async fn login(config: &Config) -> Result<Lemmy, ()> { + let login_params = Login { + username_or_email: config.get_username(), + password: config.get_password(), + totp_2fa_token: None, + }; + + let response = match HTTP_CLIENT + .post(config.instance.to_owned() + "/api/v3/user/login") + .json(&login_params) + .send() + .await + { + Ok(data) => data, + Err(e) => { + let err_msg = format!("{e}"); + write_error(err_msg); + return Err(()); } - } + }; - pub(crate) fn as_string(&self) -> String { - self.as_u8().to_string() - } -} - -impl PartialEq for PartInfo { - fn eq(&self, other: &Self) -> bool { - let self_numeric = self.as_u8(); - let other_numeric = other.as_u8(); - self_numeric == other_numeric - } -} - -impl PartialOrd for PartInfo { - fn partial_cmp(&self, other: &Self) -> Option<Ordering> { - if self.gt(other) { - Some(Ordering::Greater) - } else if self.eq(other) { - Some(Ordering::Equal) - } else { - Some(Ordering::Less) - } - } - - fn lt(&self, other: &Self) -> bool { - let self_numeric = self.as_u8(); - let other_numeric = other.as_u8(); - - self_numeric < other_numeric - } - - fn le(&self, other: &Self) -> bool { - !self.gt(other) - } - - fn gt(&self, other: &Self) -> bool { - let self_numeric = self.as_u8(); - let other_numeric = other.as_u8(); - - self_numeric > other_numeric - } - - fn ge(&self, other: &Self) -> bool { - !self.lt(other) - } -} - -#[derive(Debug, Clone, Copy)] -pub(crate) enum PostType { - Chapter, - Volume -} - -#[derive(Debug, Clone)] -pub(crate) struct PostInfo { - pub(crate) part: Option<PartInfo>, - pub(crate) lemmy_info: PostInfoInner, - pub(crate) description: Option<String>, - pub(crate) post_type: Option<PostType> -} - -impl PostInfo { - pub(crate)fn get_info(&self) -> PostInfoInner { - self.lemmy_info.clone() - } - - pub(crate)fn get_description(&self) -> Option<String> { - self.description.clone() - } - - pub(crate) fn get_part_info(&self) -> Option<PartInfo> { - self.part - } - - pub(crate) fn get_post_config(&self, series: &SeriesConfig) -> PostConfig { - match self.post_type { - Some(post_type) => { - match post_type { - PostType::Chapter => series.prepub_community.clone(), - PostType::Volume => series.volume_community.clone(), + match response.status() { + StatusCode::OK => { + let data: LoginResponse = response + .json() + .await + .expect("Successful Login Request should return JSON"); + match data.jwt { + Some(token) => Ok(Lemmy { + jwt_token: token.clone(), + instance: config.instance.to_owned(), + }), + None => { + let err_msg = "Login did not return JWT token. Are the credentials valid?".to_owned(); + write_error(err_msg); + Err(()) } } - None => series.prepub_community.clone(), } - } - - pub(crate) fn get_post_data(&self, series: &SeriesConfig, lemmy: &Lemmy) -> CreatePost { - let post_config = self.get_post_config(series); - - let post_body = match &post_config.post_body { - PostBody::None => None, - PostBody::Description => self.get_description(), - PostBody::Custom(text) => Some(text.clone()), - }; - - let community_id: CommunityId = lemmy.get_community_id(&post_config.name); - - CreatePost { - name: self.get_info().title.clone(), - community_id, - url: Some(self.get_info().url), - custom_thumbnail: self.get_info().thumbnail, - body: post_body, - alt_text: None, - honeypot: None, - nsfw: None, - language_id: Some(LanguageId(37)), // TODO get this id once every few hours per API request, the ordering of IDs suggests that the EN Id might change in the future + status => { + let err_msg = format!("Unexpected HTTP Status '{}' during Login", status); + write_error(err_msg); + Err(()) } } } -impl PartialEq for PostInfo { - fn eq(&self, other: &Self) -> bool { - self.part.eq(&other.part) - } -} - -impl PartialOrd for PostInfo { - fn partial_cmp(&self, other: &Self) -> Option<Ordering> { - if self.gt(other) { - Some(Ordering::Greater) - } else if self.eq(other) { - Some(Ordering::Equal) - } else { - Some(Ordering::Less) - } - } - - fn lt(&self, other: &Self) -> bool { - self.part < other.part - } - - fn le(&self, other: &Self) -> bool { - !self.gt(other) - } - - fn gt(&self, other: &Self) -> bool { - self.part > other.part - } - - fn ge(&self, other: &Self) -> bool { - !self.lt(other) - } -} - impl Lemmy { - pub(crate) fn get_community_id(&self, name: &str) -> CommunityId { - *self.communities.get(name).expect("Given community is invalid") - } - pub(crate) async fn new(config: &RwLock<Config>) -> Result<Self, ()> { - let read_config = config.read().expect("Read Lock Failed").clone(); - let login_params = Login { - username_or_email: read_config.get_username(), - password: read_config.get_password(), - totp_2fa_token: None, - }; - + pub(crate) async fn post(&self, post: CreatePost) -> Result<PostId, ()> { let response = match HTTP_CLIENT - .post(read_config.instance.to_owned() + "/api/v3/user/login") - .json(&login_params) + .post(format!("{}/api/v3/post", &self.instance)) + .bearer_auth(&self.jwt_token.to_string()) + .json(&post) .send() .await { - Ok(data) => data, + Ok(data) => match data.text().await { + Ok(data) => data, + Err(e) => { + let err_msg = format!("{e}"); + write_error(err_msg); + return Err(()); + } + }, Err(e) => { let err_msg = format!("{e}"); - error!(err_msg); + write_error(err_msg); return Err(()); } }; - match response.status() { - StatusCode::OK => { - let data: LoginResponse = response - .json() - .await - .expect("Successful Login Request should return JSON"); - match data.jwt { - Some(token) => Ok(Lemmy { - jwt_token: token.clone(), - instance: read_config.instance.to_owned(), - communities: HashMap::new(), - }), - None => { - let err_msg = "Login did not return JWT token. Are the credentials valid?".to_owned(); - error!(err_msg); - Err(()) - } + let json_data = match serde_json::from_str::<HashMap<&str, PostView>>(&response) { + Ok(mut data) => data.remove("post_view").expect("Element should be present"), + Err(e) => { + let err_msg = format!("{e}"); + write_error(err_msg); + return Err(()); + } + }; + + Ok(json_data.post.id) + } + + async fn feature(&self, params: FeaturePost) -> Result<PostView, ()> { + let response = match HTTP_CLIENT + .post(format!("{}/api/v3/post/feature", &self.instance)) + .bearer_auth(&self.jwt_token.to_string()) + .json(¶ms) + .send() + .await + { + Ok(data) => match data.text().await { + Ok(data) => data, + Err(e) => { + let err_msg = format!("{e}"); + write_error(err_msg); + return Err(()); } + }, + Err(e) => { + let err_msg = format!("{e}"); + write_error(err_msg); + return Err(()); } - status => { - let err_msg = format!("Unexpected HTTP Status '{}' during Login", status); - error!(err_msg); - Err(()) + }; + + let json_data = match serde_json::from_str::<HashMap<&str, PostView>>(&response) { + Ok(mut data) => data.remove("post_view").expect("Element should be present"), + Err(e) => { + let err_msg = format!("{e}"); + write_error(err_msg); + return Err(()); } - } - } - - pub(crate) async fn logout(&self) { - let _ = self.post_data_json("/api/v3/user/logout", &"").await; - } - - - pub(crate) async fn post(&self, post: CreatePost) -> Option<PostId> { - let response: String = match self.post_data_json("/api/v3/post", &post).await { - Some(data) => data, - None => return None, - }; - let json_data: PostView = match self.parse_json_map(&response).await { - Some(data) => data, - None => return None, }; - Some(json_data.post.id) + Ok(json_data) } - async fn feature(&self, params: FeaturePost) -> Option<PostView> { - let response: String = match self.post_data_json("/api/v3/post/feature", ¶ms).await { - Some(data) => data, - None => return None, - }; - let json_data: PostView = match self.parse_json_map(&response).await { - Some(data) => data, - None => return None, - }; - - Some(json_data) - } - - pub(crate) async fn unpin(&self, post_id: PostId, location: PostFeatureType) -> Option<PostView> { + pub(crate) async fn unpin(&self, post_id: PostId, location: PostFeatureType) -> Result<PostView, ()> { let pin_params = FeaturePost { post_id, featured: false, @@ -307,7 +156,7 @@ impl Lemmy { self.feature(pin_params).await } - pub(crate) async fn pin(&self, post_id: PostId, location: PostFeatureType) -> Option<PostView> { + pub(crate) async fn pin(&self, post_id: PostId, location: PostFeatureType) -> Result<PostView, ()> { let pin_params = FeaturePost { post_id, featured: true, @@ -316,23 +165,45 @@ impl Lemmy { self.feature(pin_params).await } - pub(crate) async fn get_community_pinned(&self, community: CommunityId) -> Option<Vec<PostView>> { + pub(crate) async fn get_community_pinned(&self, community: CommunityId) -> Result<Vec<PostView>, ()> { let list_params = GetPosts { community_id: Some(community), type_: Some(ListingType::Local), ..Default::default() }; - let response: String = match self.get_data_query("/api/v3/post/list", &list_params).await { - Some(data) => data, - None => return None, - }; - let json_data: GetPostsResponse = match self.parse_json(&response).await { - Some(data) => data, - None => return None, + let response = match HTTP_CLIENT + .get(format!("{}/api/v3/post/list", &self.instance)) + .bearer_auth(&self.jwt_token.to_string()) + .query(&list_params) + .send() + .await + { + Ok(data) => match data.text().await { + Ok(data) => data, + Err(e) => { + let err_msg = format!("{e}"); + write_error(err_msg); + return Err(()); + } + }, + Err(e) => { + let err_msg = format!("{e}"); + write_error(err_msg); + return Err(()); + } }; - Some(json_data + let json_data: GetPostsResponse = match serde_json::from_str(&response) { + Ok(data) => data, + Err(e) => { + let err_msg = format!("{e}"); + write_error(err_msg); + return Err(()); + } + }; + + Ok(json_data .posts .iter() .filter(|post| post.post.featured_community) @@ -340,22 +211,44 @@ impl Lemmy { .collect()) } - pub(crate) async fn get_local_pinned(&self) -> Option<Vec<PostView>> { + pub(crate) async fn get_local_pinned(&self) -> Result<Vec<PostView>, ()> { let list_params = GetPosts { type_: Some(ListingType::Local), ..Default::default() }; - let response: String = match self.get_data_query("/api/v3/post/list", &list_params).await { - Some(data) => data, - None => return None, - }; - let json_data: GetPostsResponse = match self.parse_json(&response).await { - Some(data) => data, - None => return None, + let response = match HTTP_CLIENT + .get(format!("{}/api/v3/post/list", &self.instance)) + .bearer_auth(&self.jwt_token.to_string()) + .query(&list_params) + .send() + .await + { + Ok(data) => match data.text().await { + Ok(data) => data, + Err(e) => { + let err_msg = format!("{e}"); + write_error(err_msg); + return Err(()); + } + }, + Err(e) => { + let err_msg = format!("{e}"); + write_error(err_msg); + return Err(()); + } }; - Some(json_data + let json_data: GetPostsResponse = match serde_json::from_str(&response) { + Ok(data) => data, + Err(e) => { + let err_msg = format!("{e}"); + write_error(err_msg); + return Err(()); + } + }; + + Ok(json_data .posts .iter() .filter(|post| post.post.featured_local) @@ -363,19 +256,41 @@ impl Lemmy { .collect()) } - pub(crate) async fn get_communities(&mut self) { + pub(crate) async fn get_communities(&self) -> Result<HashMap<String, CommunityId>, ()> { let list_params = ListCommunities { type_: Some(ListingType::Local), ..Default::default() }; - let response: String = match self.get_data_query("/api/v3/community/list", &list_params).await { - Some(data) => data, - None => return, + let response = match HTTP_CLIENT + .get(format!("{}/api/v3/community/list", &self.instance)) + .bearer_auth(&self.jwt_token.to_string()) + .query(&list_params) + .send() + .await + { + Ok(data) => match data.text().await { + Ok(data) => data, + Err(e) => { + let err_msg = format!("{e}"); + write_error(err_msg); + return Err(()); + } + }, + Err(e) => { + let err_msg = format!("{e}"); + write_error(err_msg); + return Err(()); + } }; - let json_data: ListCommunitiesResponse = match self.parse_json::<ListCommunitiesResponse>(&response).await { - Some(data) => data, - None => return, + + let json_data: ListCommunitiesResponse = match serde_json::from_str(&response) { + Ok(data) => data, + Err(e) => { + let err_msg = format!("{e}"); + write_error(err_msg); + return Err(()); + } }; let mut communities: HashMap<String, CommunityId> = HashMap::new(); @@ -384,118 +299,6 @@ impl Lemmy { communities.insert(community.name, community.id); } - self.communities = communities; - } - - pub(crate) async fn check_community_for_post(&self, post: CreatePost) -> Option<PostId> { - let get_params: GetPosts = GetPosts { - type_: None, - sort: Some(SortType::New), - page: None, - limit: None, - community_id: Some(post.community_id), - community_name: None, - saved_only: None, - liked_only: None, - disliked_only: None, - show_hidden: None, - show_read: None, - show_nsfw: None, - page_cursor: None, - }; - - let response: String = match self.get_data_query("/api/v3/post/list", &get_params).await { - Some(data) => data, - None => { - error!("Unable to query post list"); - return None - }, - }; - let json_data: GetPostsResponse = match self.parse_json(&response).await { - Some(data) => data, - None => { - error!("Unable to parse post data"); - return None - }, - }; - - for api_post in json_data.posts { - if api_post.post.name == post.name { - return Some(api_post.post.id); - } - } - let msg = format!("Unable to find post {}", post.name); - info!(msg); - None - } - - async fn post_data_json<T: Serialize>(&self, route: &str, json: &T ) -> Option<String> { - let res = HTTP_CLIENT - .post(format!("{}{route}", &self.instance)) - .bearer_auth(self.jwt_token.to_string()) - .json(&json) - .send() - .await; - self.extract_data(res).await - } - - async fn get_data_query<T: Serialize>(&self, route: &str, param: &T ) -> Option<String> { - let res = HTTP_CLIENT - .get(format!("{}{route}", &self.instance)) - .bearer_auth(self.jwt_token.to_string()) - .query(¶m) - .send() - .await; - self.extract_data(res).await - } - - async fn extract_data(&self, response: Result<reqwest::Response, reqwest::Error>) -> Option<String> { - match response { - Ok(data) => { - if data.status().is_success() { - match data.text().await { - Ok(data) => Some(data), - Err(e) => { - let err_msg = format!("{e}"); - error!(err_msg); - None - } - } - } - else { - let err_msg = format!("HTTP Request failed: {}", data.text().await.unwrap()); - error!(err_msg); - None - } - }, - Err(e) => { - let err_msg = format!("{e}"); - error!(err_msg); - None - } - } - } - - async fn parse_json<'a, T: Deserialize<'a>>(&self, response: &'a str) -> Option<T> { - match serde_json::from_str::<T>(response) { - Ok(data) => Some(data), - Err(e) => { - let err_msg = format!("while parsing JSON: {e} "); - error!(err_msg); - None - } - } - } - - async fn parse_json_map<'a, T: Deserialize<'a>>(&self, response: &'a str) -> Option<T> { - debug!(response); - match serde_json::from_str::<HashMap<&str, T>>(response) { - Ok(mut data) => Some(data.remove("post_view").expect("Element should be present")), - Err(e) => { - let err_msg = format!("while parsing JSON HashMap: {e}"); - error!(err_msg); - None - } - } + Ok(communities) } } diff --git a/src/main.rs b/src/main.rs index cc0e9cd..6e077df 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,15 @@ -use chrono::{Duration}; -use log::{LevelFilter}; +use crate::config::Config; +use crate::post_history::SeriesHistory; +use chrono::{DateTime, Duration, Utc}; +use log::{error, info, warn, LevelFilter}; use once_cell::sync::Lazy; use reqwest::Client; -use systemd_journal_logger::{JournalLog}; -use crate::bot::Bot; +use std::collections::HashMap; +use std::fmt::Debug; +use std::sync::Arc; +use systemd_journal_logger::{connected_to_journal, JournalLog}; +use tokio::sync::RwLock; +use tokio::time::sleep; mod bot; mod config; @@ -11,31 +17,88 @@ mod lemmy; mod post_history; mod fetchers; +pub(crate) fn write_error(err_msg: String) { + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => println!("[ERROR] {err_msg}"), + } +} + +pub(crate) fn write_warn(warn_msg: String) { + match connected_to_journal() { + true => warn!("[WARN] {warn_msg}"), + false => println!("[WARN] {warn_msg}"), + } +} + +pub(crate) fn write_info(info_msg: String) { + match connected_to_journal() { + true => info!("[INFO] {info_msg}"), + false => println!("[INFO] {info_msg}"), + } +} + pub static HTTP_CLIENT: Lazy<Client> = Lazy::new(|| { Client::builder() - .timeout(Duration::seconds(10).to_std().unwrap()) - .connect_timeout(Duration::seconds(10).to_std().unwrap()) + .timeout(Duration::seconds(30).to_std().unwrap()) + .connect_timeout(Duration::seconds(30).to_std().unwrap()) .build() .expect("build client") }); +#[derive(Clone, Debug)] +pub(crate) struct SharedData { + config: Config, + post_history: SeriesHistory, + start: DateTime<Utc>, +} + +impl SharedData { + pub(crate) fn new() -> Self { + SharedData { + config: Config::default(), + post_history: SeriesHistory { + series: HashMap::new(), + }, + start: Utc::now(), + } + } +} + #[tokio::main] async fn main() { JournalLog::new() .expect("Systemd-Logger crate error") .install() .expect("Systemd-Logger crate error"); - match std::env::var("LOG_LEVEL") { - Ok(level) => { - match level.as_str() { - "debug" => log::set_max_level(LevelFilter::Debug), - "info" => log::set_max_level(LevelFilter::Info), - _ => log::set_max_level(LevelFilter::Info), + log::set_max_level(LevelFilter::Info); + + let mut data = SharedData::new(); + + loop { + let write_data = Arc::new(RwLock::new(data.clone())); + //let read_data = write_data.clone(); + let persistent_data = write_data.clone(); + + let bot_thread = tokio::spawn(async move { bot::run(write_data).await }); + + let _ = bot_thread.await; + + data = persistent_data.read().await.clone(); + + { + let err_msg = "Bot crashed due to unknown Error, restarting thread after wait..."; + match connected_to_journal() { + true => error!("[ERROR] {err_msg}"), + false => println!("[ERROR] {err_msg}"), } } - _ => log::set_max_level(LevelFilter::Info), - } - let mut bot = Bot::new(); - bot.run().await; + sleep( + Duration::seconds(5) + .to_std() + .expect("Conversion should always work since static"), + ) + .await; + } } diff --git a/src/post_history.rs b/src/post_history.rs index 71499b8..5e3b57e 100644 --- a/src/post_history.rs +++ b/src/post_history.rs @@ -1,24 +1,6 @@ +use crate::write_error; use serde_derive::{Deserialize, Serialize}; use std::collections::HashMap; -use systemd_journal_logger::connected_to_journal; - -macro_rules! info { - ($msg:tt) => { - match connected_to_journal() { - true => log::info!("[INFO] {}", $msg), - false => println!("[INFO] {}", $msg), - } - }; -} - -macro_rules! error { - ($msg:tt) => { - match connected_to_journal() { - true => log::error!("[ERROR] {}", $msg), - false => eprintln!("[ERROR] {}", $msg), - } - }; -} #[derive(Serialize, Deserialize, Default, Clone, Debug)] pub(crate) struct SeriesHistory { @@ -27,8 +9,6 @@ pub(crate) struct SeriesHistory { impl SeriesHistory { pub(crate) fn load_history() -> Self { - let info_msg = "Loading History"; - info!(info_msg); match confy::load(env!("CARGO_PKG_NAME"), "history") { Ok(data) => data, Err(e) => panic!("history.toml not found: {e}"), @@ -36,11 +16,9 @@ impl SeriesHistory { } pub(crate) fn save_history(&self) { - let info_msg = "Saving History"; - info!(info_msg); if let Err(e) = confy::store(env!("CARGO_PKG_NAME"), "history", self) { let err_msg = format!("Unexpected error saving to history.toml: {e}"); - error!(err_msg); + write_error(err_msg); } }