With small changes, comes great change
Introduced asynchronous plugins that gather their data on different intervals than the mainloop. Here it is used for the modsde plugin. The statushandler has a new class Module, which acts as documentation for the API These changes let the output evenly flow, even if an async plugin hangs due to network problems or similiar issues.
This commit is contained in:
parent
80d7184e14
commit
db94df07a3
98
modsde.py
98
modsde.py
@ -2,75 +2,84 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import json
|
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 urllib.request, urllib.parse, urllib.error, urllib.request, urllib.error, urllib.parse
|
||||||
import re
|
import re
|
||||||
import http.cookiejar
|
import http.cookiejar
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
class LoginError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
class ModsDeChecker(object):
|
class ModsDeChecker(object):
|
||||||
"""
|
"""
|
||||||
This class returns i3status parsable output of the number of
|
This class returns i3status parsable output of the number of
|
||||||
unread posts in any bookmark in the mods.de forums.
|
unread posts in any bookmark in the mods.de forums.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
last_checked = datetime.now()
|
async = True
|
||||||
unread_cache = 0
|
output = None
|
||||||
login_url = 'http://login.mods.de/'
|
|
||||||
|
login_url = "http://login.mods.de/"
|
||||||
bookmark_url = "http://forum.mods.de/bb/xml/bookmarks.php"
|
bookmark_url = "http://forum.mods.de/bb/xml/bookmarks.php"
|
||||||
opener = None
|
opener = None
|
||||||
cj = None
|
cj = None
|
||||||
logged_in = False
|
logged_in = False
|
||||||
|
|
||||||
settings = {
|
settings = {
|
||||||
'color': '#7181fe',
|
"color": "#7181fe",
|
||||||
'pause': 20,
|
"pause": 20,
|
||||||
'username': "",
|
"username": "",
|
||||||
'password': ""
|
"password": "",
|
||||||
|
"offset": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, settings = None):
|
def __init__(self, settings = None):
|
||||||
self.settings.update(settings)
|
self.settings.update(settings)
|
||||||
self.cj = http.cookiejar.CookieJar()
|
self.cj = http.cookiejar.CookieJar()
|
||||||
self.last_checked = \
|
self.opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(self.cj))
|
||||||
datetime.now() - timedelta(seconds=self.settings['pause'])
|
|
||||||
self.opener = urllib.request.build_opener(
|
self.thread = threading.Thread(target=self.mainloop)
|
||||||
urllib.request.HTTPCookieProcessor(self.cj))
|
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):
|
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):
|
def login(self):
|
||||||
|
|
||||||
data = urllib.parse.urlencode({
|
data = urllib.parse.urlencode({
|
||||||
"login_username": self.settings["username"],
|
"login_username": self.settings["username"],
|
||||||
"login_password": self.settings["password"],
|
"login_password": self.settings["password"],
|
||||||
"login_lifetime": "31536000"
|
"login_lifetime": "31536000"
|
||||||
})
|
})
|
||||||
|
|
||||||
response = self.opener.open(self.login_url, data)
|
response = self.opener.open(self.login_url, data.encode("ascii"))
|
||||||
m = re.search("http://forum.mods.de/SSO.php[^']*", response.read())
|
m = re.search("http://forum.mods.de/SSO.php[^']*", response.read().decode("ISO-8859-15"))
|
||||||
self.cj.clear()
|
self.cj.clear()
|
||||||
|
|
||||||
if m and m.group(0):
|
if m and m.group(0):
|
||||||
@ -79,20 +88,5 @@ class ModsDeChecker(object):
|
|||||||
for cookie in self.cj:
|
for cookie in self.cj:
|
||||||
self.cj.clear
|
self.cj.clear
|
||||||
self.logged_in = True
|
self.logged_in = True
|
||||||
self.opener.addheaders.append(('Cookie',
|
self.opener.addheaders.append(('Cookie', '{}={}'.format(cookie.name, cookie.value)))
|
||||||
'{}={}'.format(cookie.name, cookie.value)))
|
|
||||||
return True
|
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']}
|
|
||||||
|
@ -4,6 +4,16 @@ import sys
|
|||||||
import json
|
import json
|
||||||
import urllib.request, urllib.error, urllib.parse
|
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):
|
class I3statusHandler(object):
|
||||||
modules = []
|
modules = []
|
||||||
|
|
||||||
@ -11,24 +21,24 @@ class I3statusHandler(object):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def register_module(self, module):
|
def register_module(self, module):
|
||||||
""" Register a new module. """
|
"""Register a new module."""
|
||||||
|
|
||||||
# check if module implemented the
|
# check if module implemented the
|
||||||
# correct functions
|
# correct functions
|
||||||
if not hasattr(module, 'output'):
|
#if not hasattr(module, 'output'):
|
||||||
raise Exception("Module %s does not implement \
|
# raise Exception("Module %s does not implement \
|
||||||
all the needed functions!".format(module))
|
# all the needed functions!".format(module))
|
||||||
|
|
||||||
self.modules.append(module)
|
self.modules.append(module)
|
||||||
|
|
||||||
def print_line(self, message):
|
def print_line(self, message):
|
||||||
""" Non-buffered printing to stdout. """
|
"""Unbuffered printing to stdout."""
|
||||||
|
|
||||||
sys.stdout.write(message + '\n')
|
sys.stdout.write(message + '\n')
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
||||||
def read_line(self):
|
def read_line(self):
|
||||||
""" Interrupted respecting reader for stdin. """
|
"""Interrupted respecting reader for stdin."""
|
||||||
|
|
||||||
# try reading a line, removing any extra whitespace
|
# try reading a line, removing any extra whitespace
|
||||||
try:
|
try:
|
||||||
@ -52,13 +62,16 @@ class I3statusHandler(object):
|
|||||||
if line.startswith(','):
|
if line.startswith(','):
|
||||||
line, prefix = line[1:], ','
|
line, prefix = line[1:], ','
|
||||||
|
|
||||||
j = json.loads(line)
|
j = [] #json.loads(line)
|
||||||
|
|
||||||
for module in self.modules:
|
for module in self.modules:
|
||||||
output = module.output()
|
if not module.async:
|
||||||
|
module.tick()
|
||||||
|
|
||||||
|
output = module.output
|
||||||
|
|
||||||
if output:
|
if output:
|
||||||
j.insert(0, module.output())
|
j.insert(0, output)
|
||||||
|
|
||||||
# and echo back new encoded json
|
# and echo back new encoded json
|
||||||
self.print_line(prefix+json.dumps(j))
|
self.print_line(prefix+json.dumps(j))
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
import dbus, gobject
|
import dbus, gobject
|
||||||
from dbus.mainloop.glib import DBusGMainLoop
|
from dbus.mainloop.glib import DBusGMainLoop
|
||||||
import json
|
import json
|
||||||
|
import threading
|
||||||
|
|
||||||
class ThunderbirdMailChecker(object):
|
class ThunderbirdMailChecker(object):
|
||||||
"""
|
"""
|
||||||
@ -17,7 +18,10 @@ class ThunderbirdMailChecker(object):
|
|||||||
the dbus-sender extension for thunderbird.
|
the dbus-sender extension for thunderbird.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
unread = []
|
async = False
|
||||||
|
output = None
|
||||||
|
|
||||||
|
unread = set()
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
|
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
|
||||||
@ -32,20 +36,28 @@ class ThunderbirdMailChecker(object):
|
|||||||
dbus.mainloop.glib.threads_init()
|
dbus.mainloop.glib.threads_init()
|
||||||
self.context = loop.get_context()
|
self.context = loop.get_context()
|
||||||
|
|
||||||
|
def tick(self):
|
||||||
|
self.context.iteration(False)
|
||||||
|
|
||||||
def new_msg(self, id, author, subject):
|
def new_msg(self, id, author, subject):
|
||||||
if id not in self.unread:
|
if id not in self.unread:
|
||||||
self.unread.append(id)
|
self.unread.add(id)
|
||||||
|
self._output()
|
||||||
|
|
||||||
def changed_msg(self, id, event):
|
def changed_msg(self, id, event):
|
||||||
if event == "read" and id in self.unread:
|
if event == "read" and id in self.unread:
|
||||||
self.unread.remove(id)
|
self.unread.remove(id)
|
||||||
|
self._output()
|
||||||
|
|
||||||
def output(self):
|
def _output(self):
|
||||||
self.context.iteration(False)
|
self.context.iteration(False)
|
||||||
|
|
||||||
unread = len(self.unread)
|
unread = len(self.unread)
|
||||||
|
if unread:
|
||||||
return {'full_text' : '%d new email' % unread,
|
self.output = {'full_text' : '%d new email' % unread,
|
||||||
'name' : 'newmail-tb',
|
'name' : 'newmail-tb',
|
||||||
'urgent' : True,
|
'urgent' : True,
|
||||||
'color' : '#ff0000' } if unread else None
|
'color' : '#ff0000' }
|
||||||
|
else:
|
||||||
|
self.output = None
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user