46 lines
2.0 KiB
Forth
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 } |