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