|
|
feeddb.py - zs - Zeitungsschau rss to email converter |
|
|
 |
git clone git://r-36.net/zs (git://r-36.net) |
|
|
 |
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 |
|