iAdd final features before alpha. - 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 3c5f83c92d5f06694af51f996986c7fe0f9f9544 /scm/zs//commit/3c5f83c92d5f06694af51f996986c7fe0f9f9544.gph gopher.r-36.net 70 1parent 05d5993acdbe7f3d6de29e18d5f0349e935cf2a8 /scm/zs//commit/05d5993acdbe7f3d6de29e18d5f0349e935cf2a8.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: Wed, 12 Mar 2014 22:55:50 +0100 Err gopher.r-36.net 70 i Err gopher.r-36.net 70 iAdd final features before alpha. Err gopher.r-36.net 70 i Err gopher.r-36.net 70 i * "del" instead of "delete" Err gopher.r-36.net 70 i * add "dryrun" Err gopher.r-36.net 70 i * use pickle instead of shelve Err gopher.r-36.net 70 i * make email handling easier Err gopher.r-36.net 70 i * add useful output when "run" is invoked Err gopher.r-36.net 70 i Err gopher.r-36.net 70 iDiffstat: Err gopher.r-36.net 70 i feeddb.py | 43 +++++++++++++++++++++---------- Err gopher.r-36.net 70 i feedemail.py | 10 +++++----- Err gopher.r-36.net 70 i zs.py | 50 ++++++++++++++++++++++--------- Err gopher.r-36.net 70 i Err gopher.r-36.net 70 i3 files changed, 71 insertions(+), 32 deletions(-) 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@@ -5,7 +5,7 @@ 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 pickle 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@@ -16,16 +16,25 @@ class feeddb(object): 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+ dbpath = "" Err gopher.r-36.net 70 i+ lpath = "" 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+ self.dbpath = os.path.expanduser(path) Err gopher.r-36.net 70 i+ path = os.path.abspath(os.path.dirname(self.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+ self.lpath = "%s.lck" % (self.dbpath) Err gopher.r-36.net 70 i+ self.lockf = open(self.lpath, "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+ Err gopher.r-36.net 70 i+ try: Err gopher.r-36.net 70 i+ fd = open(self.dbpath, "rb") Err gopher.r-36.net 70 i+ self.db = pickle.load(fd) Err gopher.r-36.net 70 i+ fd.close() Err gopher.r-36.net 70 i+ except FileNotFoundError: Err gopher.r-36.net 70 i+ self.db = {} Err gopher.r-36.net 70 i+ 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@@ -51,10 +60,13 @@ class feeddb(object): 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+ fd = open(self.dbpath, "wb+") Err gopher.r-36.net 70 i+ pickle.dump(self.db, fd) Err gopher.r-36.net 70 i+ fd.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+ os.remove(self.lpath) 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@@ -100,16 +112,21 @@ class feeddb(object): 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 getfeedval(self, uri, key): 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 None Err gopher.r-36.net 70 i+ return feed[key] Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def ispaused(self, uri): Err gopher.r-36.net 70 i+ return self.getfeedval(uri, "pause") 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 addfeed(self, uri, email=None): Err gopher.r-36.net 70 i+ def addfeed(self, uri): Err gopher.r-36.net 70 i if not uri in self.listfeeds(): 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@@ -181,7 +198,7 @@ class feeddb(object): Err gopher.r-36.net 70 i article["id"]] Err gopher.r-36.net 70 i if len(a) > 0: Err gopher.r-36.net 70 i for aa in a: Err gopher.r-36.net 70 i- a["unread"] = False Err gopher.r-36.net 70 i+ aa["unread"] = False 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 resetarticles(self, uri): 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@@ -42,7 +42,7 @@ def send(feed, to, smtphost="localhost", smtpport=None, ssl="False", \ 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+ text = "%sURL: %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@@ -88,11 +88,11 @@ def send(feed, to, smtphost="localhost", smtpport=None, ssl="False", \ 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+ 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+ if user != None and password != None: 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 1diff --git a/zs.py b/zs.py /scm/zs//file/zs.py.gph gopher.r-36.net 70 i@@ -11,28 +11,44 @@ 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+import urllib.error 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+def run(db, selfeed=None, dryrun=False): 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: %d" % (len(curfeed["articles"]))) Err gopher.r-36.net 70 i+ if db.ispaused(feeduri): Err gopher.r-36.net 70 i+ print("pause %s" % (feeduri)) Err gopher.r-36.net 70 i+ continue Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ print("fetch %s" % (feeduri)) Err gopher.r-36.net 70 i+ try: Err gopher.r-36.net 70 i+ curfeed = feed.fetch(feeduri) Err gopher.r-36.net 70 i+ except urllib.error.HTTPError as err: Err gopher.r-36.net 70 i+ if err.code == 404: Err gopher.r-36.net 70 i+ print("404 -> pause %s" % (feeduri)) Err gopher.r-36.net 70 i+ db.pause(feeduri) Err gopher.r-36.net 70 i+ continue Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ clen = len(curfeed["articles"]) Err gopher.r-36.net 70 i+ if clen == 0: Err gopher.r-36.net 70 i+ print("0 articles -> pause %s" % (feeduri)) Err gopher.r-36.net 70 i+ db.pause(feeduri) Err gopher.r-36.net 70 i+ continue Err gopher.r-36.net 70 i+ 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: %d" % (len(ufeed["articles"]))) 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+ if len(ufeed["articles"]) > 0: Err gopher.r-36.net 70 i+ print("cur %d unread %d" % (clen, \ Err gopher.r-36.net 70 i+ len(ufeed["articles"]))) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ if dryrun == False: Err gopher.r-36.net 70 i+ feedemail.send(ufeed, db.cfg["email"], 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@@ -54,6 +70,12 @@ def main(args): 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] == "dryrun": Err gopher.r-36.net 70 i+ if len(args) > 2: Err gopher.r-36.net 70 i+ run(db, args[2], dryrun=True) Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ run(db, dryrun=True) 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@@ -80,7 +102,7 @@ def main(args): 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+ elif args[1] == "del": 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 .