summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugins/screen_detach.py45
-rw-r--r--plugins/test.py12
-rw-r--r--src/core.py27
-rw-r--r--src/plugin.py33
-rw-r--r--src/plugin_manager.py25
5 files changed, 142 insertions, 0 deletions
diff --git a/plugins/screen_detach.py b/plugins/screen_detach.py
new file mode 100644
index 00000000..6ebc77f2
--- /dev/null
+++ b/plugins/screen_detach.py
@@ -0,0 +1,45 @@
+import os
+import stat
+import pyinotify
+
+SCREEN_DIR = '/var/run/screen/S-%s' % (os.getlogin(),)
+
+class Plugin(BasePlugin):
+ def init(self):
+ self.timed_event = None
+ sock_path = None
+ self.thread = None
+ for f in os.listdir(SCREEN_DIR):
+ path = os.path.join(SCREEN_DIR, f)
+ if screen_attached(path):
+ sock_path = path
+ self.attached = True
+ break
+
+ # Only actually do something if we found an attached screen (assuming only one)
+ if sock_path:
+ wm = pyinotify.WatchManager()
+ wm.add_watch(sock_path, pyinotify.EventsCodes.ALL_FLAGS['IN_ATTRIB'])
+ self.thread = pyinotify.ThreadedNotifier(wm, default_proc_fun=HandleScreen(plugin=self))
+ self.thread.start()
+
+ def cleanup(self):
+ if self.thread:
+ self.thread.stop()
+
+ def update_screen_state(self, socket):
+ attached = screen_attached(socket)
+ if attached != self.attached:
+ self.attached = attached
+ status = 'available' if self.attached else 'away'
+ self.core.command_status(status)
+
+def screen_attached(socket):
+ return (os.stat(socket).st_mode & stat.S_IXUSR) != 0
+
+class HandleScreen(pyinotify.ProcessEvent):
+ def my_init(self, **kwargs):
+ self.plugin = kwargs['plugin']
+
+ def process_IN_ATTRIB(self, event):
+ self.plugin.update_screen_state(event.path)
diff --git a/plugins/test.py b/plugins/test.py
new file mode 100644
index 00000000..fc7aedc6
--- /dev/null
+++ b/plugins/test.py
@@ -0,0 +1,12 @@
+class Plugin(BasePlugin):
+ def init(self):
+ self.core.information("Plugin loaded")
+
+ def cleanup(self):
+ self.core.information("Plugin unloaded")
+
+ def on_message(self, message):
+ self.core.information("Test plugin received message: {}".format(message))
+
+ def command_plugintest(self, args):
+ self.core.information("Command! With args {}".format(args))
diff --git a/src/core.py b/src/core.py
index 3cdc7592..d644c19b 100644
--- a/src/core.py
+++ b/src/core.py
@@ -36,6 +36,8 @@ import windows
import connection
import timed_events
+from plugin_manager import PluginManager
+
from data_forms import DataFormsTab
from config import config, options
from logger import logger
@@ -125,6 +127,8 @@ class Core(object):
'server_cycle': (self.command_server_cycle, _('Usage: /server_cycle [domain] [message]\nServer Cycle: disconnect and reconnects in all the rooms in domain.'), None),
'bind': (self.command_bind, _('Usage: /bind <key> <equ>\nBind: bind a key to an other key or to a “command”. For example "/bind ^H KEY_UP" makes Control + h do the same same than the Up key.'), None),
'pubsub': (self.command_pubsub, _('Usage: /pubsub <domain>\nPubsub: Open a pubsub browser on the given domain'), None),
+ 'load': (self.command_load, _('Usage: /load <script.py>\nLoad: Load the specified python script'), None),
+ 'unload': (self.command_unload, _('Usage: /unload <script.py>\nUnload: Unload the specified python script'), None),
}
self.key_func = {
@@ -169,6 +173,7 @@ class Core(object):
self.xmpp.add_event_handler("chatstate_inactive", self.on_chatstate_inactive)
self.timed_events = set()
+ self.plugin_manager = PluginManager(self)
def coucou(self):
self.command_pubsub('pubsub.louiz.org')
@@ -1121,6 +1126,28 @@ class Core(object):
def completion_status(self, the_input):
return the_input.auto_completion([status for status in possible_show], ' ')
+ def command_load(self, arg):
+ """
+ /load <script.py>
+ """
+ args = arg.split()
+ if len(args) != 1:
+ self.command_help('load')
+ return
+ filename = args[0]
+ self.plugin_manager.load(filename)
+
+ def command_unload(self, arg):
+ """
+ /unload <script.py>
+ """
+ args = arg.split()
+ if len(args) != 1:
+ self.command_help('unload')
+ return
+ filename = args[0]
+ self.plugin_manager.unload(filename)
+
def command_message(self, arg):
"""
/message <jid> [message]
diff --git a/src/plugin.py b/src/plugin.py
new file mode 100644
index 00000000..e8386d16
--- /dev/null
+++ b/src/plugin.py
@@ -0,0 +1,33 @@
+import inspect
+
+class BasePlugin(object):
+ """
+ Class that all plugins derive from. Any methods beginning with command_
+ are interpreted as a command and beginning with on_ are interpreted as
+ event handlers
+ """
+
+ def __init__(self, core):
+ self.core = core
+ for k, v in inspect.getmembers(self, inspect.ismethod):
+ if k.startswith('on_'):
+ core.xmpp.add_event_handler(k[3:], v)
+ elif k.startswith('command_'):
+ command = k[len('command_'):]
+ core.commands[command] = (v, v.__doc__, None)
+ self.init()
+
+ def init(self):
+ pass
+
+ def cleanup(self):
+ pass
+
+ def unload(self):
+ for k, v in inspect.getmembers(self, inspect.ismethod):
+ if k.startswith('on_'):
+ self.core.xmpp.del_event_handler(k[3:], v)
+ elif k.startswith('command_'):
+ command = k[len('command_'):]
+ del self.core.commands[command]
+ self.cleanup()
diff --git a/src/plugin_manager.py b/src/plugin_manager.py
new file mode 100644
index 00000000..3f900e39
--- /dev/null
+++ b/src/plugin_manager.py
@@ -0,0 +1,25 @@
+class PluginManager(object):
+ def __init__(self, core):
+ self.core = core
+ self.plugins = {}
+
+ def load(self, name):
+ if name in self.plugins:
+ self.plugins[name].unload()
+
+ try:
+ code = compile(open(name).read(), name, 'exec')
+ from plugin import BasePlugin
+ globals = { 'BasePlugin' : BasePlugin }
+ exec(code, globals)
+ self.plugins[name] = globals['Plugin'](self.core)
+ except Exception as e:
+ self.core.information("Could not load plugin: %s" % (e,))
+
+ def unload(self, name):
+ if name in self.plugins:
+ try:
+ self.plugins[name].unload()
+ del self.plugins[name]
+ except Exception as e:
+ self.core.information("Could not unload plugin (may not be safe to try again): %s" % (e,))