1 | #!/usr/bin/python |
---|
2 | |
---|
3 | import operator |
---|
4 | import re |
---|
5 | |
---|
6 | import requests |
---|
7 | |
---|
8 | import implugin |
---|
9 | |
---|
10 | class NewsBlurPlugin(implugin.BitlBeeIMPlugin): |
---|
11 | NAME = "newsblur" |
---|
12 | ACCOUNT_FLAGS = 0 |
---|
13 | SETTINGS = { |
---|
14 | "backlog": { |
---|
15 | "default": 20, |
---|
16 | "type": "int", |
---|
17 | }, |
---|
18 | } |
---|
19 | |
---|
20 | BASE_URL = "https://newsblur.com" |
---|
21 | |
---|
22 | def url(self, path): |
---|
23 | return (self.BASE_URL + path) |
---|
24 | |
---|
25 | def login(self, account): |
---|
26 | super(NewsBlurPlugin, self).login(account) |
---|
27 | self.ua = requests.Session() |
---|
28 | creds = {"username": account["user"], "password": account["pass"]} |
---|
29 | r = self.ua.post(self.url("/api/login"), creds) |
---|
30 | self.bee.log("You're running BitlBee %d.%d.%d" % self.bitlbee_version) |
---|
31 | if r.status_code != 200: |
---|
32 | self.bee.error("HTTP error %d" % r.status_code) |
---|
33 | self.bee.logout(True) |
---|
34 | elif r.json()["errors"]: |
---|
35 | self.bee.error("Authentication error") |
---|
36 | self.bee.logout(False) |
---|
37 | else: |
---|
38 | self.bee.add_buddy("rss", None) |
---|
39 | self.bee.connected() |
---|
40 | self.seen_hashes = set() |
---|
41 | self.keepalive() |
---|
42 | |
---|
43 | def logout(self): |
---|
44 | self.bee.log("Ok bye!") |
---|
45 | |
---|
46 | def buddy_msg(self, handle, msg, flags): |
---|
47 | feed = self.feeds[handle] |
---|
48 | cmd = re.split(r"\s+", msg) |
---|
49 | |
---|
50 | def set_set_backlog(self, setting, value): |
---|
51 | print "Setting %s changed to %r" % (setting, value) |
---|
52 | |
---|
53 | # BitlBee will call us here every minute which is actually a neat way |
---|
54 | # to get periodic work (like RSS polling) scheduled. :-D |
---|
55 | def keepalive(self): |
---|
56 | r = self.ua.post( |
---|
57 | self.url("/reader/unread_story_hashes"), |
---|
58 | {"include_timestamps": True}) |
---|
59 | if r.status_code != 200: |
---|
60 | self.bee.error("HTTP error %d" % r.status_code) |
---|
61 | return |
---|
62 | |
---|
63 | # Throw all unread-post hashes in a flat list and sort it by posting time. |
---|
64 | feed_hashes = r.json()["unread_feed_story_hashes"] |
---|
65 | all_hashes = [] |
---|
66 | for feed, hashes in feed_hashes.iteritems(): |
---|
67 | all_hashes += [tuple(h) for h in hashes] |
---|
68 | all_hashes.sort(key=operator.itemgetter(1)) |
---|
69 | |
---|
70 | # Look at the most recent posts, grab the ones we haven't shown yet. |
---|
71 | req_hashes = [] |
---|
72 | for hash, _ in all_hashes[-self.setting("backlog"):]: |
---|
73 | if hash not in self.seen_hashes: |
---|
74 | req_hashes.append(hash) |
---|
75 | |
---|
76 | if not req_hashes: |
---|
77 | return |
---|
78 | |
---|
79 | # Grab post details. |
---|
80 | r = self.ua.post(self.url("/reader/river_stories"), {"h": req_hashes}) |
---|
81 | if r.status_code != 200: |
---|
82 | self.bee.error("HTTP error %d" % r.status_code) |
---|
83 | return |
---|
84 | |
---|
85 | # Response is not in the order we requested. :-( Make it a hash |
---|
86 | # and reconstruct order from our request. |
---|
87 | stories = {s["story_hash"]: s for s in r.json()["stories"]} |
---|
88 | for s in (stories[hash] for hash in req_hashes): |
---|
89 | line = "%(story_title)s <%(story_permalink)s>" % s |
---|
90 | ts = int(s.get("story_timestamp", "0")) |
---|
91 | self.bee.buddy_msg("rss", line, 0, ts) |
---|
92 | self.seen_hashes.add(s["story_hash"]) |
---|
93 | |
---|
94 | |
---|
95 | implugin.RunPlugin(NewsBlurPlugin, debug=True) |
---|