diff --git a/.forgejo/workflows/build+release.yml b/.forgejo/workflows/build+release.yml
index 6b63dad..a21f697 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@v1
+                uses: actions/forgejo-release@v2
                 with:
                     direction: upload
                     url: https://forgejo.neshweb.net
diff --git a/Cargo.lock b/Cargo.lock
index aa6f6ca..734bb74 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 = 3
+version = 4
 
 [[package]]
 name = "addr2line"
@@ -42,9 +42,19 @@ dependencies = [
 ]
 
 [[package]]
-name = "aob-lemmy-bot"
-version = "3.0.0-rc.1"
+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.3.1"
+dependencies = [
+ "async-trait",
  "chrono",
  "confy",
  "lemmy_api_common",
@@ -56,7 +66,7 @@ dependencies = [
  "serde",
  "serde_derive",
  "serde_json",
- "strum_macros 0.26.2",
+ "strum_macros",
  "systemd-journal-logger",
  "tokio",
  "toml",
@@ -64,10 +74,21 @@ dependencies = [
 ]
 
 [[package]]
-name = "async-trait"
-version = "0.1.77"
+name = "async-lock"
+version = "3.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
+checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
+dependencies = [
+ "event-listener",
+ "event-listener-strategy",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -95,12 +116,6 @@ dependencies = [
  "rustc-demangle",
 ]
 
-[[package]]
-name = "base64"
-version = "0.21.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
-
 [[package]]
 name = "base64"
 version = "0.22.1"
@@ -125,6 +140,12 @@ 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"
@@ -140,6 +161,17 @@ 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"
@@ -148,9 +180,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "chrono"
-version = "0.4.31"
+version = "0.4.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
+checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
 dependencies = [
  "android-tzdata",
  "iana-time-zone",
@@ -158,7 +190,30 @@ dependencies = [
  "num-traits",
  "serde",
  "wasm-bindgen",
- "windows-targets 0.48.5",
+ "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",
 ]
 
 [[package]]
@@ -173,6 +228,12 @@ dependencies = [
  "toml",
 ]
 
+[[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"
@@ -198,6 +259,15 @@ 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"
@@ -249,6 +319,17 @@ 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"
@@ -270,6 +351,17 @@ dependencies = [
  "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",
+]
+
 [[package]]
 name = "encoding_rs"
 version = "0.8.33"
@@ -315,6 +407,27 @@ 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"
@@ -329,7 +442,7 @@ checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
 dependencies = [
  "cfg-if",
  "libc",
- "redox_syscall",
+ "redox_syscall 0.4.1",
  "windows-sys 0.52.0",
 ]
 
@@ -384,21 +497,21 @@ dependencies = [
 
 [[package]]
 name = "futures-core"
-version = "0.3.29"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
 
 [[package]]
 name = "futures-io"
-version = "0.3.29"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa"
+checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
 
 [[package]]
 name = "futures-macro"
-version = "0.3.29"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb"
+checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -407,21 +520,21 @@ dependencies = [
 
 [[package]]
 name = "futures-sink"
-version = "0.3.29"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
+checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
 
 [[package]]
 name = "futures-task"
-version = "0.3.29"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
 
 [[package]]
 name = "futures-util"
-version = "0.3.29"
+version = "0.3.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
 dependencies = [
  "futures-core",
  "futures-io",
@@ -436,9 +549,9 @@ dependencies = [
 
 [[package]]
 name = "getrandom"
-version = "0.2.11"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
+checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
 dependencies = [
  "cfg-if",
  "js-sys",
@@ -486,9 +599,9 @@ checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
 
 [[package]]
 name = "heck"
-version = "0.4.1"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
 
 [[package]]
 name = "hermit-abi"
@@ -621,6 +734,124 @@ 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"
@@ -629,12 +860,23 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
 
 [[package]]
 name = "idna"
-version = "0.5.0"
+version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
 dependencies = [
- "unicode-bidi",
- "unicode-normalization",
+ "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",
 ]
 
 [[package]]
@@ -659,6 +901,15 @@ 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"
@@ -728,36 +979,44 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "lemmy_api_common"
-version = "0.19.3"
+version = "0.19.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17366fcde90b07f4e5a8fefa62378fe348424ba79c8e9fb3ddc63ef76d687493"
+checksum = "06a7554d0a71b37c1f666125918a72fe92103a5f254668a988aaa31ed80d4b7b"
 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.3"
+version = "0.19.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e2d9a0c6f8f3df4664f9479ceca138ee6217b89653bafb753a498cabc5f3ab5"
+checksum = "5454e0df45ec4831b7c6502d965cb9865ecbe2cb181a20c616ad8235444c06e8"
 dependencies = [
+ "anyhow",
  "async-trait",
  "chrono",
+ "derive-new",
  "futures-util",
+ "moka",
  "serde",
  "serde_with",
  "strum",
- "strum_macros 0.25.3",
  "tracing",
  "typed-builder",
  "url",
@@ -766,10 +1025,11 @@ dependencies = [
 
 [[package]]
 name = "lemmy_db_views"
-version = "0.19.3"
+version = "0.19.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02659eb474ab54da6296d4edb5a974c0affa5cfcf8a2fa31bb52793dedef9d5a"
+checksum = "c4d348095887630617ef02b823887d422ae2e93dfff7a13a127e7524eec51d99"
 dependencies = [
+ "chrono",
  "lemmy_db_schema",
  "serde",
  "serde_with",
@@ -777,23 +1037,22 @@ dependencies = [
 
 [[package]]
 name = "lemmy_db_views_actor"
-version = "0.19.3"
+version = "0.19.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5187730858dc808b5cf06aadbb1d1f8ca360d4ed3c375e5606e63be4c9e24e06"
+checksum = "cd7a42bf16a72d6f313f59116538e1333e6ce5f2efbeb9b834ddb147b2cdf986"
 dependencies = [
  "chrono",
  "lemmy_db_schema",
  "serde",
  "serde_with",
  "strum",
- "strum_macros 0.25.3",
 ]
 
 [[package]]
 name = "lemmy_db_views_moderator"
-version = "0.19.3"
+version = "0.19.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18f6fab36e1dcadc043b81c5916044ee65219d837f5d221a908296f9c55f3872"
+checksum = "4b6a1a841c4ccac1947f7edfbb52b3113d32f218a2eede769ec5dce4a039cd43"
 dependencies = [
  "lemmy_db_schema",
  "serde",
@@ -801,10 +1060,23 @@ dependencies = [
 ]
 
 [[package]]
-name = "libc"
-version = "0.2.151"
+name = "lemmy_utils"
+version = "0.19.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
+checksum = "0ee28dc456fac8d5c7ae67b4f596a1e9e2a0a9a6e1c4f8a024bdfb34cf25dfa5"
+dependencies = [
+ "cfg-if",
+ "clearurls",
+ "rosetta-build",
+ "serde",
+ "strum",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.155"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
 
 [[package]]
 name = "libredox"
@@ -814,7 +1086,16 @@ checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
 dependencies = [
  "bitflags 2.4.1",
  "libc",
- "redox_syscall",
+ "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",
 ]
 
 [[package]]
@@ -823,6 +1104,22 @@ 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"
@@ -844,6 +1141,16 @@ 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"
@@ -865,6 +1172,30 @@ dependencies = [
  "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"
@@ -902,6 +1233,12 @@ dependencies = [
  "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"
@@ -986,6 +1323,35 @@ 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"
@@ -1045,6 +1411,21 @@ 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"
@@ -1054,6 +1435,15 @@ 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"
@@ -1063,6 +1453,15 @@ 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"
@@ -1076,9 +1475,9 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.10.2"
+version = "1.10.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
+checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -1088,9 +1487,9 @@ dependencies = [
 
 [[package]]
 name = "regex-automata"
-version = "0.4.3"
+version = "0.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -1109,7 +1508,7 @@ version = "0.12.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10"
 dependencies = [
- "base64 0.22.1",
+ "base64",
  "bytes",
  "encoding_rs",
  "futures-channel",
@@ -1146,12 +1545,35 @@ 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"
@@ -1171,7 +1593,7 @@ version = "2.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d"
 dependencies = [
- "base64 0.22.1",
+ "base64",
  "rustls-pki-types",
 ]
 
@@ -1211,6 +1633,12 @@ 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"
@@ -1235,19 +1663,25 @@ dependencies = [
 ]
 
 [[package]]
-name = "serde"
-version = "1.0.193"
+name = "semver"
+version = "1.0.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
+checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
+
+[[package]]
+name = "serde"
+version = "1.0.204"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.193"
+version = "1.0.204"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
+checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1256,11 +1690,12 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.108"
+version = "1.0.140"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
+checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
 dependencies = [
  "itoa",
+ "memchr",
  "ryu",
  "serde",
 ]
@@ -1288,16 +1723,17 @@ dependencies = [
 
 [[package]]
 name = "serde_with"
-version = "3.4.0"
+version = "3.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23"
+checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857"
 dependencies = [
- "base64 0.21.5",
+ "base64",
  "chrono",
  "hex",
  "indexmap 1.9.3",
  "indexmap 2.1.0",
  "serde",
+ "serde_derive",
  "serde_json",
  "serde_with_macros",
  "time",
@@ -1305,9 +1741,9 @@ dependencies = [
 
 [[package]]
 name = "serde_with_macros"
-version = "3.4.0"
+version = "3.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788"
+checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350"
 dependencies = [
  "darling",
  "proc-macro2",
@@ -1340,6 +1776,12 @@ 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"
@@ -1348,28 +1790,18 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
 
 [[package]]
 name = "strum"
-version = "0.25.0"
+version = "0.26.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
-
-[[package]]
-name = "strum_macros"
-version = "0.25.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
+checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
 dependencies = [
- "heck",
- "proc-macro2",
- "quote",
- "rustversion",
- "syn",
+ "strum_macros",
 ]
 
 [[package]]
 name = "strum_macros"
-version = "0.26.2"
+version = "0.26.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
+checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
 dependencies = [
  "heck",
  "proc-macro2",
@@ -1395,6 +1827,17 @@ 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"
@@ -1426,6 +1869,12 @@ 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"
@@ -1434,7 +1883,7 @@ checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
 dependencies = [
  "cfg-if",
  "fastrand",
- "redox_syscall",
+ "redox_syscall 0.4.1",
  "rustix",
  "windows-sys 0.48.0",
 ]
@@ -1461,12 +1910,13 @@ dependencies = [
 
 [[package]]
 name = "time"
-version = "0.3.30"
+version = "0.3.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5"
+checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
 dependencies = [
  "deranged",
  "itoa",
+ "num-conv",
  "powerfmt",
  "serde",
  "time-core",
@@ -1481,27 +1931,29 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
 
 [[package]]
 name = "time-macros"
-version = "0.2.15"
+version = "0.2.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20"
+checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
 dependencies = [
+ "num-conv",
  "time-core",
 ]
 
 [[package]]
-name = "tinyvec"
-version = "1.6.0"
+name = "tinyjson"
+version = "2.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
-dependencies = [
- "tinyvec_macros",
-]
+checksum = "9ab95735ea2c8fd51154d01e39cf13912a78071c2d89abc49a7ef102a7dd725a"
 
 [[package]]
-name = "tinyvec_macros"
-version = "0.1.1"
+name = "tinystr"
+version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
 
 [[package]]
 name = "tokio"
@@ -1649,6 +2101,12 @@ 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"
@@ -1657,18 +2115,18 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
 
 [[package]]
 name = "typed-builder"
-version = "0.18.0"
+version = "0.19.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e47c0496149861b7c95198088cbf36645016b1a0734cf350c50e2a38e070f38a"
+checksum = "a06fbd5b8de54c5f7c91f6fe4cebb949be2125d7758e630bb58b1d831dbce600"
 dependencies = [
  "typed-builder-macro",
 ]
 
 [[package]]
 name = "typed-builder-macro"
-version = "0.18.0"
+version = "0.19.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "982ee4197351b5c9782847ef5ec1fdcaf50503fb19d68f9771adae314e72b492"
+checksum = "f9534daa9fd3ed0bd911d462a37f172228077e7abf18c18a5f67199d959205f8"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1676,10 +2134,10 @@ dependencies = [
 ]
 
 [[package]]
-name = "unicode-bidi"
-version = "0.3.14"
+name = "unicase"
+version = "2.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416"
+checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
 
 [[package]]
 name = "unicode-ident"
@@ -1687,20 +2145,11 @@ version = "1.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 
-[[package]]
-name = "unicode-normalization"
-version = "0.1.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
-dependencies = [
- "tinyvec",
-]
-
 [[package]]
 name = "url"
-version = "2.5.0"
+version = "2.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
+checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
 dependencies = [
  "form_urlencoded",
  "idna",
@@ -1709,10 +2158,28 @@ dependencies = [
 ]
 
 [[package]]
-name = "uuid"
-version = "1.6.1"
+name = "urlencoding"
+version = "2.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560"
+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"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
 dependencies = [
  "getrandom",
  "serde",
@@ -1831,6 +2298,22 @@ dependencies = [
  "wasm-bindgen",
 ]
 
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
 [[package]]
 name = "winapi-util"
 version = "0.1.8"
@@ -1840,6 +2323,12 @@ dependencies = [
  "windows-sys 0.52.0",
 ]
 
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
 [[package]]
 name = "windows-core"
 version = "0.51.1"
@@ -1999,3 +2488,82 @@ 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 1599c33..e97bea1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 authors = ["Neshura"]
 name = "aob-lemmy-bot"
-version = "3.0.0-rc.1"
+version = "3.3.1"
 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"
@@ -17,8 +17,8 @@ systemd-units = { enable = false }
 
 [dependencies]
 chrono = "^0.4"
-lemmy_api_common = "0.19.3"
-lemmy_db_schema = "0.19.3"
+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"
@@ -31,4 +31,5 @@ confy = "^0.6"
 toml = "^0.8"
 systemd-journal-logger = "^2.1.1"
 log = "^0.4"
+async-trait = "^0.1"
 notify = "6.1.1"
\ No newline at end of file
diff --git a/src/bot.rs b/src/bot.rs
index 4539351..3e0bde8 100644
--- a/src/bot.rs
+++ b/src/bot.rs
@@ -1,4 +1,4 @@
-use crate::{config::{Config}};
+use crate::{config::{Config}, HTTP_CLIENT};
 use crate::lemmy::{Lemmy};
 use crate::post_history::{SeriesHistory};
 use chrono::{DateTime, Duration, Utc};
@@ -7,6 +7,15 @@ use notify::{Event, EventKind, event::{AccessKind, AccessMode}, RecursiveMode, W
 use tokio::time::sleep;
 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() {
@@ -72,10 +81,15 @@ impl Bot {
         loop {
             let mut lemmy = match Lemmy::new(&self.shared_config).await {
                 Ok(data) => data,
-                Err(_) => continue,
+                Err(_) => {
+                    sleep(Duration::seconds(10).to_std().unwrap()).await;
+                    continue;
+                },
             };
 
             lemmy.get_communities().await;
+            
+            self.history = SeriesHistory::load_history();
 
             let start: DateTime<Utc> = Utc::now();
             while Utc::now() - start <= Duration::minutes(60) {
@@ -84,10 +98,14 @@ impl Bot {
                 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;
             }
 
@@ -98,7 +116,7 @@ impl Bot {
     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 reqwest::get(status_url).await {
+            match HTTP_CLIENT.get(status_url).send().await {
                 Ok(_) => {},
                 Err(e) => {
                     let err_msg = format!("While pinging status URL: {e}");
diff --git a/src/config.rs b/src/config.rs
index 0bee169..d392db0 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -2,8 +2,8 @@ use std::path::PathBuf;
 use std::sync::{Arc, RwLock};
 use chrono::{Timelike, Utc};
 use crate::config::PostBody::Description;
-use lemmy_api_common::sensitive::Sensitive;
 use lemmy_db_schema::PostFeatureType;
+use lemmy_db_schema::sensitive::SensitiveString;
 use serde_derive::{Deserialize, Serialize};
 use crate::lemmy::{Lemmy, PartInfo, PostType};
 use crate::post_history::{SeriesHistory};
@@ -11,6 +11,15 @@ 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() {
@@ -41,8 +50,8 @@ macro_rules! error {
 #[derive(Serialize, Deserialize, Clone, Debug)]
 pub(crate) struct Config {
     pub(crate) instance: String,
-    username: String,
-    password: String,
+    username: SensitiveString,
+    password: SensitiveString,
     pub(crate) status_post_url: Option<String>,
     pub(crate) config_reload_seconds: u32,
     pub(crate) protected_communities: Vec<String>,
@@ -80,12 +89,12 @@ impl Config {
         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) -> Sensitive<String> {
-        Sensitive::new(self.password.clone())
+    pub(crate) fn get_password(&self) -> SensitiveString {
+        self.password.clone()
     }
 }
 
@@ -93,8 +102,8 @@ impl Default for Config {
     fn default() -> Self {
         Config {
             instance: "".to_owned(),
-            username: "".to_owned(),
-            password: "".to_owned(),
+            username: SensitiveString::from("".to_owned()),
+            password: SensitiveString::from("".to_owned()),
             status_post_url: None,
             config_reload_seconds: 21600,
             protected_communities: vec![],
@@ -167,11 +176,17 @@ impl SeriesConfig {
             );
             info!(info);
 
-            let post_id = match lemmy.post(post_data).await {
-                Ok(data) => data,
-                Err(_) => {
-                    error!("Error posting chapter");
-                    return;
+            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;
+                        }
+                    }
                 }
             };
 
@@ -188,31 +203,18 @@ impl SeriesConfig {
                     post_info.get_post_config(self).name.as_str()
                 );
                 info!(info);
-                let pinned_posts = match lemmy.get_community_pinned(lemmy.get_community_id(&post_info.get_post_config(self).name)).await {
-                    Ok(data) => data,
-                    Err(_) => {
-                        error!("Pinning of Post to community failed");
-                        continue;
-                    }
-                };
+                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];
-                    match lemmy
-                        .unpin(community_pinned_post.post.id, PostFeatureType::Community)
-                        .await {
-                        Ok(_) => {}
-                        Err(_) => {
-                            error!("Error un-pinning post");
-                            return;
-                        }
+                    if lemmy.unpin(community_pinned_post.post.id, PostFeatureType::Community).await.is_none() {
+                        error!("Error un-pinning post");
                     }
                 }
-                match lemmy.pin(post_id, PostFeatureType::Community).await {
-                    Ok(_) => {}
-                    Err(_) => {
-                        error!("Error pinning post");
-                        return;
-                    }
+                if lemmy.pin(post_id, PostFeatureType::Community).await.is_none() {
+                    error!("Error pinning post");
                 }
             } else if read_config
                 .protected_communities
@@ -229,10 +231,10 @@ impl SeriesConfig {
                 let info = format!("Pinning '{}' to Instance", post_info.get_info().title);
                 info!(info);
                 let pinned_posts = match lemmy.get_local_pinned().await {
-                    Ok(data) => {data}
-                    Err(_) => {
+                    Some(data) => {data}
+                    None => {
                         error!("Error fetching pinned posts");
-                        return;
+                        vec![]
                     }
                 };
 
@@ -245,25 +247,16 @@ impl SeriesConfig {
                             continue;
                         } else {
                             let community_pinned_post = &pinned_post;
-                            match lemmy
-                                .unpin(community_pinned_post.post.id, PostFeatureType::Local)
-                                .await {
-                                Ok(_) => {}
-                                Err(_) => {
-                                    error!("Error pinning post");
-                                    return;
-                                }
+                            if lemmy.unpin(community_pinned_post.post.id, PostFeatureType::Local).await.is_none() {
+                                error!("Error pinning post");
+                                continue;
                             }
                             break;
                         }
                     }
                 }
-                match lemmy.pin(post_id, PostFeatureType::Local).await {
-                    Ok(_) => {}
-                    Err(_) => {
-                        error!("Error pinning post");
-                        return;
-                    }
+                if lemmy.pin(post_id, PostFeatureType::Local).await.is_none() {
+                    error!("Error pinning post");
                 };
             }
 
@@ -283,6 +276,7 @@ impl SeriesConfig {
             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();
         }
     }
diff --git a/src/fetchers/jnovel.rs b/src/fetchers/jnovel.rs
index 7ce0a6a..c5a0a42 100644
--- a/src/fetchers/jnovel.rs
+++ b/src/fetchers/jnovel.rs
@@ -3,7 +3,7 @@ use chrono::{DateTime, Duration, Utc};
 use serde_derive::{Deserialize, Serialize};
 use std::collections::HashMap;
 use std::ops::Sub;
-use url::Url;
+use async_trait::async_trait;
 use crate::fetchers::{FetcherTrait};
 use crate::lemmy::{PartInfo, PostInfo, PostInfoInner, PostType};
 use systemd_journal_logger::connected_to_journal;
@@ -31,7 +31,7 @@ static PAST_DAYS_ELIGIBLE: u8 = 4;
 
 macro_rules! api_url {
     () => {
-        "https://labs.j-novel.club/app/v1".to_owned()
+        "https://labs.j-novel.club/app/v2".to_owned()
     };
 }
 
@@ -94,6 +94,15 @@ pub(crate) struct JNovelFetcher {
     series_has_parts: bool
 }
 
+impl Default for JNovelFetcher {
+    fn default() -> Self {
+        Self {
+            series_slug: "".to_owned(),
+            series_has_parts: false,
+        }
+    }
+}
+
 impl JNovelFetcher {
     pub(crate) fn set_series(&mut self, series: String) {
         self.series_slug = series;
@@ -104,6 +113,7 @@ impl JNovelFetcher {
     }
 }
 
+#[async_trait]
 impl FetcherTrait for JNovelFetcher {
     fn new() -> Self {
         JNovelFetcher {
@@ -190,7 +200,8 @@ impl FetcherTrait for JNovelFetcher {
             );
             let post_details = PostInfoInner {
                 title: volume.title.clone(),
-                url: Url::parse(&post_url).unwrap(),
+                url: post_url.clone(),
+                thumbnail: Some(volume.cover.thumbnail.clone())
             };
 
             let new_post_info = PostInfo {
@@ -213,7 +224,7 @@ impl FetcherTrait for JNovelFetcher {
                     .or_insert(new_post_info);
             }
 
-            if let Some(prepub_info) = get_latest_prepub(&volume.slug).await? {
+            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),
@@ -241,7 +252,7 @@ impl FetcherTrait for JNovelFetcher {
 }
 
 
-async fn get_latest_prepub(volume_slug: &str) -> Result<Option<PostInfoInner>, ()> {
+async fn get_latest_prepub(volume_slug: &str) -> Option<PostInfoInner> {
     let response = match HTTP_CLIENT
         .get(api_url!() + "/volumes/" + volume_slug + "/parts?format=json")
         .send()
@@ -252,13 +263,13 @@ async fn get_latest_prepub(volume_slug: &str) -> Result<Option<PostInfoInner>, (
             Err(e) => {
                 let err_msg = format!("While getting latest PrePub: {e}");
                 error!(err_msg);
-                return Err(());
+                return None;
             }
         },
         Err(e) => {
             let err_msg = format!("{e}");
             error!(err_msg);
-            return Err(());
+            return None;
         }
     };
 
@@ -267,7 +278,7 @@ async fn get_latest_prepub(volume_slug: &str) -> Result<Option<PostInfoInner>, (
         Err(e) => {
             let err_msg = format!("{e}");
             error!(err_msg);
-            return Err(());
+            return None;
         }
     };
     volume_prepub_parts_data.parts.reverse(); // Makes breaking out of the parts loop easier
@@ -282,12 +293,15 @@ async fn get_latest_prepub(volume_slug: &str) -> Result<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 {
             title: prepub_part.title.clone(),
-            url: Url::parse(&post_url).unwrap(),
+            url: post_url.clone(),
+            thumbnail
         });
     }
 
-    Ok(post_details)
+    post_details
 }
diff --git a/src/fetchers/mod.rs b/src/fetchers/mod.rs
index 990d7c1..0f2eacc 100644
--- a/src/fetchers/mod.rs
+++ b/src/fetchers/mod.rs
@@ -1,3 +1,4 @@
+use async_trait::async_trait;
 use serde_derive::{Deserialize, Serialize};
 use strum_macros::Display;
 use crate::fetchers::Fetcher::Jnc;
@@ -6,6 +7,7 @@ use crate::lemmy::{PostInfo};
 
 pub mod jnovel;
 
+#[async_trait]
 pub(crate) trait FetcherTrait {
     fn new() -> Self where Self: Sized;
     async fn check_feed(&self) -> Result<Vec<PostInfo>, ()>;
@@ -27,5 +29,5 @@ impl Fetcher {
 #[derive(Deserialize, Serialize, Debug, Clone, Display)]
 pub(crate) enum Fetcher {
     #[serde(rename = "jnc")]
-    Jnc(JNovelFetcher)
+    Jnc(#[serde(skip)] JNovelFetcher)
 }
diff --git a/src/lemmy.rs b/src/lemmy.rs
index 29b2408..9dc45ec 100644
--- a/src/lemmy.rs
+++ b/src/lemmy.rs
@@ -5,16 +5,33 @@ 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_api_common::sensitive::Sensitive;
 use lemmy_db_schema::newtypes::{CommunityId, LanguageId, PostId};
-use lemmy_db_schema::{ListingType, PostFeatureType};
+use lemmy_db_schema::{ListingType, PostFeatureType, SortType};
 use reqwest::StatusCode;
 use std::collections::HashMap;
 use std::sync::{RwLock};
+use lemmy_db_schema::sensitive::SensitiveString;
 use serde::{Deserialize, Serialize};
-use url::Url;
 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() {
@@ -25,7 +42,7 @@ macro_rules! error {
 }
 
 pub(crate) struct Lemmy {
-    jwt_token: Sensitive<String>,
+    jwt_token: SensitiveString,
     instance: String,
     communities: HashMap<String, CommunityId>,
 }
@@ -34,7 +51,8 @@ pub(crate) struct Lemmy {
 #[derive(Debug, Clone)]
 pub(crate) struct PostInfoInner {
     pub(crate) title: String,
-    pub(crate) url: Url,
+    pub(crate) url: String,
+    pub(crate) thumbnail: Option<String>
 }
 
 #[derive(Debug, Copy, Clone)]
@@ -124,7 +142,7 @@ impl PostInfo {
     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) => {
@@ -136,10 +154,10 @@ impl PostInfo {
                 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(),
@@ -152,11 +170,13 @@ impl PostInfo {
             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
-        }   
+        }
     }
 }
 
@@ -207,7 +227,7 @@ impl Lemmy {
         };
 
         let response = match HTTP_CLIENT
-            .post(read_config.instance.to_owned() + "/api/v3/user/login")
+            .post(read_config.instance.to_owned() + "/api/alpha/user/login")
             .json(&login_params)
             .send()
             .await
@@ -248,25 +268,37 @@ impl Lemmy {
     }
 
     pub(crate) async fn logout(&self) {
-        let _ = self.post_data_json("/api/v3/user/logout", &"").await;
+        let _ = self.post_data_json("/api/alpha/user/logout", &"").await;
     }
 
 
-    pub(crate) async fn post(&self, post: CreatePost) -> Result<PostId, ()> {
-        let response: String = self.post_data_json("/api/v3/post", &post).await?;
-        let json_data: PostView = self.parse_json_map(&response).await?;
+    pub(crate) async fn post(&self, post: CreatePost) -> Option<PostId> {
+        let response: String = match self.post_data_json("/api/alpha/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,
+        };
 
-        Ok(json_data.post.id)
+        Some(json_data.post.id)
     }
 
-    async fn feature(&self, params: FeaturePost) -> Result<PostView, ()> {
-        let response: String = self.post_data_json("/api/v3/post/feature", &params).await?;
-        let json_data: PostView = self.parse_json_map(&response).await?;
+    async fn feature(&self, params: FeaturePost) -> Option<PostView> {
+        let response: String = match self.post_data_json("/api/alpha/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,
+        };
 
-        Ok(json_data)
+        Some(json_data)
     }
 
-    pub(crate) async fn unpin(&self, post_id: PostId, location: PostFeatureType) -> Result<PostView, ()> {
+    pub(crate) async fn unpin(&self, post_id: PostId, location: PostFeatureType) -> Option<PostView> {
         let pin_params = FeaturePost {
             post_id,
             featured: false,
@@ -275,7 +307,7 @@ impl Lemmy {
         self.feature(pin_params).await
     }
 
-    pub(crate) async fn pin(&self, post_id: PostId, location: PostFeatureType) -> Result<PostView, ()> {
+    pub(crate) async fn pin(&self, post_id: PostId, location: PostFeatureType) -> Option<PostView> {
         let pin_params = FeaturePost {
             post_id,
             featured: true,
@@ -284,17 +316,23 @@ impl Lemmy {
         self.feature(pin_params).await
     }
 
-    pub(crate) async fn get_community_pinned(&self, community: CommunityId) -> Result<Vec<PostView>, ()> {
+    pub(crate) async fn get_community_pinned(&self, community: CommunityId) -> Option<Vec<PostView>> {
         let list_params = GetPosts {
             community_id: Some(community),
             type_: Some(ListingType::Local),
             ..Default::default()
         };
 
-        let response: String = self.get_data_query("/api/v3/post/list", &list_params).await?;
-        let json_data: GetPostsResponse = self.parse_json(&response).await?;
+        let response: String = match self.get_data_query("/api/alpha/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,
+        };
 
-        Ok(json_data
+        Some(json_data
             .posts
             .iter()
             .filter(|post| post.post.featured_community)
@@ -302,16 +340,22 @@ impl Lemmy {
             .collect())
     }
 
-    pub(crate) async fn get_local_pinned(&self) -> Result<Vec<PostView>, ()> {
+    pub(crate) async fn get_local_pinned(&self) -> Option<Vec<PostView>> {
         let list_params = GetPosts {
             type_: Some(ListingType::Local),
             ..Default::default()
         };
 
-        let response: String = self.get_data_query("/api/v3/post/list", &list_params).await?;
-        let json_data: GetPostsResponse = self.parse_json(&response).await?;
+        let response: String = match self.get_data_query("/api/alpha/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,
+        };
 
-        Ok(json_data
+        Some(json_data
             .posts
             .iter()
             .filter(|post| post.post.featured_local)
@@ -325,19 +369,13 @@ impl Lemmy {
             ..Default::default()
         };
 
-        let response: String = match self.get_data_query("/api/v3/community/list", &list_params).await {
-            Ok(data) => data,
-            Err(_) => {
-                error!("Unable to extract data from request");
-                return;
-            }
+        let response: String = match self.get_data_query("/api/alpha/community/list", &list_params).await {
+            Some(data) => data,
+            None => return,
         };
         let json_data: ListCommunitiesResponse = match self.parse_json::<ListCommunitiesResponse>(&response).await {
-            Ok(data) => data,
-            Err(_) => {
-                error!("Unable to parse data from json");
-                return;
-            },
+            Some(data) => data,
+            None => return,
         };
 
         let mut communities: HashMap<String, CommunityId> = HashMap::new();
@@ -349,62 +387,114 @@ impl Lemmy {
         self.communities = communities;
     }
 
-    async fn post_data_json<T: Serialize>(&self, route: &str, json: &T ) -> Result<String,()> {
+    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/alpha/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())
+            .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 ) -> Result<String,()> {
+    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())
+            .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>) -> Result<String,()> {
+    async fn extract_data(&self, response: Result<reqwest::Response, reqwest::Error>) -> Option<String> {
         match response {
-            Ok(data) => match data.text().await {
-                Ok(data) => Ok(data),
-                Err(e) => {
-                    let err_msg = format!("{e}");
+            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);
-                    Err(())
+                    None
                 }
             },
             Err(e) => {
                 let err_msg = format!("{e}");
                 error!(err_msg);
-                Err(())
+                None
             }
         }
     }
     
-    async fn parse_json<'a, T: Deserialize<'a>>(&self, response: &'a str) -> Result<T,()> {
+    async fn parse_json<'a, T: Deserialize<'a>>(&self, response: &'a str) -> Option<T> {
         match serde_json::from_str::<T>(response) {
-            Ok(data) => Ok(data),
+            Ok(data) => Some(data),
             Err(e) => {
-                let err_msg = format!("{e} while parsing JSON");
+                let err_msg = format!("while parsing JSON: {e} ");
                 error!(err_msg);
-                Err(())
+                None
             }
         }
     }
 
-    async fn parse_json_map<'a, T: Deserialize<'a>>(&self, response: &'a str) -> Result<T,()> {
+    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) => Ok(data.remove("post_view").expect("Element should be present")),
+            Ok(mut data) => Some(data.remove("post_view").expect("Element should be present")),
             Err(e) => {
-                let err_msg = format!("{e} while parsing JSON HashMap");
+                let err_msg = format!("while parsing JSON HashMap: {e}");
                 error!(err_msg);
-                Err(())
+                None
             }
         }
     }
diff --git a/src/main.rs b/src/main.rs
index 5eae9ae..cc0e9cd 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -25,7 +25,17 @@ async fn main() {
         .expect("Systemd-Logger crate error")
         .install()
         .expect("Systemd-Logger crate error");
-    log::set_max_level(LevelFilter::Info);
+    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 bot = Bot::new();
     bot.run().await;
 }