zs

Zeitungsschau rss to email converter
git clone git://r-36.net/zs
Log | Files | Refs | README | LICENSE

feeddb.py (6332B)


      1 #
      2 # See LICENSE for licensing details.
      3 #
      4 # Copy me if you can.
      5 # by 20h
      6 #
      7 
      8 import pickle
      9 import os
     10 import os.path
     11 import fcntl
     12 from subprocess import Popen
     13 
     14 class feeddb(object):
     15 	feeds = {}
     16 	cfg = {}
     17 	dbpath = ""
     18 	lpath = ""
     19 	lockarr = ""
     20 	locks = {}
     21 	default = True
     22 
     23 	def lock(self, fpath):
     24 		if fpath not in self.locks:
     25 			self.lpath = "%s.lck" % (fpath)
     26 			self.locks[fpath] = open(self.lpath, "w")
     27 			fcntl.lockf(self.locks[fpath].fileno(), fcntl.LOCK_EX)
     28 
     29 	def unlock(self, fpath, doremove=True):
     30 		if fpath in self.locks:
     31 			fcntl.flock(self.locks[fpath].fileno(), fcntl.LOCK_UN)
     32 			self.locks[fpath].close()
     33 			lpath = "%s.lck" % (fpath)
     34 			try:
     35 				os.remove(lpath)
     36 			except FileNotFoundError:
     37 				pass
     38 			if doremove == True:
     39 				del self.locks[fpath]
     40 
     41 	def pickleread(self, fi):
     42 		fpath = "%s/%s" % (self.dbpath, fi)
     43 		path = os.path.abspath(os.path.dirname(fpath))
     44 		if not os.path.exists(path):
     45 			os.makedirs(path, 0o750)
     46 
     47 		self.lock(fpath)
     48 
     49 		try:
     50 			fd = open(fpath, "rb")
     51 			db = pickle.load(fd)
     52 			fd.close()
     53 		except FileNotFoundError:
     54 			db = {}
     55 
     56 		return db
     57 
     58 	def picklewrite(self, fi, db):
     59 		fpath = "%s/%s" % (self.dbpath, fi)
     60 		path = os.path.abspath(os.path.dirname(fpath))
     61 		if not os.path.exists(path):
     62 			os.makedirs(path, 0o750)
     63 
     64 		fd = open(fpath, "wb+")
     65 		pickle.dump(db, fd)
     66 		fd.close()
     67 	
     68 	def unlockall(self):
     69 		for key in self.locks.keys():
     70 			self.unlock(key, doremove=False)
     71 		self.locks = []
     72 
     73 	def __init__(self, path="~/.zs", email=None):
     74 		self.dbpath = os.path.abspath(os.path.expanduser(path))
     75 		self.feeds = self.pickleread("feeds.db")
     76 		self.cfg = self.pickleread("cfg.db")
     77 
     78 		if not "email" in self.cfg:
     79 			print("You need to specify the default email. Please "\
     80 					"run 'zs cfg email me@me.com' to "\
     81 					"set it.")
     82 		else:
     83 			self.default = False
     84 
     85 		if not "smtphost" in self.cfg:
     86 			self.cfg["smtphost"] = "localhost"
     87 		if not "smtpport" in self.cfg:
     88 			self.cfg["smtpport"] = None
     89 		if not "smtpssl" in self.cfg:
     90 			self.cfg["smtpssl"] = "False"
     91 		if not "smtpuser" in self.cfg:
     92 			self.cfg["smtpuser"] = None
     93 		if not "smtppassword" in self.cfg:
     94 			self.cfg["smtppassword"] = None
     95 		if not "smtpstarttls" in self.cfg:
     96 			self.cfg["smtpstarttls"] = "False"
     97 		if not "smtpcmd" in self.cfg:
     98 			self.cfg["smtpcmd"] = None
     99 		if not "smtpuselocal" in self.cfg:
    100 			self.cfg["smtpuselocal"] = "False"
    101 
    102 	def sync(self):
    103 		if self.cfg != None and self.default == False:
    104 			self.picklewrite("cfg.db", self.cfg)
    105 		if self.feeds != None and self.default == False:
    106 			self.picklewrite("feeds.db", self.feeds)
    107 
    108 	def __del__(self):
    109 		self.sync()
    110 		self.unlockall()
    111 
    112 	def readfeed(self, uri):
    113 		if not uri in self.feeds:
    114 			return None
    115 		return self.feeds[uri]
    116 
    117 	def writefeed(self, uri, feed):
    118 		self.feeds[uri] = feed
    119 		self.default = False
    120 
    121 	def sethook(self, uri, hookfile):
    122 		feed = self.readfeed(uri)
    123 		if feed == None:
    124 			return
    125 		feed["hook"] = hookfile
    126 		self.writefeed(uri, feed)
    127 
    128 	def runhook(self, uri):
    129 		feed = self.readfeed(uri)
    130 		if feed == None:
    131 			return
    132 		if not "hook" in feed:
    133 			return
    134 
    135 		cmd = os.path.expanduser(feed["hook"])
    136 		if not os.path.exists(cmd):
    137 			return
    138 
    139 		fd = open("/dev/null")
    140 		if os.fork() == 0:
    141 			p = Popen(cmd, shell=True, stdout=fd, stderr=fd)
    142 			p.wait()
    143 
    144 	def setfeedval(self, uri, key, value):
    145 		feed = self.readfeed(uri)
    146 		if feed == None:
    147 			return
    148 		feed[key] = value
    149 		self.writefeed(uri, feed)
    150 
    151 	def getfeedval(self, uri, key):
    152 		feed = self.readfeed(uri)
    153 		if feed == None:
    154 			return None
    155 		if key not in feed:
    156 			return None
    157 		return feed[key]
    158 
    159 	def setretry(self, uri, retries):
    160 		self.setfeedval(uri, "retry", retries)
    161 
    162 	def getretry(self, uri):
    163 		retries = self.getfeedval(uri, "retry")
    164 		if retries == None:
    165 			return 0
    166 		else:
    167 			return retries
    168 	
    169 	def pause(self, uri):
    170 		self.setfeedval(uri, "pause", True)
    171 
    172 	def unpause(self, uri):
    173 		self.setfeedval(uri, "pause", False)
    174 		self.setretry(uri, 0)
    175 
    176 	def ispaused(self, uri):
    177 		return self.getfeedval(uri, "pause")
    178 
    179 	def listfeeds(self):
    180 		return list(self.feeds.keys())
    181 
    182 	def addfeed(self, uri):
    183 		if not uri in self.listfeeds():
    184 			feed = {}
    185 			feed["uri"] = uri
    186 			feed["pause"] = False
    187 			feed["articles"] = []
    188 			self.writefeed(uri, feed)
    189 			self.default = False
    190 
    191 	def delfeed(self, uri):
    192 		if uri in self.listfeeds():
    193 			del self.feeds[uri]
    194 			return True
    195 		else:
    196 			return False
    197 
    198 	def listactivefeeds(self):
    199 		rfeeds = []
    200 		for f in self.feeds:
    201 			if self.feeds[f]["pause"] == False:
    202 				rfeeds.append(f)
    203 		return rfeeds
    204 
    205 	def mergefeed(self, uri, curfeed):
    206 		rarticles = []
    207 		feed = self.readfeed(uri)
    208 		if feed == None:
    209 			return curfeed
    210 
    211 		history = feed["articles"]
    212 		for article in curfeed["articles"]:
    213 			a = [art for art in history if art["uuid"] == \
    214 				article["uuid"]]
    215 			if len(a) == 0:
    216 				article["unread"] = True
    217 				history.append(article)
    218 				rarticles.append(article)
    219 		# Only keep track of the last 2048 articles.
    220 		feed["articles"] = history[-2048:]
    221 
    222 		for metakey in ("link", "title", "updated", "author", \
    223 				"email", "feeduri"):
    224 			if metakey in curfeed:
    225 				feed[metakey] = curfeed[metakey]
    226 
    227 		self.writefeed(uri, feed)
    228 		curfeed["articles"] = rarticles
    229 
    230 		return curfeed
    231 
    232 	def unreadarticles(self, uri):
    233 		rfeed = {}
    234 		rfeed["articles"] = []
    235 		feed = self.readfeed(uri)
    236 		if feed == None:
    237 			return rfeed
    238 
    239 		for metakey in ("link", "title", "updated", "author", \
    240 				"email", "toemail", "feeduri"):
    241 			if metakey in feed:
    242 				rfeed[metakey] = feed[metakey]
    243 
    244 		history = feed["articles"]
    245 		for article in history:
    246 			if article["unread"] == True:
    247 				rfeed["articles"].append(article)
    248 
    249 		return rfeed
    250 
    251 	def setarticleunread(self, uri, ids):
    252 		feed = self.readfeed(uri)
    253 		if feed == None:
    254 			return
    255 
    256 		for article in feed["articles"]:
    257 			a = [art for art in feed["articles"] if art["uuid"] == \
    258 				ids]
    259 			if len(a) > 0:
    260 				for aa in a:
    261 					aa["unread"] = True
    262 		self.writefeed(uri, feed);
    263 
    264 	def setreadarticles(self, uri, curfeed=None):
    265 		feed = self.readfeed(uri)
    266 		if feed == None:
    267 			return
    268 
    269 		for article in curfeed["articles"]:
    270 			a = [art for art in curfeed["articles"] if art["uuid"] == \
    271 				article["uuid"]]
    272 			if len(a) > 0:
    273 				for aa in a:
    274 					aa["unread"] = False
    275 		self.writefeed(uri, feed);
    276 
    277 	def resetarticles(self, uri):
    278 		feed = self.readfeed(uri)
    279 		if feed == None:
    280 			return
    281 		feed["articles"] = []
    282 		self.writefeed(uri, feed)
    283