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(&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(());
             }
-            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", &params).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(&param)
-            .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);
         }
     }