#!/usr/bin/env python3 import lxml.etree as ET class RssFeed(object): """Class for storing an RSS 2.0 feed. https://validator.w3.org/feed/docs/rss2.html. """ # pylint: disable=too-many-instance-attributes REQUIRED_ELEMENTS = ["title", "link", "description"] OPTIONAL_ELEMENTS = ["language", "copyright", "managingEditor", "webMaster", "pubDate", "lastBuildDate", "category", "generator", "docs", "cloud", "ttl", "image", "textInput", "skipHours", "skipDays"] def __init__(self): """Define available attributes.""" self.rssurl = None # the URL of the rss feed self.atomlink = None for element in self.REQUIRED_ELEMENTS: setattr(self, element, None) for element in self.OPTIONAL_ELEMENTS: setattr(self, element, None) self.docs = ET.Element("docs") self.docs.text = "https://validator.w3.org/feed/docs/rss2.html" self.author_text = None self.update_timestamp = None self.items = [] self.rss = None self.channel = None def assemble_rss(self, FEED_MAX_ENTRIES): """Assemble RSS 2.0 feed.""" self.rss = ET.Element("rss", version="2.0", nsmap={"atom": "http://www.w3.org/2005/Atom"}) self.channel = ET.SubElement(self.rss, "channel") # https://validator.w3.org/feed/docs/warning/MissingAtomSelfLink.html self.atomlink = ET.SubElement(self.channel, "{http://www.w3.org/2005/Atom}link", href=self.rssurl, rel="self", type="application/rss+xml") for element in self.REQUIRED_ELEMENTS: self.channel.append(getattr(self, element)) for element in self.OPTIONAL_ELEMENTS: attr = getattr(self, element) if attr is not None: self.channel.append(attr) # include at most FEED_MAX_ENTRIES items in the RSS feed for item in self.items[:FEED_MAX_ENTRIES]: self.channel.append(item.item) def dump_rss(self, FEED_MAX_ENTRIES): """Dump RSS feed XML.""" if self.rss is None: self.assemble_rss(FEED_MAX_ENTRIES) return ET.tostring(self.rss).decode("utf-8")