Add context menu support
This patch adds support for adding context menu entries to connected users. As these callbacks are - in contrast to all others in mumo - user specific they do not fit well with the existing mumo architecture. To cope with this creating these callbacks is now handled directly inside the manager on behalf of the remote manager. The remote manager then handles forwarding any context menu actions to a user selected handler. This patch also removes the vestigial parts of context menu handling already present but unused in mumo as they were an architectural dead end.
This commit is contained in:
19
mumo.py
19
mumo.py
@@ -194,7 +194,7 @@ def do_main_program():
|
||||
|
||||
info('Connecting to Ice server (%s:%d)', cfg.ice.host, cfg.ice.port)
|
||||
base = ice.stringToProxy(prxstr)
|
||||
self.meta =Murmur.MetaPrx.uncheckedCast(base)
|
||||
self.meta = Murmur.MetaPrx.uncheckedCast(base)
|
||||
|
||||
if cfg.ice.callback_port > 0:
|
||||
cbp = ' -p %d' % cfg.ice.callback_port
|
||||
@@ -204,6 +204,7 @@ def do_main_program():
|
||||
adapter = ice.createObjectAdapterWithEndpoints('Callback.Client', 'tcp -h %s%s' % (cfg.ice.callback_host, cbp))
|
||||
adapter.activate()
|
||||
self.adapter = adapter
|
||||
self.manager.setClientAdapter(adapter)
|
||||
|
||||
metacbprx = adapter.addWithUUID(metaCallback(self))
|
||||
self.metacb = Murmur.MetaCallbackPrx.uncheckedCast(metacbprx)
|
||||
@@ -423,23 +424,23 @@ def do_main_program():
|
||||
@forwardServer
|
||||
def userTextMessage(self, u, m, current=None) : pass
|
||||
|
||||
class contextCallback(Murmur.ServerContextCallback):
|
||||
def __init__(self, manager, server, sid):
|
||||
class customContextCallback(Murmur.ServerContextCallback):
|
||||
def __init__(self, contextActionCallback, *ctx):
|
||||
Murmur.ServerContextCallback.__init__(self)
|
||||
self.manager = manager
|
||||
self.server = server
|
||||
self.sid = sid
|
||||
self.cb = contextActionCallback
|
||||
self.ctx = ctx
|
||||
|
||||
@checkSecret
|
||||
def contextAction(self, action, p, session, chanid, current=None):
|
||||
self.manager.announceContext(self.sid, "contextAction", self.server, action, p, session, chanid, current)
|
||||
def contextAction(self, *args, **argv):
|
||||
# (action, user, target_session, target_chanid, current=None)
|
||||
self.cb(*(self.ctx + args), **argv)
|
||||
|
||||
#
|
||||
#--- Start of moderator
|
||||
#
|
||||
info('Starting mumble moderator')
|
||||
debug('Initializing manager')
|
||||
manager = MumoManager(Murmur)
|
||||
manager = MumoManager(Murmur, customContextCallback)
|
||||
manager.start()
|
||||
manager.loadModules()
|
||||
manager.startModules()
|
||||
|
||||
159
mumo_manager.py
159
mumo_manager.py
@@ -34,6 +34,7 @@ from worker import Worker, local_thread, local_thread_blocking
|
||||
from config import Config
|
||||
import sys
|
||||
import os
|
||||
import uuid
|
||||
|
||||
class FailedLoadModuleException(Exception):
|
||||
pass
|
||||
@@ -82,6 +83,8 @@ class MumoManagerRemote(object):
|
||||
self.__name = name
|
||||
self.__queue = queue
|
||||
|
||||
self.__context_callbacks = {} # server -> action -> callback
|
||||
|
||||
def getQueue(self):
|
||||
return self.__queue
|
||||
|
||||
@@ -139,29 +142,87 @@ class MumoManagerRemote(object):
|
||||
"""
|
||||
return self.__master.unsubscribeServerCallbacks(self.__queue, handler, servers)
|
||||
|
||||
def subscribeContextCallbacks(self, handler, servers = SERVERS_ALL):
|
||||
def getUniqueAction(self):
|
||||
"""
|
||||
Subscribe to context callbacks. Subscribes the given handler to the following
|
||||
callbacks:
|
||||
Returns a unique action string that can be used in addContextMenuEntry.
|
||||
|
||||
>>> contextAction(self, action, user, session, channelid, context = None)
|
||||
|
||||
@param servers: List of server IDs for which to subscribe. To subscribe to all
|
||||
servers pass SERVERS_ALL.
|
||||
@param handler: Object on which to call the callback functions
|
||||
:return: Unique action string
|
||||
"""
|
||||
return self.__master.subscribeContextCallbacks(self.__queue, handler, servers)
|
||||
return str(uuid.uuid4())
|
||||
|
||||
def unsubscribeContextCallbacks(self, handler, servers = SERVERS_ALL):
|
||||
def addContextMenuEntry(self, server, user, action, text, handler, context):
|
||||
"""
|
||||
Unsubscribe from context callbacks. Unsubscribes the given handler from callbacks
|
||||
for the given servers.
|
||||
Adds a new context callback menu entry with the given text for the given user.
|
||||
|
||||
@param servers: List of server IDs for which to unsubscribe. To unsubscribe from all
|
||||
servers pass SERVERS_ALL.
|
||||
@param handler: Subscribed handler
|
||||
You can use the same action identifier for multiple users entries to
|
||||
simplify your handling. However make sure an action identifier is unique
|
||||
to your module. The easiest way to achieve this is to use getUniqueAction
|
||||
to generate a guaranteed unique one.
|
||||
|
||||
Your handler should be of form:
|
||||
>>> handler(self, server, action, user, target)
|
||||
|
||||
Here server is the server the user who triggered the action resides on.
|
||||
Target identifies what the context action was invoked on. It can be either
|
||||
a User, Channel or None.
|
||||
|
||||
@param server: Server the user resides on
|
||||
@param user: User to add entry for
|
||||
@param action: Action identifier passed to your callback (see above)
|
||||
@param text: Text for the menu entry
|
||||
@param handler: Handler function to call when the menu item is used
|
||||
@param context: Contexts to show entry in (can be a combination of ContextServer, ContextChannel and ContextUser)
|
||||
"""
|
||||
return self.__master.unsubscribeContextCallbacks(self.__queue, handler, servers)
|
||||
|
||||
server_actions = self.__context_callbacks.get(server.id())
|
||||
if not server_actions:
|
||||
server_actions = {}
|
||||
self.__context_callbacks[server.id()] = server_actions
|
||||
|
||||
action_cb = server_actions.get(action)
|
||||
if not action_cb:
|
||||
# We need to create an register a new context callback
|
||||
action_cb = self.__master.createContextCallback(self.__handle_context_callback, handler, server)
|
||||
server_actions[action] = action_cb
|
||||
|
||||
server.addContextCallback(user.session, action, text, action_cb, context)
|
||||
|
||||
def __handle_context_callback(self, handler, server, action, user, target_session, target_channelid, current=None):
|
||||
"""
|
||||
Small callback wrapper for context menu operations.
|
||||
|
||||
Translates the given target into the corresponding object and
|
||||
schedules a call to the actual user context menu handler which
|
||||
will be executed in the modules thread.
|
||||
"""
|
||||
|
||||
if target_session != 0:
|
||||
target = server.getState(target_session)
|
||||
elif target_channelid != -1:
|
||||
target = server.getChannelState(target_channelid)
|
||||
else:
|
||||
target = None
|
||||
|
||||
# Schedule a call to the handler
|
||||
self.__queue.put((None, handler, [server, action, user, target], {}))
|
||||
|
||||
def removeContextMenuEntry(self, server, action):
|
||||
"""
|
||||
Removes a previously created context action callback from a server.
|
||||
|
||||
Applies to all users that share the action on this server.
|
||||
|
||||
@param server Server the action should be removed from.
|
||||
@param action Action to remove
|
||||
"""
|
||||
|
||||
try:
|
||||
cb = self.__context_callbacks[server.id()].pop(action)
|
||||
except KeyError:
|
||||
# Nothing to unregister
|
||||
return
|
||||
|
||||
server.removeContextCallback(cb)
|
||||
|
||||
def getMurmurModule(self):
|
||||
"""
|
||||
@@ -183,7 +244,7 @@ class MumoManager(Worker):
|
||||
('cfg_dir', str, "modules-enabled/"),
|
||||
('timeout', int, 2))}
|
||||
|
||||
def __init__(self, murmur, cfg = Config(default = cfg_default)):
|
||||
def __init__(self, murmur, context_callback_type, cfg = Config(default = cfg_default)):
|
||||
Worker.__init__(self, "MumoManager")
|
||||
self.queues = {} # {queue:module}
|
||||
self.modules = {} # {name:module}
|
||||
@@ -192,10 +253,22 @@ class MumoManager(Worker):
|
||||
|
||||
self.murmur = murmur
|
||||
self.meta = None
|
||||
self.client_adapter = None
|
||||
|
||||
self.metaCallbacks = {} # {sid:{queue:[handler]}}
|
||||
self.serverCallbacks = {}
|
||||
self.contextCallbacks = {}
|
||||
|
||||
self.context_callback_type = context_callback_type
|
||||
|
||||
def setClientAdapter(self, client_adapter):
|
||||
"""
|
||||
Sets the ice adapter used for client-side callbacks. This is needed
|
||||
in case per-module callbacks have to be attached during run-time
|
||||
as is the case for context callbacks.
|
||||
|
||||
:param client_adapter: Ice object adapter
|
||||
"""
|
||||
self.client_adapter = client_adapter
|
||||
|
||||
def __add_to_dict(self, mdict, queue, handler, servers):
|
||||
for server in servers:
|
||||
@@ -303,17 +376,6 @@ class MumoManager(Worker):
|
||||
"""
|
||||
self.__announce_to_dict(self.serverCallbacks, server, function, *args, **kwargs)
|
||||
|
||||
@local_thread
|
||||
def announceContext(self, server, function, *args, **kwargs):
|
||||
"""
|
||||
Call a function on the context handlers
|
||||
|
||||
@param server Server to announce to
|
||||
@param function Name of the function to call on the handler
|
||||
@param args List of arguments
|
||||
@param kwargs List of keyword arguments
|
||||
"""
|
||||
self.__announce_to_dict(self.serverCallbacks, server, function, *args, **kwargs)
|
||||
#
|
||||
#--- Module self management functionality
|
||||
#
|
||||
@@ -350,32 +412,27 @@ class MumoManager(Worker):
|
||||
"""
|
||||
return self.__rem_from_dict(self.serverCallbacks, queue, handler, servers)
|
||||
|
||||
@local_thread
|
||||
def subscribeContextCallbacks(self, queue, handler, servers):
|
||||
"""
|
||||
@param queue Target worker queue
|
||||
@see MumoManagerRemote
|
||||
"""
|
||||
|
||||
#TODO: Implement context callbacks
|
||||
self.log().error("Context callbacks not implemented at this point")
|
||||
|
||||
return self.__add_to_dict(self.contextCallbacks, queue, handler, servers)
|
||||
|
||||
@local_thread
|
||||
def unsubscribeContextCallbacks(self, queue, handler, servers):
|
||||
"""
|
||||
@param queue Target worker queue
|
||||
@see MumoManagerRemote
|
||||
"""
|
||||
return self.__rem_from_dict(self.contextCallbacks, queue, handler, servers)
|
||||
|
||||
def getMurmurModule(self):
|
||||
"""
|
||||
Returns the Murmur module generated from the slice file
|
||||
"""
|
||||
return self.murmur
|
||||
|
||||
def createContextCallback(self, callback, *ctx):
|
||||
"""
|
||||
Creates a new context callback handler class instance.
|
||||
|
||||
@param callback Callback to set for handler
|
||||
@param *ctx Additional context parameters passed to callback
|
||||
before the actual parameters.
|
||||
@return Murmur ServerContextCallbackPrx object for the context
|
||||
callback handler class.
|
||||
"""
|
||||
contextcbprx = self.client_adapter.addWithUUID(self.context_callback_type(callback, *ctx))
|
||||
contextcb = self.murmur.ServerContextCallbackPrx.uncheckedCast(contextcbprx)
|
||||
|
||||
return contextcb
|
||||
|
||||
def getMeta(self):
|
||||
"""
|
||||
Returns the connected servers meta module or None if it is not available
|
||||
@@ -582,6 +639,10 @@ class MumoManager(Worker):
|
||||
return stoppedmodules
|
||||
|
||||
def stop(self, force = True):
|
||||
"""
|
||||
Stops all modules and shuts down the manager.
|
||||
"""
|
||||
self.log().debug("Stopping")
|
||||
self.stopModules()
|
||||
Worker.stop(self, force)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user