Use rss crate for RSS feed parsing (#709)

* Refactor annoying type difference

* Add rss

* Add support for parsing RSS feeds

* Add rss lib

* Add Cargo project

* Lint rust

* changelog

* Add support for new fields
This commit is contained in:
Will Hunt 2023-04-17 12:48:03 +01:00 committed by GitHub
parent 97ff9f2e3a
commit eda6fc8c26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 416 additions and 151 deletions

5
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"rust-analyzer.linkedProjects": [
"./Cargo.toml"
]
}

429
Cargo.lock generated
View File

@ -4,18 +4,37 @@ version = 3
[[package]]
name = "aho-corasick"
version = "0.7.18"
version = "0.7.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
dependencies = [
"memchr",
]
[[package]]
name = "autocfg"
version = "1.0.1"
name = "atom_syndication"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
checksum = "ca96cb38e3d8236f1573a84bbc55e130bd1ae07df770e36d0cf221ea7a50e36c"
dependencies = [
"chrono",
"derive_builder",
"diligent-date-parser",
"never",
"quick-xml",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c70beb79cbb5ce9c4f8e20849978f34225931f665bb49efa6982875a4d5facb3"
[[package]]
name = "block-buffer"
@ -46,9 +65,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
[[package]]
name = "bytemuck"
version = "1.7.3"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f"
checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea"
[[package]]
name = "byteorder"
@ -56,6 +75,22 @@ version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "contrast"
version = "0.1.0"
@ -68,18 +103,87 @@ dependencies = [
[[package]]
name = "convert_case"
version = "0.4.0"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "ctor"
version = "0.1.21"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa"
checksum = "dd4056f63fce3b82d852c3da92b08ea59959890813a7f4ce9c0ff85b10cf301b"
dependencies = [
"quote",
"syn",
"syn 2.0.14",
]
[[package]]
name = "darling"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 1.0.109",
]
[[package]]
name = "darling_macro"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e"
dependencies = [
"darling_core",
"quote",
"syn 1.0.109",
]
[[package]]
name = "derive_builder"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8"
dependencies = [
"derive_builder_macro",
]
[[package]]
name = "derive_builder_core"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "derive_builder_macro"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e"
dependencies = [
"derive_builder_core",
"syn 1.0.109",
]
[[package]]
@ -92,12 +196,35 @@ dependencies = [
]
[[package]]
name = "form_urlencoded"
version = "1.0.1"
name = "diligent-date-parser"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
checksum = "f6cf7fe294274a222363f84bcb63cdea762979a0443b4cf1f4f8fd17c86b1182"
dependencies = [
"chrono",
]
[[package]]
name = "encoding_rs"
version = "0.8.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394"
dependencies = [
"cfg-if",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
dependencies = [
"matches",
"percent-encoding",
]
@ -117,33 +244,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "idna"
version = "0.2.3"
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
dependencies = [
"matches",
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "itoa"
version = "1.0.1"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]]
name = "lazy_static"
version = "1.4.0"
name = "libloading"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "matches"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
"cfg-if",
"winapi",
]
[[package]]
name = "matrix-hookshot"
@ -156,6 +286,7 @@ dependencies = [
"napi-build",
"napi-derive",
"rgb",
"rss",
"serde",
"serde_derive",
"serde_json",
@ -175,22 +306,23 @@ dependencies = [
[[package]]
name = "memchr"
version = "2.4.1"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "napi"
version = "2.0.2"
version = "2.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4cfe7fef533df6323a5aa17d75cb162850f2b045adabb9922bfbd234426a1bb"
checksum = "556470a21074b55be8adee5f27ca04389cfdaca323a28b4b0e9c15466de94731"
dependencies = [
"bitflags",
"ctor",
"lazy_static",
"napi-derive",
"napi-sys",
"once_cell",
"serde",
"serde_json",
"windows",
]
[[package]]
@ -201,51 +333,71 @@ checksum = "ebd4419172727423cf30351406c54f6cc1b354a2cfb4f1dba3e6cd07f6d5522b"
[[package]]
name = "napi-derive"
version = "2.0.4"
version = "2.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fb47b1f5f021abe96fcb84e7977f46c0aa645904c18cdb7b3a031e61899bf48"
checksum = "af2ac63101a19228b0881694cac07468d642fd10e4f943a9c9feebeebf1a4787"
dependencies = [
"convert_case",
"napi-derive-backend",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
name = "napi-derive-backend"
version = "1.0.21"
version = "1.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "487c39117657211e9bf93acade9363eac61ceead142e4906d15ff08d29fa448d"
checksum = "0e32b5bc4d803e40b783b0aa3fe488eac8711cfaa4c5c9915293dfd3d0b99925"
dependencies = [
"convert_case",
"once_cell",
"proc-macro2",
"quote",
"regex",
"syn",
"semver",
"syn 1.0.109",
]
[[package]]
name = "napi-sys"
version = "2.1.0"
version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a385494dac3c52cbcacb393bb3b42669e7db8ab240c7ad5115f549eb061f2cc"
checksum = "166b5ef52a3ab5575047a9fe8d4a030cdd0f63c96f071cd6907674453b07bae3"
dependencies = [
"libloading",
]
[[package]]
name = "never"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91"
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.14"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.9.0"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "opaque-debug"
@ -255,33 +407,43 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
name = "percent-encoding"
version = "2.1.0"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]]
name = "proc-macro2"
version = "1.0.36"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
dependencies = [
"unicode-xid",
"unicode-ident",
]
[[package]]
name = "quick-xml"
version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5c1a97b1bc42b1d550bfb48d4262153fe400a12bab1511821736f7eac76d7e2"
dependencies = [
"encoding_rs",
"memchr",
]
[[package]]
name = "quote"
version = "1.0.14"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.5.6"
version = "1.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d"
dependencies = [
"aho-corasick",
"memchr",
@ -290,47 +452,65 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.6.26"
version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "rgb"
version = "0.8.31"
version = "0.8.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a374af9a0e5fdcdd98c1c7b64f05004f9ea2555b6c75f211daa81268a3c50f1"
checksum = "20ec2d3e3fc7a92ced357df9cebd5a10b6fb2aa1ee797bf7e9ce2f17dffc8f59"
dependencies = [
"bytemuck",
]
[[package]]
name = "ryu"
version = "1.0.9"
name = "rss"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
checksum = "aa1ec965a5f5ec71e16106b35df21861c4014ace848c6b75720816925552936d"
dependencies = [
"atom_syndication",
"derive_builder",
"never",
"quick-xml",
]
[[package]]
name = "ryu"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
[[package]]
name = "semver"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
[[package]]
name = "serde"
version = "1.0.132"
version = "1.0.160"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b9875c23cf305cd1fd7eb77234cbb705f21ea6a72c637a5c6db5fe4b8e7f008"
checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
[[package]]
name = "serde_derive"
version = "1.0.132"
version = "1.0.160"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc0db5cb2556c0e558887d9bbdcf6ac4471e83ff66cf696e5419024d1606276"
checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.14",
]
[[package]]
name = "serde_json"
version = "1.0.73"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5"
checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744"
dependencies = [
"itoa",
"ryu",
@ -338,109 +518,110 @@ dependencies = [
]
[[package]]
name = "syn"
version = "1.0.84"
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecb2e6da8ee5eb9a61068762a32fa9619cc591ceb055b3687f4cd4051ec2e06b"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tinyvec"
version = "1.5.1"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.0"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "typenum"
version = "1.15.0"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "unicode-bidi"
version = "0.3.7"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
[[package]]
name = "unicode-ident"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
name = "unicode-normalization"
version = "0.1.19"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-xid"
version = "0.2.2"
name = "unicode-segmentation"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "url"
version = "2.2.2"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
dependencies = [
"form_urlencoded",
"idna",
"matches",
"percent-encoding",
]
[[package]]
name = "windows"
version = "0.29.0"
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aac7fef12f4b59cd0a29339406cc9203ab44e440ddff6b3f5a41455349fa9cf3"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "windows_aarch64_msvc"
version = "0.29.0"
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d027175d00b01e0cbeb97d6ab6ebe03b12330a35786cbaca5252b1c4bf5d9b"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "windows_i686_gnu"
version = "0.29.0"
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8793f59f7b8e8b01eda1a652b2697d87b93097198ae85f823b969ca5b89bba58"
[[package]]
name = "windows_i686_msvc"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8602f6c418b67024be2996c512f5f995de3ba417f4c75af68401ab8756796ae4"
[[package]]
name = "windows_x86_64_gnu"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3d615f419543e0bd7d2b3323af0d86ff19cbc4f816e6453f36a2c2ce889c354"
[[package]]
name = "windows_x86_64_msvc"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11d95421d9ed3672c280884da53201a5c46b7b2765ca6faf34b0d71cf34a3561"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@ -17,7 +17,7 @@ contrast = "0"
rgb = "0"
md-5 = "0.8.0"
hex = "0.4.3"
rss = "2.0.3"
[build-dependencies]
napi-build = "1"

1
changelog.d/709.misc Normal file
View File

@ -0,0 +1 @@
Switch to using Rust for parsing RSS feeds.

View File

@ -2,7 +2,7 @@
import { Appservice, Intent, IRichReplyMetadata, StateEvent } from "matrix-bot-sdk";
import { BotCommands, botCommand, compileBotCommands, HelpFunction } from "../BotCommands";
import { CommentProcessor } from "../CommentProcessor";
import { FormatUtil } from "../FormatUtil";
import { FormatUtil, LooseMinimalGitHubRepo } from "../FormatUtil";
import { Octokit } from "@octokit/rest";
import { Connection, IConnection, IConnectionState, InstantiateConnectionOpts, ProvisionConnectionOpts } from "./IConnection";
import { GetConnectionsResponseItem } from "../provisioning/api";
@ -25,7 +25,7 @@ import { GitHubIssueConnection } from "./GithubIssue";
import { BridgeConfigGitHub } from "../Config/Config";
import { ApiError, ErrCode, ValidatorApiError } from "../api";
import { PermissionCheckFn } from ".";
import { GitHubRepoMessageBody, MinimalGitHubIssue, MinimalGitHubRepo } from "../libRs";
import { GitHubRepoMessageBody, MinimalGitHubIssue } from "../libRs";
import Ajv, { JSONSchemaType } from "ajv";
import { HookFilter } from "../HookFilter";
import { GitHubGrantChecker } from "../Github/GrantChecker";
@ -637,7 +637,7 @@ export class GitHubRepoConnection extends CommandConnection<GitHubRepoConnection
const issueNumber = result?.[1];
if (issueNumber) {
let issue: MinimalGitHubIssue & { repository?: MinimalGitHubRepo, pull_request?: unknown, state: string };
let issue: MinimalGitHubIssue & { repository?: LooseMinimalGitHubRepo, pull_request?: unknown, state: string };
try {
issue = (await octokit.issues.get({
repo: this.state.repo,

View File

@ -23,12 +23,19 @@ export interface ILabel {
description?: string
}
export type LooseMinimalGitHubRepo = {
id: number,
full_name: string,
html_url: string,
description?: string|null,
}
export class FormatUtil {
public static formatIssueRoomName(issue: MinimalGitHubIssue, repository: { full_name: string}) {
return emoji.emojify(`${repository.full_name}#${issue.number}: ${issue.title}`);
}
public static formatRepoRoomName(repo: MinimalGitHubRepo) {
public static formatRepoRoomName(repo: LooseMinimalGitHubRepo) {
return emoji.emojify(repo.description ? `${repo.full_name}: ${repo.description}` : repo.full_name);
}
@ -40,24 +47,30 @@ export class FormatUtil {
return `${repo.html_url}`;
}
public static getPartialBodyForGithubRepo(repo: MinimalGitHubRepo) {
public static getPartialBodyForGithubRepo(repo: LooseMinimalGitHubRepo) {
if (!repo.id || !repo.html_url || !repo.full_name) {
throw Error('Missing keys in repo object');
}
return getPartialBodyForGithubRepo(repo);
return getPartialBodyForGithubRepo({
...repo,
description: repo.description ?? undefined,
});
}
public static getPartialBodyForGithubIssue(repo: MinimalGitHubRepo, issue: MinimalGitHubIssue) {
public static getPartialBodyForGithubIssue(repo: LooseMinimalGitHubRepo, issue: MinimalGitHubIssue) {
if (!repo.id || !repo.html_url || !repo.full_name) {
throw Error('Missing keys in repo object');
}
if (!issue.html_url || !issue.id || !issue.number || !issue.title) {
throw Error('Missing keys in issue object');
}
return getPartialBodyForGithubIssue(repo, issue);
return getPartialBodyForGithubIssue({
...repo,
description: repo.description ?? undefined,
}, issue);
}
public static getPartialBodyForGitHubPR(repo: MinimalGitHubRepo, issue: IMinimalPR) {
public static getPartialBodyForGitHubPR(repo: LooseMinimalGitHubRepo, issue: IMinimalPR) {
return {
...FormatUtil.getPartialBodyForGithubRepo(repo),
"external_url": issue.html_url,
@ -72,7 +85,7 @@ export class FormatUtil {
public static getPartialBodyForComment(comment: {id: number, html_url: string},
repo?: MinimalGitHubRepo,
repo?: LooseMinimalGitHubRepo,
issue?: MinimalGitHubIssue) {
return {
...(issue && repo ? FormatUtil.getPartialBodyForGithubIssue(repo, issue) : undefined),

View File

@ -1,4 +1,4 @@
import { MatrixClient, MatrixError } from "matrix-bot-sdk";
import { MatrixError } from "matrix-bot-sdk";
import { BridgeConfigFeeds } from "../Config/Config";
import { ConnectionManager } from "../ConnectionManager";
import { FeedConnection } from "../Connections";
@ -13,6 +13,7 @@ import UserAgent from "../UserAgent";
import { randomUUID } from "crypto";
import { StatusCodes } from "http-status-codes";
import { FormatUtil } from "../FormatUtil";
import { FeedItem, parseRSSFeed } from "../libRs";
const log = new Logger("FeedReader");
@ -86,8 +87,8 @@ const accountDataSchema = {
const ajv = new Ajv();
const validateAccountData = ajv.compile<AccountData>(accountDataSchema);
function isNonEmptyString(input: any): input is string {
return input && typeof input === 'string';
function isNonEmptyString(input: unknown): input is string {
return Boolean(input) && typeof input === 'string';
}
function stripHtml(input: string): string {
@ -108,11 +109,6 @@ function shuffle<T>(array: T[]): T[] {
return array;
}
interface FeedItem {
title?: string;
link?: string;
id?: string;
}
export class FeedReader {
private static buildParser(): Parser {
@ -131,7 +127,6 @@ export class FeedReader {
url: string,
headers: Record<string, string>,
timeoutMs: number,
parser: Parser = FeedReader.buildParser(),
httpClient = axios,
): Promise<{ response: AxiosResponse, feed: Parser.Output<FeedItem> }> {
const response = await httpClient.get(url, {
@ -142,7 +137,11 @@ export class FeedReader {
// We don't want to wait forever for the feed.
timeout: timeoutMs,
});
const feed = await parser.parseString(response.data);
if (typeof response.data !== "string") {
throw Error('Unexpected response type');
}
const feed = parseRSSFeed(response.data);
return { response, feed };
}
@ -151,16 +150,16 @@ export class FeedReader {
* @param item A feed item.
* @returns Return either a link to the item, or null.
*/
private static parseLinkFromItem(item: {guid?: string, link?: string}) {
private static parseLinkFromItem(item: FeedItem) {
if (item.link) {
return item.link;
}
if (item.guid) {
if (item.id && item.idIsPermalink) {
try {
// The feed librray doesn't give us attributes (needs isPermaLink), so we're not really sure if this a URL or not.
// Parse it and see.
// https://validator.w3.org/feed/docs/rss2.html#ltguidgtSubelementOfLtitemgt
const url = new URL(item.guid);
const url = new URL(item.id);
return url.toString();
} catch (ex) {
return null;
@ -298,7 +297,6 @@ export class FeedReader {
},
// We don't want to wait forever for the feed.
this.config.pollTimeoutSeconds * 1000,
this.parser,
this.httpClient,
);
@ -327,7 +325,7 @@ export class FeedReader {
for (const item of feed.items) {
// Find the first guid-like that looks like a string.
// Some feeds have a nasty habit of leading a empty tag there, making us parse it as garbage.
const guid = [item.guid, item.id, item.link, item.title].find(isNonEmptyString);
const guid = [item.id, item.link, item.title].find(isNonEmptyString);
if (!guid) {
log.error(`Could not determine guid for entry in ${url}, skipping`);
continue;
@ -352,7 +350,7 @@ export class FeedReader {
title: isNonEmptyString(item.title) ? stripHtml(item.title) : null,
pubdate: item.pubDate ?? null,
summary: item.summary ?? null,
author: item.creator ?? null,
author: item.author ?? null,
link: FeedReader.parseLinkFromItem(item),
fetchKey
};

1
src/feeds/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod parser;

65
src/feeds/parser.rs Normal file
View File

@ -0,0 +1,65 @@
use std::str::FromStr;
use napi::bindgen_prelude::{Error as JsError, Status};
use rss::{Channel, Error as RssError};
#[derive(Serialize, Debug, Deserialize)]
#[napi(object)]
pub struct FeedItem {
pub title: Option<String>,
pub link: Option<String>,
pub id: Option<String>,
pub id_is_permalink: bool,
pub pubdate: Option<String>,
pub summary: Option<String>,
pub author: Option<String>,
}
#[derive(Serialize, Debug, Deserialize)]
#[napi(object)]
pub struct JsRssChannel {
pub title: String,
pub items: Vec<FeedItem>,
}
#[napi(js_name = "parseRSSFeed")]
pub fn js_parse_rss_feed(xml: String) -> Result<JsRssChannel, JsError> {
fn map_item_value(original: &str) -> String {
original.to_string()
}
Channel::from_str(&xml)
.map(|channel| JsRssChannel {
title: channel.title().to_string(),
items: channel
.items()
.iter()
.map(|item| FeedItem {
title: item.title().map(map_item_value),
link: item.link().map(map_item_value),
id: item.guid().map(|f| f.value().to_string()),
id_is_permalink: item.guid().map_or(false, |f| f.is_permalink()),
pubdate: item.pub_date().map(map_item_value),
summary: item.description().map(map_item_value),
author: item.author().map(map_item_value),
})
.collect(),
})
.map_err(|op| match op {
RssError::Utf8(err) => JsError::new(
Status::Unknown,
format!("An error while converting bytes to UTF8. {}'", err).to_string(),
),
RssError::Xml(err) => JsError::new(
Status::Unknown,
format!("XML parsing error. {}", err).to_string(),
),
RssError::InvalidStartTag => JsError::new(
Status::Unknown,
format!("The input didn't begin with an opening <rss> tag.").to_string(),
),
err => JsError::new(
Status::Unknown,
format!("Unknown error trying to parse feed parse feed '{}'", err).to_string(),
),
})
}

View File

@ -1,6 +1,7 @@
pub mod Config;
pub mod Github;
pub mod Jira;
pub mod feeds;
pub mod format_util;
#[macro_use]