iInitial commit of Zeitungsschau. - zs - Zeitungsschau rss to email converter Err gopher.r-36.net 70 i Err gopher.r-36.net 70 1Log /scm/zs//log.gph gopher.r-36.net 70 1Files /scm/zs//files.gph gopher.r-36.net 70 1Refs /scm/zs//refs.gph gopher.r-36.net 70 1LICENSE /scm/zs//file/LICENSE.gph gopher.r-36.net 70 i--- Err gopher.r-36.net 70 1commit 5a5d10ddc8ffc58403a4469fa04edf781148e9d7 /scm/zs//commit/5a5d10ddc8ffc58403a4469fa04edf781148e9d7.gph gopher.r-36.net 70 hAuthor: Christoph Lohmann <20h@r-36.net> URL:mailto:20h@r-36.net gopher.r-36.net 70 iDate: Sun, 9 Mar 2014 18:26:25 +0100 Err gopher.r-36.net 70 i Err gopher.r-36.net 70 iInitial commit of Zeitungsschau. Err gopher.r-36.net 70 i Err gopher.r-36.net 70 iDiffstat: Err gopher.r-36.net 70 i feed.py | 170 +++++++++++++++++++++++++++++++ Err gopher.r-36.net 70 i feeddb.py | 180 +++++++++++++++++++++++++++++++ Err gopher.r-36.net 70 i feedemail.py | 97 ++++++++++++++++++++++++++++++ Err gopher.r-36.net 70 i opml.py | 51 +++++++++++++++++++++++++++++++ Err gopher.r-36.net 70 i zs.py | 122 +++++++++++++++++++++++++++++++ Err gopher.r-36.net 70 i Err gopher.r-36.net 70 i5 files changed, 620 insertions(+), 0 deletions(-) Err gopher.r-36.net 70 i--- Err gopher.r-36.net 70 1diff --git a/feed.py b/feed.py /scm/zs//file/feed.py.gph gopher.r-36.net 70 i@@ -0,0 +1,170 @@ Err gopher.r-36.net 70 i+# Err gopher.r-36.net 70 i+# Copy me if you can. Err gopher.r-36.net 70 i+# by 20h Err gopher.r-36.net 70 i+# Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+from lxml import objectify Err gopher.r-36.net 70 i+from datetime import datetime Err gopher.r-36.net 70 i+import dateutil.parser Err gopher.r-36.net 70 i+import urllib.request, urllib.parse, urllib.error Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+def parseiso(dstr): Err gopher.r-36.net 70 i+ return dateutil.parser.parse(str(dstr)) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+def removenamespaces(xml): Err gopher.r-36.net 70 i+ for key in xml.nsmap: Err gopher.r-36.net 70 i+ nsstr = u'{%s}' % (xml.nsmap[key]) Err gopher.r-36.net 70 i+ nsl = len(nsstr) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ for elem in xml.getiterator(): Err gopher.r-36.net 70 i+ if elem.tag.startswith(nsstr): Err gopher.r-36.net 70 i+ elem.tag = elem.tag[nsl:] Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+def parsexml(astr): Err gopher.r-36.net 70 i+ xml = objectify.fromstring(astr) Err gopher.r-36.net 70 i+ removenamespaces(xml) Err gopher.r-36.net 70 i+ return xml Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+def parse(astr): Err gopher.r-36.net 70 i+ xml = parsexml(astr) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ feed = {} Err gopher.r-36.net 70 i+ articles = [] Err gopher.r-36.net 70 i+ isrss = False Err gopher.r-36.net 70 i+ isrdf = False Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ if hasattr(xml, "channel"): Err gopher.r-36.net 70 i+ if hasattr(xml, "item"): Err gopher.r-36.net 70 i+ isrdf = True Err gopher.r-36.net 70 i+ oxml = xml Err gopher.r-36.net 70 i+ xml = xml.channel Err gopher.r-36.net 70 i+ isrss = True Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ feed["title"] = "" Err gopher.r-36.net 70 i+ for e in ("title", "description"): Err gopher.r-36.net 70 i+ if hasattr(xml, e): Err gopher.r-36.net 70 i+ feed[e] = str(xml[e]) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ if hasattr(xml, "image") and hasattr(xml.image, "title"): Err gopher.r-36.net 70 i+ if "title" not in feed: Err gopher.r-36.net 70 i+ feed["title"] = str(xml.image.title) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ if hasattr(xml, "updated"): Err gopher.r-36.net 70 i+ feed["updated"] = parseiso(xml.updated) Err gopher.r-36.net 70 i+ elif hasattr(xml, "pubDate"): Err gopher.r-36.net 70 i+ feed["updated"] = parseiso(xml.pubDate) Err gopher.r-36.net 70 i+ elif hasattr(xml, "lastBuildDate"): Err gopher.r-36.net 70 i+ feed["updated"] = parseiso(xml.lastBuildDate) Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ feed["updated"] = datetime.now() Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ if hasattr(xml, "link"): Err gopher.r-36.net 70 i+ if "href" in xml.link.attrib: Err gopher.r-36.net 70 i+ feed["link"] = str(xml.link.attrib["href"]) Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ feed["link"] = str(xml.link) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ if hasattr(xml, "webmaster"): Err gopher.r-36.net 70 i+ feed["email"] = str(xml.webmaster) Err gopher.r-36.net 70 i+ elif hasattr(xml, "owner") and hasattr(xml.owner, "email"): Err gopher.r-36.net 70 i+ feed["email"] = str(xml.owner.email) Err gopher.r-36.net 70 i+ elif hasattr(xml, "author") and hasattr(xml.author, "email"): Err gopher.r-36.net 70 i+ feed["email"] = str(xml.author.email) Err gopher.r-36.net 70 i+ elif hasattr(xml, "webMaster"): Err gopher.r-36.net 70 i+ feed["email"] = str(xml.webMaster) Err gopher.r-36.net 70 i+ elif hasattr(xml, "managingeditor"): Err gopher.r-36.net 70 i+ feed["email"] = str(xml.managingeditor) Err gopher.r-36.net 70 i+ elif hasattr(xml, "managingEditor"): Err gopher.r-36.net 70 i+ feed["email"] = str(xml.managingEditor) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ if hasattr(xml, "author"): Err gopher.r-36.net 70 i+ if hasattr(xml.author, "name"): Err gopher.r-36.net 70 i+ feed["author"] = str(xml.author.name) Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ feed["author"] = str(xml.author) Err gopher.r-36.net 70 i+ elif hasattr(xml, "creator"): Err gopher.r-36.net 70 i+ feed["author"] = str(xml.creator) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ entryname = "entry" Err gopher.r-36.net 70 i+ if isrss == True or isrdf == True: Err gopher.r-36.net 70 i+ entryname = "item" Err gopher.r-36.net 70 i+ if isrdf == True: Err gopher.r-36.net 70 i+ xml = oxml Err gopher.r-36.net 70 i+ if hasattr(xml, entryname): Err gopher.r-36.net 70 i+ for entry in xml[entryname][:]: Err gopher.r-36.net 70 i+ article = {} Err gopher.r-36.net 70 i+ # title Err gopher.r-36.net 70 i+ if hasattr(entry, "title"): Err gopher.r-36.net 70 i+ article["title"] = str(entry["title"]) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ # link Err gopher.r-36.net 70 i+ if hasattr(entry, "link"): Err gopher.r-36.net 70 i+ if "href" in entry.link.attrib: Err gopher.r-36.net 70 i+ article["link"] = str(entry.link.attrib["href"]) Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ article["link"] = str(entry.link) Err gopher.r-36.net 70 i+ elif hasattr(entry, "source"): Err gopher.r-36.net 70 i+ article["link"] = str(entry.source) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ # id Err gopher.r-36.net 70 i+ if hasattr(entry, "id"): Err gopher.r-36.net 70 i+ article["id"] = str(entry["id"]) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ # enclosure Err gopher.r-36.net 70 i+ if hasattr(entry, "enclosure"): Err gopher.r-36.net 70 i+ if "href" in entry.enclosure.attrib: Err gopher.r-36.net 70 i+ article["file"] = \ Err gopher.r-36.net 70 i+ str(entry.enclosure.attrib["href"]) Err gopher.r-36.net 70 i+ elif "url" in entry.enclosure.attrib: Err gopher.r-36.net 70 i+ article["file"] = \ Err gopher.r-36.net 70 i+ str(entry.enclosure.attrib["url"]) Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ article["file"] = str(entry.enclosure) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ # updated Err gopher.r-36.net 70 i+ if hasattr(entry, "updated"): Err gopher.r-36.net 70 i+ article["updated"] = parseiso(entry.updated) Err gopher.r-36.net 70 i+ elif hasattr(entry, "pubDate"): Err gopher.r-36.net 70 i+ article["updated"] = parseiso(entry.pubDate) Err gopher.r-36.net 70 i+ elif hasattr(entry, "date"): Err gopher.r-36.net 70 i+ article["updated"] = parseiso(entry.date) Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ article["updated"] = datetime.now() Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ # author Err gopher.r-36.net 70 i+ if hasattr(entry, "author"): Err gopher.r-36.net 70 i+ if hasattr(entry.author, "name"): Err gopher.r-36.net 70 i+ article["author"] = str(entry.author.name) Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ article["author"] = str(entry.author) Err gopher.r-36.net 70 i+ elif hasattr(entry, "creator"): Err gopher.r-36.net 70 i+ article["author"] = str(entry.creator) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ # tags Err gopher.r-36.net 70 i+ if hasattr(entry, "category"): Err gopher.r-36.net 70 i+ article["tags"] = [] Err gopher.r-36.net 70 i+ for cat in entry["category"][:]: Err gopher.r-36.net 70 i+ article["tags"].append(str(cat)) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ # text Err gopher.r-36.net 70 i+ if hasattr(entry, "encoded"): Err gopher.r-36.net 70 i+ article["text"] = str(entry.encoded) Err gopher.r-36.net 70 i+ elif hasattr(entry, "content"): Err gopher.r-36.net 70 i+ article["text"] = str(entry.content) Err gopher.r-36.net 70 i+ elif hasattr(entry, "summary"): Err gopher.r-36.net 70 i+ article["text"] = str(entry.summary) Err gopher.r-36.net 70 i+ elif hasattr(entry, "description"): Err gopher.r-36.net 70 i+ article["text"] = str(entry.description) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ articles.append(article) Err gopher.r-36.net 70 i+ feed["articles"] = articles Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ return feed Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+class feedopener(urllib.request.FancyURLopener): Err gopher.r-36.net 70 i+ version = "Zeitungsschau/1.0" Err gopher.r-36.net 70 i+urllib.request._urlopener = feedopener Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+def fetch(uri): Err gopher.r-36.net 70 i+ return parse(urllib.request.urlopen(uri).read()) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 1diff --git a/feeddb.py b/feeddb.py /scm/zs//file/feeddb.py.gph gopher.r-36.net 70 i@@ -0,0 +1,180 @@ Err gopher.r-36.net 70 i+#!/usr/bin/env python Err gopher.r-36.net 70 i+# coding=utf-8 Err gopher.r-36.net 70 i+# Err gopher.r-36.net 70 i+# Copy me if you can. Err gopher.r-36.net 70 i+# by 20h Err gopher.r-36.net 70 i+# Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+import shelve Err gopher.r-36.net 70 i+import os Err gopher.r-36.net 70 i+import os.path Err gopher.r-36.net 70 i+import fcntl Err gopher.r-36.net 70 i+from subprocess import Popen Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+class feeddb(object): Err gopher.r-36.net 70 i+ db = None Err gopher.r-36.net 70 i+ lockf = None Err gopher.r-36.net 70 i+ feeds = {} Err gopher.r-36.net 70 i+ cfg = {} Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def __init__(self, path="~/.zs/feed.db", email=None): Err gopher.r-36.net 70 i+ dbpath = os.path.expanduser(path) Err gopher.r-36.net 70 i+ path = os.path.abspath(os.path.dirname(dbpath)) Err gopher.r-36.net 70 i+ if not os.path.exists(path): Err gopher.r-36.net 70 i+ os.makedirs(path, 0o750) Err gopher.r-36.net 70 i+ lockpath = "%s.lck" % (dbpath) Err gopher.r-36.net 70 i+ self.lockf = open(lockpath, "w") Err gopher.r-36.net 70 i+ fcntl.lockf(self.lockf.fileno(), fcntl.LOCK_EX) Err gopher.r-36.net 70 i+ self.db = shelve.open(dbpath) Err gopher.r-36.net 70 i+ if "feeds" in self.db: Err gopher.r-36.net 70 i+ self.feeds = self.db["feeds"] Err gopher.r-36.net 70 i+ if "cfg" in self.db: Err gopher.r-36.net 70 i+ self.cfg = self.db["cfg"] Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ if not "email" in self.cfg: Err gopher.r-36.net 70 i+ print("You need to specify the default email. Please "\ Err gopher.r-36.net 70 i+ "run 'zs cfg email me@me.com' to "\ Err gopher.r-36.net 70 i+ "set it.") Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ if not "smtphost" in self.cfg: Err gopher.r-36.net 70 i+ self.cfg["smtphost"] = "localhost" Err gopher.r-36.net 70 i+ if not "smtpport" in self.cfg: Err gopher.r-36.net 70 i+ self.cfg["smtpport"] = None Err gopher.r-36.net 70 i+ if not "smtpssl" in self.cfg: Err gopher.r-36.net 70 i+ self.cfg["smtpssl"] = False Err gopher.r-36.net 70 i+ if not "smtpuser" in self.cfg: Err gopher.r-36.net 70 i+ self.cfg["smtpuser"] = None Err gopher.r-36.net 70 i+ if not "smtppassword" in self.cfg: Err gopher.r-36.net 70 i+ self.cfg["smtppassword"] = None Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def __del__(self): Err gopher.r-36.net 70 i+ if self.db != None: Err gopher.r-36.net 70 i+ self.db["feeds"] = self.feeds Err gopher.r-36.net 70 i+ self.db["cfg"] = self.cfg Err gopher.r-36.net 70 i+ self.db.close() Err gopher.r-36.net 70 i+ if self.lockf != None: Err gopher.r-36.net 70 i+ fcntl.flock(self.lockf.fileno(), fcntl.LOCK_UN) Err gopher.r-36.net 70 i+ self.lockf.close() Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def readfeed(self, uri): Err gopher.r-36.net 70 i+ if not uri in self.feeds: Err gopher.r-36.net 70 i+ return None Err gopher.r-36.net 70 i+ return self.feeds[uri] Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def writefeed(self, uri, feed): Err gopher.r-36.net 70 i+ self.feeds[uri] = feed Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def sethook(self, uri, hookfile): Err gopher.r-36.net 70 i+ feed = self.readfeed(uri) Err gopher.r-36.net 70 i+ if feed == None: Err gopher.r-36.net 70 i+ return Err gopher.r-36.net 70 i+ feed["hook"] = hookfile Err gopher.r-36.net 70 i+ self.writefeed(uri, feed) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def runhook(self, uri): Err gopher.r-36.net 70 i+ feed = self.readfeed(uri) Err gopher.r-36.net 70 i+ if feed == None: Err gopher.r-36.net 70 i+ return Err gopher.r-36.net 70 i+ if not "hook" in feed: Err gopher.r-36.net 70 i+ return Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ cmd = os.path.expanduser(feed["hook"]) Err gopher.r-36.net 70 i+ if not os.path.exists(cmd): Err gopher.r-36.net 70 i+ return Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ fd = open("/dev/null") Err gopher.r-36.net 70 i+ if os.fork() == 0: Err gopher.r-36.net 70 i+ p = Popen(cmd, shell=True, stdout=fd, stderr=fd) Err gopher.r-36.net 70 i+ p.wait() Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def setfeedval(self, uri, key, value): Err gopher.r-36.net 70 i+ feed = self.readfeed(uri) Err gopher.r-36.net 70 i+ if feed == None: Err gopher.r-36.net 70 i+ return Err gopher.r-36.net 70 i+ feed[key] = value Err gopher.r-36.net 70 i+ self.writefeed(uri, feed) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def pause(self, uri): Err gopher.r-36.net 70 i+ self.setfeedval(uri, "pause", True) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def unpause(self, uri): Err gopher.r-36.net 70 i+ self.setfeedval(uri, "pause", False) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def addfeed(self, uri, email=None): Err gopher.r-36.net 70 i+ if not uri in self.feeds: Err gopher.r-36.net 70 i+ feed = {} Err gopher.r-36.net 70 i+ if email == None: Err gopher.r-36.net 70 i+ feed["toemail"] = self.cfg["email"] Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ feed["toemail"] = email Err gopher.r-36.net 70 i+ feed["uri"] = uri Err gopher.r-36.net 70 i+ feed["pause"] = False Err gopher.r-36.net 70 i+ feed["articles"] = [] Err gopher.r-36.net 70 i+ self.writefeed(uri, feed) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def delfeed(self, uri): Err gopher.r-36.net 70 i+ if uri in self.feeds: Err gopher.r-36.net 70 i+ del self.feeds[uri] Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def listfeeds(self): Err gopher.r-36.net 70 i+ return list(self.feeds.keys()) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def listactivefeeds(self): Err gopher.r-36.net 70 i+ rfeeds = [] Err gopher.r-36.net 70 i+ for f in self.feeds: Err gopher.r-36.net 70 i+ if self.feeds[f]["pause"] == False: Err gopher.r-36.net 70 i+ rfeeds.append(f) Err gopher.r-36.net 70 i+ return rfeeds Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def mergefeed(self, uri, curfeed): Err gopher.r-36.net 70 i+ rarticles = [] Err gopher.r-36.net 70 i+ feed = self.readfeed(uri) Err gopher.r-36.net 70 i+ if feed == None: Err gopher.r-36.net 70 i+ return curfeed Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ history = feed["articles"] Err gopher.r-36.net 70 i+ for article in curfeed["articles"]: Err gopher.r-36.net 70 i+ if not article in history: Err gopher.r-36.net 70 i+ article["unread"] = True Err gopher.r-36.net 70 i+ history.append(article) Err gopher.r-36.net 70 i+ rarticles.append(article) Err gopher.r-36.net 70 i+ feed["articles"] = history Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ for metakey in ("link", "title", "updated", "author", \ Err gopher.r-36.net 70 i+ "email"): Err gopher.r-36.net 70 i+ if metakey in curfeed: Err gopher.r-36.net 70 i+ feed[metakey] = curfeed[metakey] Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ self.writefeed(uri, feed) Err gopher.r-36.net 70 i+ curfeed["articles"] = rarticles Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ return curfeed Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def unreadarticles(self, uri): Err gopher.r-36.net 70 i+ rfeed = {} Err gopher.r-36.net 70 i+ rfeed["articles"] = [] Err gopher.r-36.net 70 i+ feed = self.readfeed(uri) Err gopher.r-36.net 70 i+ if feed == None: Err gopher.r-36.net 70 i+ return rfeed Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ for metakey in ("link", "title", "updated", "author", \ Err gopher.r-36.net 70 i+ "email", "toemail"): Err gopher.r-36.net 70 i+ if metakey in feed: Err gopher.r-36.net 70 i+ rfeed[metakey] = feed[metakey] Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ history = feed["articles"] Err gopher.r-36.net 70 i+ for article in history: Err gopher.r-36.net 70 i+ if article["unread"] == True: Err gopher.r-36.net 70 i+ rfeed["articles"].append(article) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ return rfeed Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def setreadarticles(self, uri, curfeed=None): Err gopher.r-36.net 70 i+ feed = self.readfeed(uri) Err gopher.r-36.net 70 i+ if feed == None: Err gopher.r-36.net 70 i+ return Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ for article in curfeed["articles"]: Err gopher.r-36.net 70 i+ if article in feed["history"]: Err gopher.r-36.net 70 i+ article["unread"] == False Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 1diff --git a/feedemail.py b/feedemail.py /scm/zs//file/feedemail.py.gph gopher.r-36.net 70 i@@ -0,0 +1,97 @@ Err gopher.r-36.net 70 i+#!/usr/bin/env python Err gopher.r-36.net 70 i+# coding=utf-8 Err gopher.r-36.net 70 i+# Err gopher.r-36.net 70 i+# Copy me if you can. Err gopher.r-36.net 70 i+# by 20h Err gopher.r-36.net 70 i+# Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+import smtplib Err gopher.r-36.net 70 i+from email.mime.text import MIMEText Err gopher.r-36.net 70 i+from email.mime.multipart import MIMEMultipart Err gopher.r-36.net 70 i+from email.utils import formataddr, formatdate, parseaddr Err gopher.r-36.net 70 i+from email.header import Header Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+import html2text as h2t Err gopher.r-36.net 70 i+h2t.UNICODE_SNOB = 1 Err gopher.r-36.net 70 i+h2t.LINKS_EACH_PARAGRAPH = 0 Err gopher.r-36.net 70 i+h2t.BODY_WIDTH = 0 Err gopher.r-36.net 70 i+h2t.INLINE_LINKS = 0 Err gopher.r-36.net 70 i+html2text = h2t.html2text Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+def normalizeheader(hstr): Err gopher.r-36.net 70 i+ return hstr.replace("\n", " ").strip() Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+def send(feed, to, smtphost="localhost", smtpport=None, ssl=False, \ Err gopher.r-36.net 70 i+ user=None, password=None): Err gopher.r-36.net 70 i+ articles = feed["articles"] Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ for article in articles: Err gopher.r-36.net 70 i+ if "text" in article: Err gopher.r-36.net 70 i+ text = html2text(article["text"]) Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ text = "" Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ if "title" in article: Err gopher.r-36.net 70 i+ subject = Header( \ Err gopher.r-36.net 70 i+ normalizeheader(article["title"]),\ Err gopher.r-36.net 70 i+ "utf-8") Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ subject = Header(normalizeheader(text[:70]),\ Err gopher.r-36.net 70 i+ "utf-8") Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ # Append metadata. Err gopher.r-36.net 70 i+ if "link" in article: Err gopher.r-36.net 70 i+ text = "%sLink: %s\n" % (text, article["link"]) Err gopher.r-36.net 70 i+ if "file" in article: Err gopher.r-36.net 70 i+ text = "%sEnclosure: %s\n" % (text, article["file"]) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ msg = MIMEText(text, "plain", "utf-8") Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ if "email" in feed: Err gopher.r-36.net 70 i+ faddr = feed["email"] Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ faddr = "none@none.no" Err gopher.r-36.net 70 i+ if "title" in feed: Err gopher.r-36.net 70 i+ if "author" in article: Err gopher.r-36.net 70 i+ fname = "%s: %s" % (feed["title"], \ Err gopher.r-36.net 70 i+ article["author"]) Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ fname = feed["title"] Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ msg["From"] = formataddr((fname, faddr)) Err gopher.r-36.net 70 i+ msg["To"] = formataddr(parseaddr(to)) Err gopher.r-36.net 70 i+ msg["Date"] = formatdate() Err gopher.r-36.net 70 i+ msg["Subject"] = subject Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ if "link" in article: Err gopher.r-36.net 70 i+ msg["X-RSS-URL"] = article["link"] Err gopher.r-36.net 70 i+ if "link" in feed: Err gopher.r-36.net 70 i+ msg["X-RSS-Feed"] = feed["link"] Err gopher.r-36.net 70 i+ if "id" in article: Err gopher.r-36.net 70 i+ msg["X-RSS-ID"] = article["id"] Err gopher.r-36.net 70 i+ if "tags" in article: Err gopher.r-36.net 70 i+ msg["X-RSS-TAGS"] = Header(",".join(article["tags"]),\ Err gopher.r-36.net 70 i+ "utf-8") Err gopher.r-36.net 70 i+ msg["User-Agent"] = "Zeitungsschau" Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ print(msg.as_string()) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ if ssl == True: Err gopher.r-36.net 70 i+ s = smtplib.SMTP_SSL() Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ s = smtplib.SMTP() Err gopher.r-36.net 70 i+ if smtpport != None: Err gopher.r-36.net 70 i+ s.connect(smtphost, smtpport) Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ s.connect(smtphost) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ if user != None and password != None: Err gopher.r-36.net 70 i+ s.ehlo() Err gopher.r-36.net 70 i+ if ssl == False: Err gopher.r-36.net 70 i+ s.starttls() Err gopher.r-36.net 70 i+ s.ehlo() Err gopher.r-36.net 70 i+ s.login(user, password) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ s.sendmail(faddr, to, msg.as_string()) Err gopher.r-36.net 70 i+ s.quit() Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 1diff --git a/opml.py b/opml.py /scm/zs//file/opml.py.gph gopher.r-36.net 70 i@@ -0,0 +1,51 @@ Err gopher.r-36.net 70 i+# Err gopher.r-36.net 70 i+# Copy me if you can. Err gopher.r-36.net 70 i+# by 20h Err gopher.r-36.net 70 i+# Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+from lxml import etree Err gopher.r-36.net 70 i+from datetime import datetime Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+def read(ostr): Err gopher.r-36.net 70 i+ parser = etree.XMLParser(recover=True, encoding='utf-8') Err gopher.r-36.net 70 i+ xml = etree.fromstring(ostr, parser) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ rssfeeds = [] Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ feeds = xml.xpath("//outline") Err gopher.r-36.net 70 i+ for feed in feeds: Err gopher.r-36.net 70 i+ if "xmlUrl" in feed.attrib: Err gopher.r-36.net 70 i+ rssfeeds.append(feed.attrib["xmlUrl"]) Err gopher.r-36.net 70 i+ elif "text" in feed.attrib: Err gopher.r-36.net 70 i+ rssfeeds.append(feed.attrib["text"]) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ return rssfeeds Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+def write(rssfeeds): Err gopher.r-36.net 70 i+ opmle = etree.Element("opml") Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ heade = etree.SubElement(opmle, "head") Err gopher.r-36.net 70 i+ titlee = etree.SubElement(heade, "title") Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ daten = datetime.now().strftime("%Y-%m-%dT%H:%M:%S%Z") Err gopher.r-36.net 70 i+ datece = etree.SubElement(heade, "dateCreated") Err gopher.r-36.net 70 i+ datece.text = daten Err gopher.r-36.net 70 i+ dateme = etree.SubElement(heade, "dateModified") Err gopher.r-36.net 70 i+ dateme.text = daten Err gopher.r-36.net 70 i+ ownerne = etree.SubElement(heade, "ownerName") Err gopher.r-36.net 70 i+ ownerne.text = "Me" Err gopher.r-36.net 70 i+ docse = etree.SubElement(heade, "docs") Err gopher.r-36.net 70 i+ docse.text = "http://dev.opml.org/spec2.html" Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ bodye = etree.SubElement(opmle, "body") Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ for rss in rssfeeds: Err gopher.r-36.net 70 i+ outlinee = etree.SubElement(bodye, "outline") Err gopher.r-36.net 70 i+ outlinee.attrib["type"] = "rss" Err gopher.r-36.net 70 i+ outlinee.attrib["text"] = rss Err gopher.r-36.net 70 i+ outlinee.attrib["xmlUrl"] = rss Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ return etree.tostring(opmle, encoding="utf-8", \ Err gopher.r-36.net 70 i+ pretty_print=True, \ Err gopher.r-36.net 70 i+ xml_declaration=True).decode("utf-8") Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 1diff --git a/zs.py b/zs.py /scm/zs//file/zs.py.gph gopher.r-36.net 70 i@@ -0,0 +1,122 @@ Err gopher.r-36.net 70 i+#!/usr/bin/env python Err gopher.r-36.net 70 i+# coding=utf-8 Err gopher.r-36.net 70 i+# Err gopher.r-36.net 70 i+# Copy me if you can. Err gopher.r-36.net 70 i+# by 20h Err gopher.r-36.net 70 i+# Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+import sys Err gopher.r-36.net 70 i+import os Err gopher.r-36.net 70 i+import feed Err gopher.r-36.net 70 i+import feeddb Err gopher.r-36.net 70 i+import opml Err gopher.r-36.net 70 i+import feedemail Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+def run(db, selfeed=None): Err gopher.r-36.net 70 i+ feeduris = db.listfeeds() Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ if feeduris != None and selfeed in feeduris: Err gopher.r-36.net 70 i+ feeduris = [selfeed] Err gopher.r-36.net 70 i+ print("feeduris: %s" % (feeduris)) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ for feeduri in feeduris: Err gopher.r-36.net 70 i+ curfeed = feed.fetch(feeduri) Err gopher.r-36.net 70 i+ print("curfeed: %s" % (curfeed)) Err gopher.r-36.net 70 i+ db.mergefeed(feeduri, curfeed) Err gopher.r-36.net 70 i+ ufeed = db.unreadarticles(feeduri) Err gopher.r-36.net 70 i+ print("unread: %s" % (ufeed)) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ if "toemail" in ufeed: Err gopher.r-36.net 70 i+ toemail = ufeed["toemail"] Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ toemail = db.cfg["email"] Err gopher.r-36.net 70 i+ feedemail.send(ufeed, toemail, db.cfg["smtphost"], \ Err gopher.r-36.net 70 i+ db.cfg["smtpport"], db.cfg["smtpssl"], \ Err gopher.r-36.net 70 i+ db.cfg["smtpuser"], db.cfg["smtppassword"]) Err gopher.r-36.net 70 i+ db.setreadarticles(feeduri, ufeed) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+def usage(app): Err gopher.r-36.net 70 i+ app = os.path.basename(app) Err gopher.r-36.net 70 i+ sys.stderr.write("usage: %s [-h] cmd\n" % (app)) Err gopher.r-36.net 70 i+ sys.exit(1) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+def main(args): Err gopher.r-36.net 70 i+ retval = 0 Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ if len(args) < 2: Err gopher.r-36.net 70 i+ usage(args[0]) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ db = feeddb.feeddb() Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ if args[1] == "run": Err gopher.r-36.net 70 i+ if len(args) > 2: Err gopher.r-36.net 70 i+ run(db, args[2]) Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ run(db) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ elif args[1] == "cfg": Err gopher.r-36.net 70 i+ if len(args) < 3: Err gopher.r-36.net 70 i+ for k in db.cfg: Err gopher.r-36.net 70 i+ print("%s = '%s'" % (k, db.cfg[k])) Err gopher.r-36.net 70 i+ elif len(args) < 4: Err gopher.r-36.net 70 i+ if args[2] in db.cfg: Err gopher.r-36.net 70 i+ print("%s = '%s'" % (args[2], \ Err gopher.r-36.net 70 i+ db.cfg[args[2]])) Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ retval = 1 Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ db.cfg[args[2]] = args[3] Err gopher.r-36.net 70 i+ print("%s = '%s'" % (args[2], db.cfg[args[2]])) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ elif args[1] == "add": Err gopher.r-36.net 70 i+ if len(args) < 3: Err gopher.r-36.net 70 i+ usage(args[0]) Err gopher.r-36.net 70 i+ email = None Err gopher.r-36.net 70 i+ if len(args) > 3: Err gopher.r-36.net 70 i+ email = args[3] Err gopher.r-36.net 70 i+ db.addfeed(args[2], email) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ elif args[1] == "list": Err gopher.r-36.net 70 i+ for f in db.listfeeds(): Err gopher.r-36.net 70 i+ print(f) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ elif args[1] == "delete": Err gopher.r-36.net 70 i+ if len(args) < 3: Err gopher.r-36.net 70 i+ usage(args[0]) Err gopher.r-36.net 70 i+ db.delfeed(args[1]) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ elif args[1] == "pause": Err gopher.r-36.net 70 i+ if len(args) < 3: Err gopher.r-36.net 70 i+ usage(args[0]) Err gopher.r-36.net 70 i+ db.pause(args[2]) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ elif args[1] == "unpause": Err gopher.r-36.net 70 i+ if len(args) < 3: Err gopher.r-36.net 70 i+ usage(args[0]) Err gopher.r-36.net 70 i+ db.unpause(args[2]) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ elif args[1] == "opmlexport": Err gopher.r-36.net 70 i+ if len(args) > 2: Err gopher.r-36.net 70 i+ filen = open(args[2], "w") Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ filen = sys.stdout Err gopher.r-36.net 70 i+ filen.write(opml.write(db.listfeeds())) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ elif args[1] == "opmlimport": Err gopher.r-36.net 70 i+ if len(args) > 2: Err gopher.r-36.net 70 i+ filen = open(args[2], "r") Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ filen = sys.stdin Err gopher.r-36.net 70 i+ feedlist = db.listfeeds() Err gopher.r-36.net 70 i+ nfeedlist = opml.read(filen.read().encode("utf-8")) Err gopher.r-36.net 70 i+ for f in nfeedlist: Err gopher.r-36.net 70 i+ if not f in feedlist: Err gopher.r-36.net 70 i+ print("import feed: %s" % (f)) Err gopher.r-36.net 70 i+ db.addfeed(f) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ del db Err gopher.r-36.net 70 i+ return retval Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+if __name__ == "__main__": Err gopher.r-36.net 70 i+ sys.exit(main(sys.argv)) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 .