[600e09c] | 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) |
---|