diff options
Diffstat (limited to 'src/FeedNim/rss.nim')
| -rw-r--r-- | src/FeedNim/rss.nim | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/src/FeedNim/rss.nim b/src/FeedNim/rss.nim new file mode 100644 index 0000000..35d3520 --- /dev/null +++ b/src/FeedNim/rss.nim @@ -0,0 +1,225 @@ +# Nim RSS (Really Simple Syndication) module + +# Orginally written by Adam Chesak. +# Rewritten by John Conway + +# Released under the MIT open source license. + +import httpclient +import strutils +import sequtils +import xmlparser +import xmltree +import streams +import sugar + +type + RSS* = object + title*: string + link*: string + description*: string + language*: string + copyright*: string + managingEditor*: string + webMaster*: string + pubDate*: string + lastBuildDate*: string + categories*: seq[RSSCategory] + generator*: string + docs*: string + cloud*: RSSCloud + ttl*: int + image*: RSSImage + rating*: string + textInput*: RSSTextInput + skipHours*: seq[int] + skipDays*: seq[string] + items*: seq[RSSItem] + + RSSText = ref object of RootObj + text*: string + + RSSCategory = ref object of RSSText + domain*: string + + RSSEnclosure* = object + url*: string + length*: string + enclosureType*: string + + RSSCloud* = object + domain*: string + port*: string + path*: string + registerProcedure*: string + protocol*: string + + RSSImage* = object + url*: string + title*: string + link*: string + width*: string + height*: string + description*: string + + RSSTextInput* = object + title*: string + description*: string + name*: string + link*: string + + RSSSource* = ref object of RSSText + url*: string + + RSSItem* = object + title*: string + link*: string + description*: string + author*: string + categories*: seq[RSSCategory] + comments*: string + enclosure*: RSSEnclosure + guid*: string + pubDate*: string + source*: RSSSource + +converter rssToString*(obj: RSSText): string = + return obj.text + +func parseCategories( node: XmlNode ): seq[RSSCategory] = + var categories:seq[RSSCategory] + for cat_node in node.findAll("category"): + var category: RSSCategory = RSSCategory() + if cat_node.attr("domain") != "": category.domain = cat_node.attr("domain") + category.text = cat_node.innerText + categories.add(category) + if categories.len == 0: return @[] + return categories + +func parseText ( node: XmlNode ): string = + var content = node.innerText + if content[0 .. 8] == "<![CDATA[": + return content.substr[9 .. content.len()-4 ] + return content + +func parseItem( node: XmlNode) : RSSItem = + var item: RSSItem = RSSItem() + if node.child("title") != nil: item.title = node.child("title").parseText() + + if node.child("link") != nil: item.link = node.child("link").innerText + + if node.child("description") != nil: item.description = node.child("description").parseText() + + for key in @["author", "dc:creator"]: + if node.child(key) != nil: item.author = node.child(key).innerText + + if node.child("category") != nil: item.categories = node.parseCategories() + + if node.child("comments") != nil: item.comments = node.child("comments").innerText + + if node.child("enclosure") != nil: + var encl: RSSEnclosure = RSSEnclosure() + encl.url = node.child("enclosure").attr("url") + encl.length = node.child("enclosure").attr("length") + encl.enclosureType = node.child("enclosure").attr("type") + item.enclosure = encl + + if node.child("guid") != nil: item.guid = node.child("guid").innerText + + if node.child("pubDate") != nil: item.pubDate = node.child("pubDate").innerText + + if node.child("source") != nil: + item.source = RSSSource() + item.source.url = node.child("source").attr("url") + item.source.text = node.child("source").innerText + + return item + +proc parseRSS*(data: string): RSS = + ## Parses the RSS from the given string. + + # Parse into XML. + let root: XmlNode = parseXML(newStringStream(data)) + let channel: XmlNode = root.child("channel") + + # Create the return object. + var rss: RSS = RSS() + + # Fill the required fields. + rss.title = channel.child("title").parseText() + rss.link = channel.child("link").innerText + rss.description = channel.child("description").parseText() + + # Fill the optional fields. + for key in @["language", "dc:language"]: + if channel.child(key) != nil: + rss.language = channel.child(key).innerText + + if channel.child("copyright") != nil: rss.copyright = channel.child("copyright").innerText + + if channel.child("managingEditor") != nil: rss.managingEditor = channel.child("managingEditor").innerText + + if channel.child("webMaster") != nil: rss.webMaster = channel.child("webMaster").innerText + + for key in @["pubDate", "dc:date"]: + if channel.child(key) != nil: + rss.pubDate = channel.child(key).innerText + + if channel.child("lastBuildDate") != nil: rss.lastBuildDate = channel.child("lastBuildDate").innerText + + if channel.child("category") != nil: rss.categories = channel.parseCategories() + + for key in @["generator", "dc:publisher"]: + if channel.child(key) != nil: rss.generator = channel.child(key).innerText + + if channel.child("docs") != nil: rss.docs = channel.child("docs").innerText + + if channel.child("cloud") != nil: + var cloud: RSSCloud = RSSCloud() + cloud.domain = channel.child("cloud").attr("domain") + cloud.port = channel.child("cloud").attr("port") + cloud.path = channel.child("cloud").attr("path") + cloud.registerProcedure = channel.child("cloud").attr("registerProcedure") + cloud.protocol = channel.child("cloud").attr("protocol") + rss.cloud = cloud + + if channel.child("ttl") != nil: rss.ttl = channel.child("ttl").innerText.parseInt() + + if channel.child("image") != nil: + var image: RSSImage = RSSImage() + let img = channel.child("image") + if img.child("url") != nil: image.url = img.child("url").innerText + if img.attr("rdf:resource") != "" and img.attr("rdf:resource") != "": image.url = img.attr("rdf:resource") + if img.child("title") != nil: image.title = img.child("title").innerText + if img.child("link") != nil: image.link = img.child("link").innerText + if img.child("width") != nil: image.width = img.child("width").innerText + if img.child("height") != nil: image.height = img.child("height").innerText + if img.child("description") != nil: image.description = img.child("description").parseText() + rss.image = image + + if channel.child("rating") != nil: rss.rating = channel.child("rating").innerText + + if channel.child("textInput") != nil: + var textInput: RSSTextInput = RSSTextInput() + textInput.title = channel.child("textInput").child("title").innerText + textInput.description = channel.child("textInput").child("description").innerText + textInput.name = channel.child("textInput").child("name").innerText + textInput.link = channel.child("textInput").child("link").innerText + rss.textInput = textInput + + if channel.child("skipHours") != nil: + rss.skipHours = map(channel.findAll("hour"), (x: XmlNode) -> int => x.innerText.parseInt() ) + if channel.child("skipDays") != nil: + rss.skipDays = map(channel.findAll("day"), (x: XmlNode) -> string => x.innerText) + + # If there are no items: + if channel.child("item") == nil and root.child("item") == nil: + rss.items = @[] + return rss + + # Otherwise, add the items. + if channel.child("item") != nil: rss.items = map(channel.findAll("item"), parseItem) + else: rss.items = map(root.findAll("item"), parseItem) + + # Return the RSS data. + return rss
\ No newline at end of file |
