This commit is contained in:
암냥 2026-01-09 09:52:22 +09:00
commit d564aaec8c
No known key found for this signature in database
4 changed files with 109 additions and 442 deletions

450
Cargo.lock generated
View file

@ -8,6 +8,15 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "aho-corasick"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "android_system_properties" name = "android_system_properties"
version = "0.1.5" version = "0.1.5"
@ -43,19 +52,6 @@ dependencies = [
"syn 2.0.114", "syn 2.0.114",
] ]
[[package]]
name = "atom_syndication"
version = "0.12.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2f68d23e2cb4fd958c705b91a6b4c80ceeaf27a9e11651272a8389d5ce1a4a3"
dependencies = [
"chrono",
"derive_builder",
"diligent-date-parser",
"never",
"quick-xml",
]
[[package]] [[package]]
name = "atomic-waker" name = "atomic-waker"
version = "1.1.2" version = "1.1.2"
@ -144,6 +140,7 @@ dependencies = [
"iana-time-zone", "iana-time-zone",
"js-sys", "js-sys",
"num-traits", "num-traits",
"serde",
"wasm-bindgen", "wasm-bindgen",
"windows-link", "windows-link",
] ]
@ -159,16 +156,6 @@ dependencies = [
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "core-foundation"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "core-foundation-sys" name = "core-foundation-sys"
version = "0.8.7" version = "0.8.7"
@ -218,41 +205,6 @@ dependencies = [
"typenum", "typenum",
] ]
[[package]]
name = "darling"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 2.0.114",
]
[[package]]
name = "darling_macro"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core",
"quote",
"syn 2.0.114",
]
[[package]] [[package]]
name = "data-encoding" name = "data-encoding"
version = "2.9.0" version = "2.9.0"
@ -269,37 +221,6 @@ dependencies = [
"serde_core", "serde_core",
] ]
[[package]]
name = "derive_builder"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
dependencies = [
"derive_builder_macro",
]
[[package]]
name = "derive_builder_core"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "derive_builder_macro"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
dependencies = [
"derive_builder_core",
"syn 2.0.114",
]
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.10.7" version = "0.10.7"
@ -310,15 +231,6 @@ dependencies = [
"crypto-common", "crypto-common",
] ]
[[package]]
name = "diligent-date-parser"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8ede7d79366f419921e2e2f67889c12125726692a313bffb474bd5f37a581e9"
dependencies = [
"chrono",
]
[[package]] [[package]]
name = "displaydoc" name = "displaydoc"
version = "0.2.5" version = "0.2.5"
@ -362,10 +274,21 @@ dependencies = [
] ]
[[package]] [[package]]
name = "fastrand" name = "feed-rs"
version = "2.3.0" version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" checksum = "e4c0591d23efd0d595099af69a31863ac1823046b1b021e3b06ba3aae7e00991"
dependencies = [
"chrono",
"mediatype",
"quick-xml",
"regex",
"serde",
"serde_json",
"siphasher",
"url",
"uuid",
]
[[package]] [[package]]
name = "find-msvc-tools" name = "find-msvc-tools"
@ -383,27 +306,6 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]] [[package]]
name = "form_urlencoded" name = "form_urlencoded"
version = "1.2.2" version = "1.2.2"
@ -556,25 +458,6 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "h2"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54"
dependencies = [
"atomic-waker",
"bytes",
"fnv",
"futures-core",
"futures-sink",
"http",
"indexmap",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.16.1" version = "0.16.1"
@ -654,7 +537,6 @@ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"h2",
"http", "http",
"http-body", "http-body",
"httparse", "httparse",
@ -683,22 +565,6 @@ dependencies = [
"webpki-roots 1.0.5", "webpki-roots 1.0.5",
] ]
[[package]]
name = "hyper-tls"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
dependencies = [
"bytes",
"http-body-util",
"hyper",
"hyper-util",
"native-tls",
"tokio",
"tokio-native-tls",
"tower-service",
]
[[package]] [[package]]
name = "hyper-util" name = "hyper-util"
version = "0.1.19" version = "0.1.19"
@ -718,11 +584,9 @@ dependencies = [
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"socket2", "socket2",
"system-configuration",
"tokio", "tokio",
"tower-service", "tower-service",
"tracing", "tracing",
"windows-registry",
] ]
[[package]] [[package]]
@ -830,12 +694,6 @@ dependencies = [
"zerovec", "zerovec",
] ]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "idna" name = "idna"
version = "1.1.0" version = "1.1.0"
@ -920,12 +778,6 @@ version = "0.2.180"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
[[package]]
name = "linux-raw-sys"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
[[package]] [[package]]
name = "litemap" name = "litemap"
version = "0.8.1" version = "0.8.1"
@ -985,6 +837,15 @@ dependencies = [
"xml5ever", "xml5ever",
] ]
[[package]]
name = "mediatype"
version = "0.19.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33746aadcb41349ec291e7f2f0a3aa6834d1d7c58066fb4b01f68efc4c4b7631"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.7.6" version = "2.7.6"
@ -998,9 +859,9 @@ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
"dotenvy", "dotenvy",
"feed-rs",
"htmd", "htmd",
"reqwest", "reqwest",
"rss",
"serde", "serde",
"serde_json", "serde_json",
"serenity", "serenity",
@ -1046,29 +907,6 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "native-tls"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
dependencies = [
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-sys",
"schannel",
"security-framework",
"security-framework-sys",
"tempfile",
]
[[package]]
name = "never"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91"
[[package]] [[package]]
name = "new_debug_unreachable" name = "new_debug_unreachable"
version = "1.0.6" version = "1.0.6"
@ -1096,50 +934,6 @@ version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "openssl"
version = "0.10.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328"
dependencies = [
"bitflags 2.10.0",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "openssl-probe"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "openssl-sys"
version = "0.9.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.11.2" version = "0.11.2"
@ -1244,12 +1038,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]] [[package]]
name = "potential_utf" name = "potential_utf"
version = "0.1.4" version = "0.1.4"
@ -1446,6 +1234,35 @@ dependencies = [
"bitflags 2.10.0", "bitflags 2.10.0",
] ]
[[package]]
name = "regex"
version = "1.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.12.28" version = "0.12.28"
@ -1454,22 +1271,17 @@ checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
dependencies = [ dependencies = [
"base64", "base64",
"bytes", "bytes",
"encoding_rs",
"futures-core", "futures-core",
"futures-util", "futures-util",
"h2",
"http", "http",
"http-body", "http-body",
"http-body-util", "http-body-util",
"hyper", "hyper",
"hyper-rustls", "hyper-rustls",
"hyper-tls",
"hyper-util", "hyper-util",
"js-sys", "js-sys",
"log", "log",
"mime",
"mime_guess", "mime_guess",
"native-tls",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"quinn", "quinn",
@ -1480,7 +1292,6 @@ dependencies = [
"serde_urlencoded", "serde_urlencoded",
"sync_wrapper", "sync_wrapper",
"tokio", "tokio",
"tokio-native-tls",
"tokio-rustls 0.26.4", "tokio-rustls 0.26.4",
"tokio-util", "tokio-util",
"tower", "tower",
@ -1508,37 +1319,12 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "rss"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2107738f003660f0a91f56fd3e3bd3ab5d918b2ddaf1e1ec2136fb1c46f71bf"
dependencies = [
"atom_syndication",
"derive_builder",
"never",
"quick-xml",
]
[[package]] [[package]]
name = "rustc-hash" name = "rustc-hash"
version = "2.1.1" version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
[[package]]
name = "rustix"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
dependencies = [
"bitflags 2.10.0",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.22.4" version = "0.22.4"
@ -1611,15 +1397,6 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984"
[[package]]
name = "schannel"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1"
dependencies = [
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.2.0" version = "1.2.0"
@ -1636,29 +1413,6 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "security-framework"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
"bitflags 2.10.0",
"core-foundation",
"core-foundation-sys",
"libc",
"security-framework-sys",
]
[[package]]
name = "security-framework-sys"
version = "2.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.228" version = "1.0.228"
@ -1879,12 +1633,6 @@ dependencies = [
"quote", "quote",
] ]
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]] [[package]]
name = "subtle" name = "subtle"
version = "2.6.1" version = "2.6.1"
@ -1933,40 +1681,6 @@ dependencies = [
"syn 2.0.114", "syn 2.0.114",
] ]
[[package]]
name = "system-configuration"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
dependencies = [
"bitflags 2.10.0",
"core-foundation",
"system-configuration-sys",
]
[[package]]
name = "system-configuration-sys"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "tempfile"
version = "3.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"
dependencies = [
"fastrand",
"getrandom 0.3.4",
"once_cell",
"rustix",
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "tendril" name = "tendril"
version = "0.4.3" version = "0.4.3"
@ -2102,16 +1816,6 @@ dependencies = [
"syn 2.0.114", "syn 2.0.114",
] ]
[[package]]
name = "tokio-native-tls"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
dependencies = [
"native-tls",
"tokio",
]
[[package]] [[package]]
name = "tokio-rustls" name = "tokio-rustls"
version = "0.25.0" version = "0.25.0"
@ -2362,18 +2066,23 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "uuid"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a"
dependencies = [
"getrandom 0.3.4",
"js-sys",
"wasm-bindgen",
]
[[package]] [[package]]
name = "uwl" name = "uwl"
version = "0.6.0" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4bf03e0ca70d626ecc4ba6b0763b934b6f2976e8c744088bb3c1d646fbb1ad0" checksum = "f4bf03e0ca70d626ecc4ba6b0763b934b6f2976e8c744088bb3c1d646fbb1ad0"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.5" version = "0.9.5"
@ -2576,17 +2285,6 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-registry"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
dependencies = [
"windows-link",
"windows-result",
"windows-strings",
]
[[package]] [[package]]
name = "windows-result" name = "windows-result"
version = "0.4.1" version = "0.4.1"

View file

@ -6,7 +6,7 @@ edition = "2024"
[dependencies] [dependencies]
serenity = { version = "0.12", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "standard_framework"] } serenity = { version = "0.12", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "standard_framework"] }
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
rss = "2.0" feed-rs = "2"
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"

View file

@ -85,30 +85,29 @@ async fn check_feed(
) -> Result<()> { ) -> Result<()> {
let feed = rss::fetch_feed(&config.rss).await?; let feed = rss::fetch_feed(&config.rss).await?;
// items usually come in descending order (newest first) // entries usually come in descending order (newest first)
// we should probably reverse them to process oldest first to maintain order in Discord // we should probably reverse them to process oldest first to maintain order in Discord
let mut items = feed.items().to_vec(); let mut items = feed.entries;
items.reverse(); items.reverse();
for item in items { for item in items {
let item_id = rss::get_field_value(&item, config, "link") let item_id = rss::get_field_value(&item, config, "link")
.or_else(|| item.guid().map(|g| g.value().to_string())) .unwrap_or_else(|| item.id.clone());
.unwrap_or_default();
if item_id.is_empty() { continue; } if item_id.is_empty() { continue; }
if !storage.is_processed(name, &item_id)? { if !storage.is_processed(name, &item_id)? {
// Category check // Category check
if let Some(filters) = &config.category_filter { if let Some(filters) = &config.category_filter {
let item_categories: Vec<_> = item.categories().iter().map(|c| c.name()).collect(); let item_categories: Vec<_> = item.categories.iter().map(|c| c.term.clone()).collect();
if item_categories.iter().any(|c| filters.contains(&c.to_string())) { if item_categories.iter().any(|c| filters.contains(c)) {
println!("[{}] Filtered by category: {:?}", name, item_categories); println!("[{}] Filtered by category: {:?}", name, item_categories);
storage.mark_processed(name, &item_id)?; storage.mark_processed(name, &item_id)?;
continue; continue;
} }
} }
println!("[{}] New item: {:?}", name, item.title()); println!("[{}] New item: {:?}", name, item.title.as_ref().map(|t| &t.content));
let content = rss::build_content(config, &item); let content = rss::build_content(config, &item);
let channel_id = config.channel.parse::<u64>()?; let channel_id = config.channel.parse::<u64>()?;
@ -121,18 +120,13 @@ async fn check_feed(
let post = serenity::builder::CreateForumPost::new( let post = serenity::builder::CreateForumPost::new(
title, title,
serenity::builder::CreateMessage::new().content(content) serenity::builder::CreateMessage::new().content(content)
).add_applied_tag(serenity::model::id::ForumTagId::new(tag_id)); ).add_applied_tag(serenity::all::ForumTagId::new(tag_id));
if let Err(e) = channel.create_forum_post(&http, post).await { if let Err(e) = channel.create_forum_post(&http, post).await {
eprintln!("[{}] Failed to create forum post: {}", name, e); eprintln!("[{}] Failed to create forum post: {}", name, e);
continue; continue;
} }
// After creating thread, post the message if needed,
// but CreateThread in Serenity for Forum usually takes message too?
// Actually, for Forum channels, the first message is part of the thread creation.
// Let's refine this if needed based on Serenity 0.12 API.
storage.mark_processed(name, &item_id)?; storage.mark_processed(name, &item_id)?;
} }
} }

View file

@ -1,16 +1,16 @@
use crate::config::RssConfig; use crate::config::RssConfig;
use anyhow::Result; use anyhow::Result;
use htmd::HtmlToMarkdown; use htmd::HtmlToMarkdown;
use rss::Channel; use feed_rs::model::{Feed, Entry};
pub async fn fetch_feed(url: &str) -> Result<Channel> { pub async fn fetch_feed(url: &str) -> Result<Feed> {
let content = reqwest::get(url).await?.bytes().await?; let content = reqwest::get(url).await?.bytes().await?;
let channel = Channel::read_from(&content[..])?; let feed = feed_rs::parser::parse(&content[..])?;
Ok(channel) Ok(feed)
} }
pub fn get_field_value( pub fn get_field_value(
item: &rss::Item, item: &Entry,
config: &RssConfig, config: &RssConfig,
field: &str, field: &str,
) -> Option<String> { ) -> Option<String> {
@ -25,54 +25,33 @@ pub fn get_field_value(
_ => None, _ => None,
}; };
if let Some(p) = path { if let Some(_p) = path {
// Simple JSON path-like resolution for extensions if needed // Path-based resolution is harder with feed-rs because it's already abstracted.
// For now, check standard fields first, then extensions // We'll map some known paths to feed-rs fields.
resolve_path(item, p) match _p.as_str() {
"title" => item.title.as_ref().map(|t| t.content.clone()),
"link" => item.links.first().map(|l| l.href.clone()),
"description" | "summary" => item.summary.as_ref().map(|s| s.content.clone()),
"content" => item.content.as_ref().and_then(|c| c.body.clone()),
"published" | "pubDate" => item.published.map(|d| d.to_rfc3339()),
"dc:creator" | "author.name" => item.authors.first().map(|a| a.name.clone()),
"author.uri" => item.authors.first().and_then(|a| a.uri.clone()),
_ => None,
}
} else { } else {
match field { match field {
"title" => item.title().map(|s| s.to_string()), "title" => item.title.as_ref().map(|t| t.content.clone()),
"link" => item.link().map(|s| s.to_string()), "link" => item.links.first().map(|l| l.href.clone()),
"content" => item.content().or(item.description()).map(|s| s.to_string()), "content" => item.content.as_ref().and_then(|c| c.body.clone())
"author" => item.author().map(|s| s.to_string()), .or_else(|| item.summary.as_ref().map(|s| s.content.clone())),
"pubDate" => item.pub_date().map(|s| s.to_string()), "author" => item.authors.first().map(|a| a.name.clone()),
"pubDate" => item.published.map(|d| d.to_rfc3339()),
_ => None, _ => None,
} }
} }
} }
fn resolve_path(item: &rss::Item, path: &str) -> Option<String> { pub fn build_content(config: &RssConfig, item: &Entry) -> String {
// This is a simplified version. The TS version used a more complex object traversal.
// rss-rs doesn't expose a raw JSON-like structure easily for all extensions.
// We'll handle common cases or use extensions() if needed.
match path {
"title" => item.title().map(|s| s.to_string()),
"link" => item.link().map(|s| s.to_string()),
"description" => item.description().map(|s| s.to_string()),
"content" => item.content().map(|s| s.to_string()),
"pubDate" => item.pub_date().map(|s| s.to_string()),
"dc:creator" => item.dublin_core_ext()
.and_then(|dc| dc.creators().first())
.map(|s| s.to_string()),
_ => {
// Check extensions
for (ns, ext) in item.extensions() {
if path.starts_with(ns) {
let local_name = &path[ns.len()+1..];
if let Some(v) = ext.get(local_name) {
if let Some(first) = v.first() {
return first.value().map(|s| s.to_string());
}
}
}
}
None
}
}
}
pub fn build_content(config: &RssConfig, item: &rss::Item) -> String {
let mut parts = Vec::new(); let mut parts = Vec::new();
let ht = HtmlToMarkdown::new(); let ht = HtmlToMarkdown::new();
@ -81,7 +60,7 @@ pub fn build_content(config: &RssConfig, item: &rss::Item) -> String {
let content = get_field_value(item, config, "content"); let content = get_field_value(item, config, "content");
let author = get_field_value(item, config, "author"); let author = get_field_value(item, config, "author");
let author_link = get_field_value(item, config, "authorLink"); let author_link = get_field_value(item, config, "authorLink");
let pub_date = get_field_value(item, config, "pubDate"); let pub_date = item.published;
if let Some(l) = link.as_ref() { if let Some(l) = link.as_ref() {
parts.push(format!("# {} | [{}](<{}>)", config.emoji, title, l)); parts.push(format!("# {} | [{}](<{}>)", config.emoji, title, l));
@ -103,11 +82,7 @@ pub fn build_content(config: &RssConfig, item: &rss::Item) -> String {
} }
if let Some(pd) = pub_date { if let Some(pd) = pub_date {
if let Ok(dt) = chrono::DateTime::parse_from_rfc2822(&pd) { parts.push(format!("\n-# 🕐 <t:{}:f>", pd.timestamp()));
parts.push(format!("\n-# 🕐 <t:{}:f>", dt.timestamp()));
} else if let Ok(dt) = chrono::DateTime::parse_from_rfc3339(&pd) {
parts.push(format!("\n-# 🕐 <t:{}:f>", dt.timestamp()));
}
} }
let joined = parts.join("\n"); let joined = parts.join("\n");