iMajor overhaul. - 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 c1d3567569feb6b1cbb4513fa52c10839e1a9598 /scm/zs//commit/c1d3567569feb6b1cbb4513fa52c10839e1a9598.gph gopher.r-36.net 70 1parent 8faa15f0c27e430b19c4137a2b523791efaeb45d /scm/zs//commit/8faa15f0c27e430b19c4137a2b523791efaeb45d.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, 17 Aug 2014 17:53:53 +0200 Err gopher.r-36.net 70 i Err gopher.r-36.net 70 iMajor overhaul. Err gopher.r-36.net 70 i Err gopher.r-36.net 70 iWhat has changed: Err gopher.r-36.net 70 i* Now there is setup.py, which works, using dependency handling. Err gopher.r-36.net 70 i* Zeitungsschau is more restrictive. Err gopher.r-36.net 70 i * Only import the last 64 articles in chronological order. Err gopher.r-36.net 70 i * Only manage the last 2048 articles, which keeps the database small. Err gopher.r-36.net 70 i* Allow local smtp delivery, which is faster and does bypass silly filters. Err gopher.r-36.net 70 i* Now use getopt to handle a debug flag. Err gopher.r-36.net 70 i Err gopher.r-36.net 70 iDiffstat: Err gopher.r-36.net 70 i Makefile | 45 ------------------------------- Err gopher.r-36.net 70 i config.mk | 14 -------------- Err gopher.r-36.net 70 i feed.py | 213 ------------------------------- Err gopher.r-36.net 70 i feeddb.py | 239 ------------------------------- Err gopher.r-36.net 70 i feedemail.py | 102 ------------------------------- Err gopher.r-36.net 70 i opml.py | 53 ------------------------------ Err gopher.r-36.net 70 i setup.py | 46 +++++++++++++++++++++++++++++++ Err gopher.r-36.net 70 i zeitungsschau/__init__.py | 0 Err gopher.r-36.net 70 i zeitungsschau/feed.py | 226 +++++++++++++++++++++++++++++++ Err gopher.r-36.net 70 i zeitungsschau/feeddb.py | 275 +++++++++++++++++++++++++++++++ Err gopher.r-36.net 70 i zeitungsschau/feedemail.py | 127 +++++++++++++++++++++++++++++++ Err gopher.r-36.net 70 i zeitungsschau/opml.py | 53 ++++++++++++++++++++++++++++++ Err gopher.r-36.net 70 i zs | 241 +++++++++++++++++++++++++++++++ Err gopher.r-36.net 70 i zs.py | 215 ------------------------------- Err gopher.r-36.net 70 i Err gopher.r-36.net 70 i14 files changed, 968 insertions(+), 881 deletions(-) Err gopher.r-36.net 70 i--- Err gopher.r-36.net 70 1diff --git a/Makefile b/Makefile /scm/zs//file/Makefile.gph gopher.r-36.net 70 i@@ -1,45 +0,0 @@ Err gopher.r-36.net 70 i-# zeitungsschau - rss2email Err gopher.r-36.net 70 i-# See LICENSE file for copyright and license details. Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i-include config.mk Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i-SRC = ${NAME}.py Err gopher.r-36.net 70 i-OBJ = ${SRC:.py=.exe} Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i-all: options ${NAME} Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i-options: Err gopher.r-36.net 70 i- @echo ${NAME} build options: Err gopher.r-36.net 70 i- @echo "CC = ${CC}" Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i-${OBJ}: config.mk Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i-${NAME}: ${OBJ} Err gopher.r-36.net 70 i- @echo CC -o $@.py Err gopher.r-36.net 70 i- @${CC} $@.py Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i-clean: Err gopher.r-36.net 70 i- @echo cleaning Err gopher.r-36.net 70 i- @rm -f ${NAME}.exe __pycache__ zs.build ${NAME}-${VERSION}.tar.gz Err gopher.r-36.net 70 i- @find bin -type l -exec rm {} \; Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i-dist: clean Err gopher.r-36.net 70 i- @echo creating dist tarball Err gopher.r-36.net 70 i- @mkdir -p ${NAME}-${VERSION} Err gopher.r-36.net 70 i- @cp -R LICENSE README.md config.mk Makefile \ Err gopher.r-36.net 70 i- *.py ${NAME}-${VERSION} Err gopher.r-36.net 70 i- @tar -cf ${NAME}-${VERSION}.tar ${NAME}-${VERSION} Err gopher.r-36.net 70 i- @gzip ${NAME}-${VERSION}.tar Err gopher.r-36.net 70 i- @rm -rf ${NAME}-${VERSION} Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i-install: all Err gopher.r-36.net 70 i- @echo installing executable files to ${DESTDIR}${PREFIX}/bin Err gopher.r-36.net 70 i- @mkdir -p ${DESTDIR}${PREFIX}/bin Err gopher.r-36.net 70 i- @cp zs.exe ${DESTDIR}${PREFIX}/bin/zs Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i-uninstall: Err gopher.r-36.net 70 i- @echo removing executable files from ${DESTDIR}${PREFIX}/bin Err gopher.r-36.net 70 i- @rm -f ${DESTDIR}${PREFIX}/bin/zs Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i-.PHONY: all options clean dist install uninstall Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 1diff --git a/config.mk b/config.mk /scm/zs//file/config.mk.gph gopher.r-36.net 70 i@@ -1,14 +0,0 @@ Err gopher.r-36.net 70 i-# Zeitungsschau metadata Err gopher.r-36.net 70 i-NAME = zs Err gopher.r-36.net 70 i-VERSION = 0.1 Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i-# Customize below to fit your system Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i-# paths Err gopher.r-36.net 70 i-PREFIX = /usr/local Err gopher.r-36.net 70 i-MANPREFIX = ${PREFIX}/share/man Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i-# compiler and linker Err gopher.r-36.net 70 i-CC = nuitka --recurse-all --recurse-directory --show-modules \ Err gopher.r-36.net 70 i- --show-progress 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@@ -1,213 +0,0 @@ Err gopher.r-36.net 70 i-# Err gopher.r-36.net 70 i-# See LICENSE for licensing details. 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 lxml import etree 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- try: 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- except etree.XMLSyntaxError: Err gopher.r-36.net 70 i- try: Err gopher.r-36.net 70 i- parser = etree.HTMLParser() 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- except etree.XMLSyntaxError: Err gopher.r-36.net 70 i- return None 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- if xml == None: Err gopher.r-36.net 70 i- return None 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- now = datetime.now() 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"] = 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- # 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- if hasattr(entry, "group") and \ Err gopher.r-36.net 70 i- hasattr(entry.group, "content"): Err gopher.r-36.net 70 i- if "url" in entry.group.content: Err gopher.r-36.net 70 i- article["file"] = \ Err gopher.r-36.net 70 i- str(entry.group.content.\ Err gopher.r-36.net 70 i- attrib["file"]) 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"] = 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- # 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- else: Err gopher.r-36.net 70 i- if "link" in article: Err gopher.r-36.net 70 i- article["id"] = article["link"] Err gopher.r-36.net 70 i- elif "file" in article: Err gopher.r-36.net 70 i- article["id"] = article["file"] Err gopher.r-36.net 70 i- else: Err gopher.r-36.net 70 i- article["id"] = article["text"][:30] Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i- if article["updated"] == now: Err gopher.r-36.net 70 i- article["uuid"] = "" Err gopher.r-36.net 70 i- else: Err gopher.r-36.net 70 i- article["uuid"] = "%s" % (article["updated"]) Err gopher.r-36.net 70 i- for e in ("id", "title", "file"): Err gopher.r-36.net 70 i- if e in article: Err gopher.r-36.net 70 i- article["uuid"] = "%s-%s" % \ Err gopher.r-36.net 70 i- (article["uuid"],\ Err gopher.r-36.net 70 i- article[e]) Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i- # sanity checks Err gopher.r-36.net 70 i- if "title" not in article and "text" not in article \ Err gopher.r-36.net 70 i- and "file" not in article: Err gopher.r-36.net 70 i- continue 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, timeout=5).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@@ -1,239 +0,0 @@ Err gopher.r-36.net 70 i-# Err gopher.r-36.net 70 i-# See LICENSE for licensing details. 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 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-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- 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- 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- 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- 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- 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- 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- 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 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- if key not in feed: 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 setretry(self, uri, retries): Err gopher.r-36.net 70 i- self.setfeedval(uri, "retry", retries) Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i- def getretry(self, uri): Err gopher.r-36.net 70 i- retries = self.getfeedval(uri, "retry") Err gopher.r-36.net 70 i- if retries == None: Err gopher.r-36.net 70 i- return 0 Err gopher.r-36.net 70 i- else: Err gopher.r-36.net 70 i- return retries 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- self.setretry(uri, 0) 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): 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- 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.listfeeds(): Err gopher.r-36.net 70 i- del self.feeds[uri] Err gopher.r-36.net 70 i- return True Err gopher.r-36.net 70 i- else: Err gopher.r-36.net 70 i- return False 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- a = [art for art in history if art["uuid"] == \ Err gopher.r-36.net 70 i- article["uuid"]] Err gopher.r-36.net 70 i- if len(a) == 0: 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 setarticleunread(self, uri, ids): 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 feed["articles"]: Err gopher.r-36.net 70 i- a = [art for art in feed["articles"] if art["uuid"] == \ Err gopher.r-36.net 70 i- ids] 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- aa["unread"] = True 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 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- a = [art for art in curfeed["articles"] if art["uuid"] == \ Err gopher.r-36.net 70 i- article["uuid"]] 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- 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 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["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 1diff --git a/feedemail.py b/feedemail.py /scm/zs//file/feedemail.py.gph gopher.r-36.net 70 i@@ -1,102 +0,0 @@ Err gopher.r-36.net 70 i-# Err gopher.r-36.net 70 i-# See LICENSE for licensing details. 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-import time Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i-import 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- h2t = html2text.HTML2Text() Err gopher.r-36.net 70 i- h2t.body_width = 0 Err gopher.r-36.net 70 i- h2t.unicode_snob = 1 Err gopher.r-36.net 70 i- h2t.escape_snob = 1 Err gopher.r-36.net 70 i- h2t.inline_links = 0 Err gopher.r-36.net 70 i- h2t.links_each_paragraph = 0 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 = "%s\n" % (h2t.handle(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 = "%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- 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- if "updated" in article: Err gopher.r-36.net 70 i- msg["Date"] = formatdate(time.mktime(\ Err gopher.r-36.net 70 i- article["updated"].timetuple())) Err gopher.r-36.net 70 i- else: 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- 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- 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 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@@ -1,53 +0,0 @@ Err gopher.r-36.net 70 i-# Err gopher.r-36.net 70 i-# See LICENSE for licensing details. 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/setup.py b/setup.py /scm/zs//file/setup.py.gph gopher.r-36.net 70 i@@ -0,0 +1,46 @@ 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+ttry: Err gopher.r-36.net 70 i+ from setuptools import setup Err gopher.r-36.net 70 i+except ImportError: Err gopher.r-36.net 70 i+ from distutils.core import setup Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+setup( Err gopher.r-36.net 70 i+ name='zeitungsschau', Err gopher.r-36.net 70 i+ version='0.5.0', Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ py_modules=['zeitungsschau'], Err gopher.r-36.net 70 i+ packages=['zeitungsschau'], Err gopher.r-36.net 70 i+ scripts=['zs'], Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ provides=['zeitungsschau'], Err gopher.r-36.net 70 i+ requires=[ Err gopher.r-36.net 70 i+ 'lxml (>=0.1)', Err gopher.r-36.net 70 i+ 'dateutil (>=0.1)', Err gopher.r-36.net 70 i+ 'html2text (>=0.1)' Err gopher.r-36.net 70 i+ ], Err gopher.r-36.net 70 i+ platforms=['all'], Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ author='Christoph Lohmann', Err gopher.r-36.net 70 i+ author_email='20h@r-36.net', Err gopher.r-36.net 70 i+ maintainer='Christoph Lohmann', Err gopher.r-36.net 70 i+ maintainer_email='20h@r-36.net', Err gopher.r-36.net 70 i+ url='http://git.r-36.net/zs', Err gopher.r-36.net 70 i+ description='Zeitungsschau is an rss2email converter', Err gopher.r-36.net 70 i+ long_description=open("README.md").read(), Err gopher.r-36.net 70 i+ license='GPLv3', Err gopher.r-36.net 70 i+ classifiers=[ Err gopher.r-36.net 70 i+ 'Environment :: Console', Err gopher.r-36.net 70 i+ 'Intended Audience :: End Users/Desktop', Err gopher.r-36.net 70 i+ 'Operating System :: OS Independent', Err gopher.r-36.net 70 i+ 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', Err gopher.r-36.net 70 i+ 'Programming Language :: Python', Err gopher.r-36.net 70 i+ 'Topic :: Communications :: Email' Err gopher.r-36.net 70 i+ ], Err gopher.r-36.net 70 i+) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 1diff --git a/zeitungsschau/__init__.py b/zeitungsschau/__init__.py /scm/zs//file/zeitungsschau/__init__.py.gph gopher.r-36.net 70 1diff --git a/zeitungsschau/feed.py b/zeitungsschau/feed.py /scm/zs//file/zeitungsschau/feed.py.gph gopher.r-36.net 70 i@@ -0,0 +1,226 @@ Err gopher.r-36.net 70 i+# Err gopher.r-36.net 70 i+# See LICENSE for licensing details. 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 lxml import etree 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+import hashlib 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+ try: 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+ except etree.XMLSyntaxError: Err gopher.r-36.net 70 i+ try: Err gopher.r-36.net 70 i+ parser = etree.HTMLParser() 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+ except etree.XMLSyntaxError: Err gopher.r-36.net 70 i+ return None 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+ if xml == None: Err gopher.r-36.net 70 i+ return None 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+ now = datetime.now() 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"] = 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+ # 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+ if hasattr(entry, "group") and \ Err gopher.r-36.net 70 i+ hasattr(entry.group, "content"): Err gopher.r-36.net 70 i+ if "url" in entry.group.content: Err gopher.r-36.net 70 i+ article["file"] = \ Err gopher.r-36.net 70 i+ str(entry.group.content.\ Err gopher.r-36.net 70 i+ attrib["file"]) 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"] = 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+ # 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+ else: Err gopher.r-36.net 70 i+ if "link" in article: Err gopher.r-36.net 70 i+ article["id"] = article["link"] Err gopher.r-36.net 70 i+ elif "file" in article: Err gopher.r-36.net 70 i+ article["id"] = article["file"] Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ article["id"] = article["text"][:30] Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ if article["updated"] == now: Err gopher.r-36.net 70 i+ article["uuid"] = "" Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ article["uuid"] = "%s" % (article["updated"]) Err gopher.r-36.net 70 i+ for e in ("id", "title", "file"): Err gopher.r-36.net 70 i+ if e in article: Err gopher.r-36.net 70 i+ article["uuid"] = "%s-%s" % \ Err gopher.r-36.net 70 i+ (article["uuid"],\ Err gopher.r-36.net 70 i+ article[e]) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def mkuuid(s): Err gopher.r-36.net 70 i+ return hashlib.sha256(str(s).\ Err gopher.r-36.net 70 i+ encode("utf8")).hexdigest() Err gopher.r-36.net 70 i+ if len(article["uuid"]) == 0: Err gopher.r-36.net 70 i+ article["uuid"] = mkuuid(now) Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ article["uuid"] = mkuuid(article["uuid"]) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ # sanity checks Err gopher.r-36.net 70 i+ if "title" not in article and "text" not in article \ Err gopher.r-36.net 70 i+ and "file" not in article: Err gopher.r-36.net 70 i+ continue Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ articles.append(article) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ # Will not process feeds with more than 64 entries. Can you hear me Err gopher.r-36.net 70 i+ # Richard Stallman? Err gopher.r-36.net 70 i+ feed["articles"] = sorted(articles, key=lambda article: \ Err gopher.r-36.net 70 i+ article["updated"])[-64:] 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, timeout=5).read()) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 1diff --git a/zeitungsschau/feeddb.py b/zeitungsschau/feeddb.py /scm/zs//file/zeitungsschau/feeddb.py.gph gopher.r-36.net 70 i@@ -0,0 +1,275 @@ Err gopher.r-36.net 70 i+# Err gopher.r-36.net 70 i+# See LICENSE for licensing details. 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 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+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+ 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+ lockarr = "" Err gopher.r-36.net 70 i+ locks = {} Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def lock(self, fpath): Err gopher.r-36.net 70 i+ if fpath not in self.locks: Err gopher.r-36.net 70 i+ self.lpath = "%s.lck" % (fpath) Err gopher.r-36.net 70 i+ self.locks[fpath] = open(self.lpath, "w") Err gopher.r-36.net 70 i+ fcntl.lockf(self.locks[fpath].fileno(), fcntl.LOCK_EX) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def unlock(self, fpath, doremove=True): Err gopher.r-36.net 70 i+ if fpath in self.locks: Err gopher.r-36.net 70 i+ fcntl.flock(self.locks[fpath].fileno(), fcntl.LOCK_UN) Err gopher.r-36.net 70 i+ self.locks[fpath].close() Err gopher.r-36.net 70 i+ lpath = "%s.lck" % (fpath) Err gopher.r-36.net 70 i+ os.remove(lpath) Err gopher.r-36.net 70 i+ if doremove == True: Err gopher.r-36.net 70 i+ del self.locks[fpath] Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def pickleread(self, fi): Err gopher.r-36.net 70 i+ fpath = "%s/%s" % (self.dbpath, fi) Err gopher.r-36.net 70 i+ path = os.path.abspath(os.path.dirname(fpath)) 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+ Err gopher.r-36.net 70 i+ self.lock(fpath) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ try: Err gopher.r-36.net 70 i+ fd = open(fpath, "rb") Err gopher.r-36.net 70 i+ 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+ db = {} Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ return db Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def picklewrite(self, fi, db): Err gopher.r-36.net 70 i+ fpath = "%s/%s" % (self.dbpath, fi) Err gopher.r-36.net 70 i+ path = os.path.abspath(os.path.dirname(fpath)) 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+ Err gopher.r-36.net 70 i+ fd = open(fpath, "wb+") Err gopher.r-36.net 70 i+ pickle.dump(db, fd) Err gopher.r-36.net 70 i+ fd.close() Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def unlockall(self): Err gopher.r-36.net 70 i+ for key in self.locks.keys(): Err gopher.r-36.net 70 i+ self.unlock(key, doremove=False) Err gopher.r-36.net 70 i+ self.locks = [] Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def __init__(self, path="~/.zs", email=None): Err gopher.r-36.net 70 i+ self.dbpath = os.path.abspath(os.path.expanduser(path)) Err gopher.r-36.net 70 i+ self.feeds = self.pickleread("feeds.db") Err gopher.r-36.net 70 i+ self.cfg = self.pickleread("cfg.db") 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+ if not "smtpstarttls" in self.cfg: Err gopher.r-36.net 70 i+ self.cfg["smtpstarttls"] = "False" Err gopher.r-36.net 70 i+ if not "smtpcmd" in self.cfg: Err gopher.r-36.net 70 i+ self.cfg["smtpcmd"] = None Err gopher.r-36.net 70 i+ if not "smtpuselocal" in self.cfg: Err gopher.r-36.net 70 i+ self.cfg["smtpuselocal"] = "False" Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def sync(self): Err gopher.r-36.net 70 i+ if self.cfg != None: Err gopher.r-36.net 70 i+ self.picklewrite("cfg.db", self.cfg) Err gopher.r-36.net 70 i+ if self.feeds != None: Err gopher.r-36.net 70 i+ self.picklewrite("feeds.db", self.feeds) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def __del__(self): Err gopher.r-36.net 70 i+ self.sync() Err gopher.r-36.net 70 i+ self.unlockall() 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 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+ if key not in feed: 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 setretry(self, uri, retries): Err gopher.r-36.net 70 i+ self.setfeedval(uri, "retry", retries) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def getretry(self, uri): Err gopher.r-36.net 70 i+ retries = self.getfeedval(uri, "retry") Err gopher.r-36.net 70 i+ if retries == None: Err gopher.r-36.net 70 i+ return 0 Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ return retries 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+ self.setretry(uri, 0) 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): 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+ 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.listfeeds(): Err gopher.r-36.net 70 i+ del self.feeds[uri] Err gopher.r-36.net 70 i+ return True Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ return False 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+ a = [art for art in history if art["uuid"] == \ Err gopher.r-36.net 70 i+ article["uuid"]] Err gopher.r-36.net 70 i+ if len(a) == 0: 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+ # Only keep track of the last 2048 articles. Err gopher.r-36.net 70 i+ feed["articles"] = history[-2048:] 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 setarticleunread(self, uri, ids): 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 feed["articles"]: Err gopher.r-36.net 70 i+ a = [art for art in feed["articles"] if art["uuid"] == \ Err gopher.r-36.net 70 i+ ids] 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+ aa["unread"] = True 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 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+ a = [art for art in curfeed["articles"] if art["uuid"] == \ Err gopher.r-36.net 70 i+ article["uuid"]] 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+ 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 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["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 1diff --git a/zeitungsschau/feedemail.py b/zeitungsschau/feedemail.py /scm/zs//file/zeitungsschau/feedemail.py.gph gopher.r-36.net 70 i@@ -0,0 +1,127 @@ Err gopher.r-36.net 70 i+# Err gopher.r-36.net 70 i+# See LICENSE for licensing details. 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+import time Err gopher.r-36.net 70 i+import subprocess Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+import 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+class LocalSendmail(object): Err gopher.r-36.net 70 i+ cmd="/usr/sbin/sendmail -f \"%s\" \"%s\"" Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def __init__(self, cmd=None): Err gopher.r-36.net 70 i+ if cmd != None: Err gopher.r-36.net 70 i+ self.cmd = cmd Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ def sendmail(self, faddr, taddr, msg): Err gopher.r-36.net 70 i+ cmd = self.cmd % (faddr, taddr) Err gopher.r-36.net 70 i+ p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE) Err gopher.r-36.net 70 i+ p.communicate(input=msg.encode("utf8")) 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+ starttls="True", user=None, password=None, smtpcmd=None,\ Err gopher.r-36.net 70 i+ smtpuselocal=False): 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+ h2t = html2text.HTML2Text() Err gopher.r-36.net 70 i+ h2t.body_width = 0 Err gopher.r-36.net 70 i+ h2t.unicode_snob = 1 Err gopher.r-36.net 70 i+ h2t.escape_snob = 1 Err gopher.r-36.net 70 i+ h2t.inline_links = 0 Err gopher.r-36.net 70 i+ h2t.links_each_paragraph = 0 Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ text = "%s\n" % (h2t.handle(article["text"])) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ del h2t 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+ # Larger than 10 MiB, something is wrong. Err gopher.r-36.net 70 i+ if len(text) > 10 * 1024 * 1024: Err gopher.r-36.net 70 i+ continue 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 = "%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+ 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+ if "updated" in article: Err gopher.r-36.net 70 i+ msg["Date"] = formatdate(time.mktime(\ Err gopher.r-36.net 70 i+ article["updated"].timetuple())) Err gopher.r-36.net 70 i+ else: 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+ if smtpuselocal == "True": Err gopher.r-36.net 70 i+ s = LocalSendmail(smtpcmd) Err gopher.r-36.net 70 i+ s.sendmail(faddr, to, msg.as_string()) Err gopher.r-36.net 70 i+ else: 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+ s.ehlo() Err gopher.r-36.net 70 i+ if ssl == "False" and starttls == "True": 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+ 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 i+ s.quit() Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 1diff --git a/zeitungsschau/opml.py b/zeitungsschau/opml.py /scm/zs//file/zeitungsschau/opml.py.gph gopher.r-36.net 70 i@@ -0,0 +1,53 @@ Err gopher.r-36.net 70 i+# Err gopher.r-36.net 70 i+# See LICENSE for licensing details. 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 b/zs /scm/zs//file/zs.gph gopher.r-36.net 70 i@@ -0,0 +1,241 @@ 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 zeitungsschau.feed as feed Err gopher.r-36.net 70 i+import zeitungsschau.feeddb as feeddb Err gopher.r-36.net 70 i+import zeitungsschau.opml as opml Err gopher.r-36.net 70 i+import zeitungsschau.feedemail as feedemail Err gopher.r-36.net 70 i+import urllib.error Err gopher.r-36.net 70 i+import socket Err gopher.r-36.net 70 i+import http.client Err gopher.r-36.net 70 i+import ssl Err gopher.r-36.net 70 i+import getopt Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+dodebug = False Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+def debug(msg): Err gopher.r-36.net 70 i+ global dodebug Err gopher.r-36.net 70 i+ if dodebug == True: Err gopher.r-36.net 70 i+ print("debug: %s" % (msg)) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+def sendfeed(db, ufeed): 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["smtpstarttls"], db.cfg["smtpuser"], \ Err gopher.r-36.net 70 i+ db.cfg["smtppassword"], db.cfg["smtpcmd"], \ Err gopher.r-36.net 70 i+ db.cfg["smtpuselocal"]) Err gopher.r-36.net 70 i+ 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+ Err gopher.r-36.net 70 i+ for feeduri in feeduris: 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+ retries = db.getretry(feeduri) Err gopher.r-36.net 70 i+ estr = None Err gopher.r-36.net 70 i+ print("fetch %s" % (feeduri)) Err gopher.r-36.net 70 i+ curfeed = None 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+ estr = "404" Err gopher.r-36.net 70 i+ retries += 1 Err gopher.r-36.net 70 i+ except socket.gaierror: Err gopher.r-36.net 70 i+ continue Err gopher.r-36.net 70 i+ except socket.timeout: Err gopher.r-36.net 70 i+ continue Err gopher.r-36.net 70 i+ except urllib.error.URLError: Err gopher.r-36.net 70 i+ continue Err gopher.r-36.net 70 i+ except TimeoutError: Err gopher.r-36.net 70 i+ continue Err gopher.r-36.net 70 i+ except ConnectionResetError: Err gopher.r-36.net 70 i+ estr = "connreset" Err gopher.r-36.net 70 i+ retries += 1 Err gopher.r-36.net 70 i+ except http.client.IncompleteRead: Err gopher.r-36.net 70 i+ estr = "incompleteread" Err gopher.r-36.net 70 i+ continue Err gopher.r-36.net 70 i+ except http.client.BadStatusLine: Err gopher.r-36.net 70 i+ estr = "badstatusline" Err gopher.r-36.net 70 i+ continue Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ if curfeed == None: Err gopher.r-36.net 70 i+ continue Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ # retry handling Err gopher.r-36.net 70 i+ if estr != None: Err gopher.r-36.net 70 i+ if retries > 2: Err gopher.r-36.net 70 i+ sys.stderr.write("pause %s %s\n" % \ Err gopher.r-36.net 70 i+ (estr, feeduri)) Err gopher.r-36.net 70 i+ db.pause(feeduri) Err gopher.r-36.net 70 i+ db.setretry(feeduri, retries) Err gopher.r-36.net 70 i+ continue Err gopher.r-36.net 70 i+ elif retries > 0: Err gopher.r-36.net 70 i+ db.setretry(feeduri, 0) 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+ 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+ debug(ufeed) Err gopher.r-36.net 70 i+ if dryrun == False: Err gopher.r-36.net 70 i+ sendfeed(db, ufeed) 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 [-dh] 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+ global dodebug Err gopher.r-36.net 70 i+ retval = 0 Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ try: Err gopher.r-36.net 70 i+ opts, largs = getopt.getopt(args[1:], "hd") Err gopher.r-36.net 70 i+ except getopt.GetoptError as err: Err gopher.r-36.net 70 i+ print(str(err)) Err gopher.r-36.net 70 i+ usage(args[0]) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ for o, a in opts: Err gopher.r-36.net 70 i+ if o == "-h": Err gopher.r-36.net 70 i+ usage(args[0]) Err gopher.r-36.net 70 i+ elif o == "-d": Err gopher.r-36.net 70 i+ dodebug = True Err gopher.r-36.net 70 i+ else: Err gopher.r-36.net 70 i+ usage(args[0]) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ if len(largs) < 1: 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 largs[0] == "run": Err gopher.r-36.net 70 i+ if len(largs) > 1: Err gopher.r-36.net 70 i+ run(db, largs[1]) 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 largs[0] == "dryrun": Err gopher.r-36.net 70 i+ if len(largs) > 1: Err gopher.r-36.net 70 i+ run(db, largs[1], 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 largs[0] == "cfg": Err gopher.r-36.net 70 i+ if len(largs) < 2: 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) < 3: Err gopher.r-36.net 70 i+ if largs[1] in db.cfg: Err gopher.r-36.net 70 i+ print("%s = '%s'" % (largs[1], \ Err gopher.r-36.net 70 i+ db.cfg[largs[1]])) 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[largs[1]] = largs[2] Err gopher.r-36.net 70 i+ print("%s = '%s'" % (largs[1], db.cfg[largs[1]])) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ elif largs[0] == "cfgdel": Err gopher.r-36.net 70 i+ if len(largs) < 2: Err gopher.r-36.net 70 i+ usage(args[0]) Err gopher.r-36.net 70 i+ if largs[1] in db.cfg: Err gopher.r-36.net 70 i+ del db.cfg[largs[1]] Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ elif largs[0] == "add": Err gopher.r-36.net 70 i+ if len(largs) < 2: Err gopher.r-36.net 70 i+ usage(args[0]) Err gopher.r-36.net 70 i+ db.addfeed(largs[1]) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ elif largs[0] == "list": Err gopher.r-36.net 70 i+ print("\n".join(db.listfeeds())) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ elif largs[0] == "listuuids": Err gopher.r-36.net 70 i+ if len(largs) < 2: Err gopher.r-36.net 70 i+ usage(args[0]) Err gopher.r-36.net 70 i+ feed = db.readfeed(largs[1]) Err gopher.r-36.net 70 i+ for art in feed["articles"]: Err gopher.r-36.net 70 i+ print("%s: %s: %s" % (art["uuid"], art["link"],\ Err gopher.r-36.net 70 i+ art["title"])) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ elif largs[0] == "unread": Err gopher.r-36.net 70 i+ if len(largs) < 3: Err gopher.r-36.net 70 i+ usage(args[0]) Err gopher.r-36.net 70 i+ db.setarticleunread(largs[1], largs[2]) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ elif largs[0] == "resend": Err gopher.r-36.net 70 i+ if len(largs) < 2: Err gopher.r-36.net 70 i+ usage(args[0]) Err gopher.r-36.net 70 i+ ufeed = db.unreadarticles(largs[1]) Err gopher.r-36.net 70 i+ sendfeed(db, ufeed) Err gopher.r-36.net 70 i+ db.setreadarticles(largs[1], ufeed) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ elif largs[0] == "del": Err gopher.r-36.net 70 i+ if len(largs) < 2: Err gopher.r-36.net 70 i+ usage(args[0]) Err gopher.r-36.net 70 i+ if db.delfeed(largs[1]) == True: Err gopher.r-36.net 70 i+ print("'%s' has been deleted." % (largs[1])) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ elif largs[0] == "reset": Err gopher.r-36.net 70 i+ if len(largs) < 2: Err gopher.r-36.net 70 i+ usage(args[0]) Err gopher.r-36.net 70 i+ db.resetarticles(largs[1]) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ elif largs[0] == "retry": Err gopher.r-36.net 70 i+ if len(largs) < 3: Err gopher.r-36.net 70 i+ usage(args[0]) Err gopher.r-36.net 70 i+ db.setretry(largs[1], int(largs[2])) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ elif largs[0] == "pause": Err gopher.r-36.net 70 i+ if len(largs) < 2: Err gopher.r-36.net 70 i+ usage(args[0]) Err gopher.r-36.net 70 i+ db.pause(largs[1]) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ elif largs[0] == "unpause": Err gopher.r-36.net 70 i+ if len(largs) < 2: Err gopher.r-36.net 70 i+ usage(args[0]) Err gopher.r-36.net 70 i+ db.unpause(largs[1]) Err gopher.r-36.net 70 i+ Err gopher.r-36.net 70 i+ elif largs[0] == "opmlexport": Err gopher.r-36.net 70 i+ if len(largs) > 1: Err gopher.r-36.net 70 i+ filen = open(largs[1], "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 largs[0] == "opmlimport": Err gopher.r-36.net 70 i+ if len(largs) > 1: Err gopher.r-36.net 70 i+ filen = open(largs[1], "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 1diff --git a/zs.py b/zs.py /scm/zs//file/zs.py.gph gopher.r-36.net 70 i@@ -1,215 +0,0 @@ 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-import urllib.error Err gopher.r-36.net 70 i-import socket Err gopher.r-36.net 70 i-import http.client Err gopher.r-36.net 70 i-import ssl Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i-def sendfeed(db, ufeed): 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- 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- Err gopher.r-36.net 70 i- for feeduri in feeduris: 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- retries = db.getretry(feeduri) Err gopher.r-36.net 70 i- estr = None Err gopher.r-36.net 70 i- print("fetch %s" % (feeduri)) Err gopher.r-36.net 70 i- curfeed = None 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- estr = "404" Err gopher.r-36.net 70 i- retries += 1 Err gopher.r-36.net 70 i- except socket.gaierror: Err gopher.r-36.net 70 i- continue Err gopher.r-36.net 70 i- except socket.timeout: Err gopher.r-36.net 70 i- continue Err gopher.r-36.net 70 i- except urllib.error.URLError: Err gopher.r-36.net 70 i- continue Err gopher.r-36.net 70 i- except TimeoutError: Err gopher.r-36.net 70 i- continue Err gopher.r-36.net 70 i- except ConnectionResetError: Err gopher.r-36.net 70 i- estr = "connreset" Err gopher.r-36.net 70 i- retries += 1 Err gopher.r-36.net 70 i- except http.client.IncompleteRead: Err gopher.r-36.net 70 i- estr = "incompleteread" Err gopher.r-36.net 70 i- continue Err gopher.r-36.net 70 i- except http.client.BadStatusLine: Err gopher.r-36.net 70 i- estr = "badstatusline" Err gopher.r-36.net 70 i- continue Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i- if curfeed == None: Err gopher.r-36.net 70 i- continue Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i- # retry handling Err gopher.r-36.net 70 i- if estr != None: Err gopher.r-36.net 70 i- if retries > 2: Err gopher.r-36.net 70 i- sys.stderr.write("pause %s %s\n" % \ Err gopher.r-36.net 70 i- (estr, feeduri)) Err gopher.r-36.net 70 i- db.pause(feeduri) Err gopher.r-36.net 70 i- db.setretry(feeduri, retries) Err gopher.r-36.net 70 i- continue Err gopher.r-36.net 70 i- elif retries > 0: Err gopher.r-36.net 70 i- db.setretry(feeduri, 0) 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- 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- if dryrun == False: Err gopher.r-36.net 70 i- sendfeed(db, ufeed) 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] == "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- 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] == "cfgdel": 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- if args[2] in db.cfg: Err gopher.r-36.net 70 i- del 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- db.addfeed(args[2]) 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- print("\n".join(db.listfeeds())) Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i- elif args[1] == "listuuids": 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- feed = db.readfeed(args[2]) Err gopher.r-36.net 70 i- for art in feed["articles"]: Err gopher.r-36.net 70 i- print("%s: %s: %s" % (art["uuid"], art["link"],\ Err gopher.r-36.net 70 i- art["title"])) Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i- elif args[1] == "unread": Err gopher.r-36.net 70 i- if len(args) < 4: Err gopher.r-36.net 70 i- usage(args[0]) Err gopher.r-36.net 70 i- db.setarticleunread(args[2], args[3]) Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i- elif args[1] == "resend": 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- ufeed = db.unreadarticles(args[2]) Err gopher.r-36.net 70 i- sendfeed(db, ufeed) Err gopher.r-36.net 70 i- db.setreadarticles(args[2], ufeed) Err gopher.r-36.net 70 i- 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- if db.delfeed(args[2]) == True: Err gopher.r-36.net 70 i- print("'%s' has been deleted." % (args[2])) Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i- elif args[1] == "reset": 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.resetarticles(args[1]) Err gopher.r-36.net 70 i- Err gopher.r-36.net 70 i- elif args[1] == "retry": Err gopher.r-36.net 70 i- if len(args) < 4: Err gopher.r-36.net 70 i- usage(args[0]) Err gopher.r-36.net 70 i- db.setretry(args[1], int(args[2])) 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 .