diff --git a/modsde.py b/modsde.py index bb06ae1..1cdaa25 100644 --- a/modsde.py +++ b/modsde.py @@ -2,75 +2,84 @@ import sys import json -from datetime import datetime,timedelta +import time +import threading import urllib.request, urllib.parse, urllib.error, urllib.request, urllib.error, urllib.parse import re import http.cookiejar import xml.etree.ElementTree as ET +class LoginError(Exception): + pass + class ModsDeChecker(object): """ This class returns i3status parsable output of the number of unread posts in any bookmark in the mods.de forums. """ - last_checked = datetime.now() - unread_cache = 0 - login_url = 'http://login.mods.de/' + async = True + output = None + + login_url = "http://login.mods.de/" bookmark_url = "http://forum.mods.de/bb/xml/bookmarks.php" opener = None cj = None logged_in = False settings = { - 'color': '#7181fe', - 'pause': 20, - 'username': "", - 'password': "" + "color": "#7181fe", + "pause": 20, + "username": "", + "password": "", + "offset": 0, } def __init__(self, settings = None): self.settings.update(settings) self.cj = http.cookiejar.CookieJar() - self.last_checked = \ - datetime.now() - timedelta(seconds=self.settings['pause']) - self.opener = urllib.request.build_opener( - urllib.request.HTTPCookieProcessor(self.cj)) + self.opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(self.cj)) + + self.thread = threading.Thread(target=self.mainloop) + self.thread.daemon = True + self.thread.start() + + def mainloop(self): + while True: + unread = self.get_unread_count() + + if not unread: + self.output = None + else: + self.output = {"full_text" : "%d new posts in bookmarks" % unread, + "name" : "modsde", + "urgent" : "true", + "color" : self.settings["color"]} + + time.sleep(self.settings["pause"]) def get_unread_count(self): - delta = datetime.now() - self.last_checked + if not self.logged_in: + self.login() - if delta.total_seconds() > self.settings['pause']: - if not self.logged_in: - try: - self.login() - except Exception: - pass - - try: - f = self.opener.open(self.bookmark_url) - root = ET.fromstring(f.read()) - self.last_checked = datetime.now() - self.unread_cache = int(root.attrib['newposts']) - except Exception: - self.cj.clear() - self.opener = urllib.request.build_opener( - urllib.request.HTTPCookieProcessor(self.cj)) - self.logged_in = False - - return self.unread_cache - + try: + f = self.opener.open(self.bookmark_url) + root = ET.fromstring(f.read()) + return int(root.attrib["newposts"]) - self.settings["offset"] + except Exception: + self.cj.clear() + self.opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(self.cj)) + self.logged_in = False def login(self): - data = urllib.parse.urlencode({ "login_username": self.settings["username"], "login_password": self.settings["password"], "login_lifetime": "31536000" }) - response = self.opener.open(self.login_url, data) - m = re.search("http://forum.mods.de/SSO.php[^']*", response.read()) + response = self.opener.open(self.login_url, data.encode("ascii")) + m = re.search("http://forum.mods.de/SSO.php[^']*", response.read().decode("ISO-8859-15")) self.cj.clear() if m and m.group(0): @@ -79,20 +88,5 @@ class ModsDeChecker(object): for cookie in self.cj: self.cj.clear self.logged_in = True - self.opener.addheaders.append(('Cookie', - '{}={}'.format(cookie.name, cookie.value))) + self.opener.addheaders.append(('Cookie', '{}={}'.format(cookie.name, cookie.value))) return True - - return False - - def output(self): - - unread = self.get_unread_count() - - if not unread: - return None - - return {'full_text' : '%d new posts in bookmarks' % unread, - 'name' : 'modsde', - 'urgent' : 'true', - 'color' : self.settings['color']} diff --git a/statushandler.py b/statushandler.py index d97bec3..4b2e53f 100644 --- a/statushandler.py +++ b/statushandler.py @@ -4,6 +4,16 @@ import sys import json import urllib.request, urllib.error, urllib.parse +class Module(object): + output = None + async = False + + def registered(self, status_handler): + """Called when this module is registered with a status handler""" + + def run(self): + """Only called if self.async == False. Called once per tick""" + class I3statusHandler(object): modules = [] @@ -11,24 +21,24 @@ class I3statusHandler(object): pass def register_module(self, module): - """ Register a new module. """ + """Register a new module.""" # check if module implemented the # correct functions - if not hasattr(module, 'output'): - raise Exception("Module %s does not implement \ - all the needed functions!".format(module)) + #if not hasattr(module, 'output'): + # raise Exception("Module %s does not implement \ + # all the needed functions!".format(module)) self.modules.append(module) def print_line(self, message): - """ Non-buffered printing to stdout. """ + """Unbuffered printing to stdout.""" sys.stdout.write(message + '\n') sys.stdout.flush() def read_line(self): - """ Interrupted respecting reader for stdin. """ + """Interrupted respecting reader for stdin.""" # try reading a line, removing any extra whitespace try: @@ -52,13 +62,16 @@ class I3statusHandler(object): if line.startswith(','): line, prefix = line[1:], ',' - j = json.loads(line) + j = [] #json.loads(line) for module in self.modules: - output = module.output() + if not module.async: + module.tick() + + output = module.output if output: - j.insert(0, module.output()) + j.insert(0, output) # and echo back new encoded json self.print_line(prefix+json.dumps(j)) diff --git a/thunderbirdnewmail.py b/thunderbirdnewmail.py index c92ea81..96e5d53 100644 --- a/thunderbirdnewmail.py +++ b/thunderbirdnewmail.py @@ -10,6 +10,7 @@ import dbus, gobject from dbus.mainloop.glib import DBusGMainLoop import json +import threading class ThunderbirdMailChecker(object): """ @@ -17,7 +18,10 @@ class ThunderbirdMailChecker(object): the dbus-sender extension for thunderbird. """ - unread = [] + async = False + output = None + + unread = set() def __init__(self): dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) @@ -32,20 +36,28 @@ class ThunderbirdMailChecker(object): dbus.mainloop.glib.threads_init() self.context = loop.get_context() + def tick(self): + self.context.iteration(False) + def new_msg(self, id, author, subject): if id not in self.unread: - self.unread.append(id) + self.unread.add(id) + self._output() def changed_msg(self, id, event): if event == "read" and id in self.unread: self.unread.remove(id) + self._output() - def output(self): + def _output(self): self.context.iteration(False) unread = len(self.unread) - - return {'full_text' : '%d new email' % unread, + if unread: + self.output = {'full_text' : '%d new email' % unread, 'name' : 'newmail-tb', 'urgent' : True, - 'color' : '#ff0000' } if unread else None \ No newline at end of file + 'color' : '#ff0000' } + else: + self.output = None +