dashboard-rss/src/Server/Feed.fs

46 lines
2.0 KiB
Forth

module Feed
open System.Xml
open System.ServiceModel.Syndication
open Shared.Feed
open System.Net
open System
open System.IO
open Microsoft.FSharp.Control.CommonExtensions
// Parses a RSS feed from an XMLReader, into an array of items
// The .NET Framework SyndicationFeed class does most of the work, except it has a non-F#y class-based model, annoyingly feed-type-specific stuff, and is generally inconvenient to actually use
let parseFromReader (xmlReader : XmlReader) : Item array =
let feed = SyndicationFeed.Load<SyndicationFeed>(xmlReader)
feed.Items |> Seq.toArray |> Array.map (fun item ->
// Try and find some kind of description/content/summary/whatever to display - what sort it is varies based on what type the feed we parse is
let desc =
match item.Content with
| (:? TextSyndicationContent as txt) -> txt.Text
| _ ->
match item.Summary with
| null -> "<No Description>"
| txt -> txt.Text
let date =
match item.PublishDate.Ticks with // Due to format differences between Atom and RSS, we need to check whether PublishDate has an actual value and if not use something else
| 0L -> item.LastUpdatedTime
| _ -> item.PublishDate
{ title = item.Title.Text; description = desc; link = item.Links.[0].Uri.ToString(); time = date; source = feed.Title.Text }
)
let fetchAsync url = async {
let req = WebRequest.Create(Uri(url))
let! resp = req.AsyncGetResponse()
return resp.GetResponseStream() }
// Asynchronously loads an RSS feed from a URL
let loadFromUrl url = async {
use! stream = fetchAsync url
use xml = XmlReader.Create(stream)
return parseFromReader xml
}
// Given several feed URLs, loads them, concatenates them, and sorts them.
let loadMany feeds = async {
let! loadedFeeds = Seq.map loadFromUrl feeds |> Async.Parallel
return Array.concat loadedFeeds |> sort }