diff src/plugins/plugin_misc_text_commands.py @ 926:d609581bf74a

plugin text commands: refactoring, text now only contain main commands, and other plugin can add commands themselve: - registerTextCommands can be called by a plugin in its __init__ method, it looks for methods starting with "cmd_" and register them as new commands - addWhoIsCb: add a callback to /whois command, the callback can complete whois informations - plugins parrot, XEP-0045 and XEP-0092 now manage their own commands
author Goffi <goffi@goffi.org>
date Mon, 24 Mar 2014 10:57:15 +0100
parents c897c8d321b3
children cd150dd947e3
line wrap: on
line diff
--- a/src/plugins/plugin_misc_text_commands.py	Sun Mar 23 10:02:50 2014 +0100
+++ b/src/plugins/plugin_misc_text_commands.py	Mon Mar 24 10:57:15 2014 +0100
@@ -18,6 +18,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 from sat.core.i18n import _
+from sat.core.constants import Const as C
 from sat.core.sat_main import MessageSentAndStored
 from twisted.words.protocols.jabber import jid
 from twisted.internet import defer
@@ -26,10 +27,10 @@
 
 PLUGIN_INFO = {
     "name": "Text commands",
-    "import_name": "TEXT-COMMANDS",
+    "import_name": C.TEXT_CMDS,
     "type": "Misc",
     "protocols": [],
-    "dependencies": ["XEP-0045", "EXP-PARROT", "XEP-0092"],
+    "dependencies": [],
     "main": "TextCommands",
     "handler": "no",
     "description": _("""IRC like text commands""")
@@ -45,6 +46,45 @@
         info(_("Text commands initialization"))
         self.host = host
         host.trigger.add("sendMessage", self.sendMessageTrigger)
+        self._commands = {}
+        self._whois = []
+        self.registerTextCommands(self)
+
+    def registerTextCommands(self, instance):
+        """ Add a text command
+        @param instance: instance of a class containing text commands
+
+        """
+        for attr in dir(instance):
+            if attr.startswith('cmd_'):
+                cmd = getattr(instance, attr)
+                if not callable(cmd):
+                    warning(_("Skipping not callable [%s] attribute") % attr)
+                    continue
+                cmd_name = attr[4:]
+                if not cmd_name:
+                    warning(_("Skipping cmd_ method"))
+                if cmd_name in self._commands:
+                    suff=2
+                    while (cmd_name + suff) in self._commands:
+                        suff+=1
+                    new_name = cmd_name + suff
+                    warning(_("Conflict for command [%(old_name)s], renaming it to [%(new_name)s]") % {'old_name': cmd_name, 'new_name': new_name})
+                    cmd_name = new_name
+                self._commands[cmd_name] = cmd
+                info(_("Registered text command [%s]") % cmd_name)
+
+    def addWhoIsCb(self, callback, priority=0):
+        """Add a callback which give information to the /whois command
+        @param callback: a callback which will be called with the following arguments
+            - whois_msg: list of information strings to display, callback need to append its own strings to it
+            - target_jid: full jid from who we want informations
+            - profile: %(doc_profile)s
+        @param priority: priority of the information to show (the highest priority will be displayed first)
+
+        """
+        self._whois.append((priority, callback))
+        self._whois.sort(key=lambda item: item[0])
 
     def sendMessageTrigger(self, mess_data, pre_xml_treatments, post_xml_treatments, profile):
         """ Install SendMessage command hook """
@@ -89,14 +129,14 @@
 
             try:
                 mess_data["unparsed"] = msg[1 + len(command):]  # part not yet parsed of the message
-                d = defer.maybeDeferred(getattr(self, "cmd_%s" % command), mess_data, profile)
+                d = defer.maybeDeferred(self._commands[command], mess_data, profile)
                 d.addCallback(retHandling)
-            except AttributeError:
+            except KeyError:
                 pass
 
         return d or mess_data # if a command is detected, we should have a deferred, else be send the message normally
 
-    def _getRoomJID(self, arg, service_jid):
+    def getRoomJID(self, arg, service_jid):
         """Return a room jid with a shortcut
         @param arg: argument: can be a full room jid (e.g.: sat@chat.jabberfr.org)
                     or a shortcut (e.g.: sat or sat@ for sat on current service)
@@ -109,7 +149,7 @@
             return jid.JID(arg + service_jid)
         return jid.JID(u"%s@%s" % (arg, service_jid))
 
-    def _feedBack(self, message, mess_data, profile):
+    def feedBack(self, message, mess_data, profile):
         """Give a message back to the user"""
         if mess_data["type"] == 'groupchat':
             _from = mess_data["to"].userhostJID()
@@ -118,128 +158,6 @@
 
         self.host.bridge.newMessage(unicode(mess_data["to"]), message, mess_data['type'], unicode(_from), {}, profile=profile)
 
-    def cmd_nick(self, mess_data, profile):
-        """change nickname"""
-        debug("Catched nick command")
-
-        if mess_data['type'] != "groupchat":
-            #/nick command does nothing if we are not on a group chat
-            info("Ignoring /nick command on a non groupchat message")
-
-            return True
-
-        nick = mess_data["unparsed"].strip()
-        room = mess_data["to"]
-
-        self.host.plugins["XEP-0045"].nick(room, nick, profile)
-
-        return False
-
-    def cmd_join(self, mess_data, profile):
-        """join a new room (on the same service if full jid is not specified)"""
-        debug("Catched join command")
-
-        if mess_data['type'] != "groupchat":
-            #/leave command does nothing if we are not on a group chat
-            info("Ignoring /join command on a non groupchat message")
-            return True
-
-        if mess_data["unparsed"].strip():
-            room = self._getRoomJID(mess_data["unparsed"].strip(), mess_data["to"].host)
-            nick = (self.host.plugins["XEP-0045"].getRoomNick(mess_data["to"].userhost(), profile) or
-                    self.host.getClient(profile).jid.user)
-            self.host.plugins["XEP-0045"].join(room, nick, {}, profile)
-
-        return False
-
-    def cmd_leave(self, mess_data, profile):
-        """quit a room"""
-        debug("Catched leave command")
-
-        if mess_data['type'] != "groupchat":
-            #/leave command does nothing if we are not on a group chat
-            info("Ignoring /leave command on a non groupchat message")
-            return True
-
-        if mess_data["unparsed"].strip():
-            room = self._getRoomJID(mess_data["unparsed"].strip(), mess_data["to"].host)
-        else:
-            room = mess_data["to"]
-
-        self.host.plugins["XEP-0045"].leave(room, profile)
-
-        return False
-
-    def cmd_part(self, mess_data, profile):
-        """just a synonym of /leave"""
-        return self.cmd_leave(mess_data, profile)
-
-    def cmd_title(self, mess_data, profile):
-        """change room's subject"""
-        debug("Catched title command")
-
-        if mess_data['type'] != "groupchat":
-            #/leave command does nothing if we are not on a group chat
-            info("Ignoring /title command on a non groupchat message")
-            return True
-
-        subject = mess_data["unparsed"].strip()
-
-        if subject:
-            room = mess_data["to"]
-            self.host.plugins["XEP-0045"].subject(room, subject, profile)
-
-        return False
-
-    def cmd_topic(self, mess_data, profile):
-        """just a synonym of /title"""
-        return self.cmd_title(mess_data, profile)
-
-    def cmd_parrot(self, mess_data, profile):
-        """activate Parrot mode between 2 entities, in both directions."""
-        #TODO: these commands must not be hardcoded, an interface should be made
-        #      to allow plugins to register simple commands like this.
-
-        debug("Catched parrot command")
-
-        try:
-            link_left_jid = jid.JID(mess_data["unparsed"].strip())
-            if not link_left_jid.user or not link_left_jid.host:
-                raise jid.InvalidFormat
-        except jid.InvalidFormat:
-            self._feedBack("Can't activate Parrot mode for invalid jid", mess_data, profile)
-            return False
-
-        link_right_jid = mess_data['to']
-
-        self.host.plugins["EXP-PARROT"].addParrot(link_left_jid, link_right_jid, profile)
-        self.host.plugins["EXP-PARROT"].addParrot(link_right_jid, link_left_jid, profile)
-
-        self._feedBack("Parrot mode activated for %s" % (unicode(link_left_jid), ), mess_data, profile)
-
-        return False
-
-    def cmd_unparrot(self, mess_data, profile):
-        """remove Parrot mode between 2 entities, in both directions."""
-        debug("Catched unparrot command")
-
-        try:
-            link_left_jid = jid.JID(mess_data["unparsed"].strip())
-            if not link_left_jid.user or not link_left_jid.host:
-                raise jid.InvalidFormat
-        except jid.InvalidFormat:
-            self._feedBack("Can't deactivate Parrot mode for invalid jid", mess_data, profile)
-            return False
-
-        link_right_jid = mess_data['to']
-
-        self.host.plugins["EXP-PARROT"].removeParrot(link_left_jid, profile)
-        self.host.plugins["EXP-PARROT"].removeParrot(link_right_jid, profile)
-
-        self._feedBack("Parrot mode deactivated for %s and %s" % (unicode(link_left_jid), unicode(link_right_jid)), mess_data, profile)
-
-        return False
-
     def cmd_whois(self, mess_data, profile):
         """show informations on entity"""
         debug("Catched whois command")
@@ -259,7 +177,7 @@
                 if not target_jid.user or not target_jid.host:
                     raise jid.InvalidFormat
             except (jid.InvalidFormat, RuntimeError):
-                self._feedBack(_("Invalid jid, can't whois"), mess_data, profile)
+                self.feedBack(_("Invalid jid, can't whois"), mess_data, profile)
                 return False
 
         if not target_jid.resource:
@@ -267,23 +185,12 @@
 
         whois_msg = [_(u"whois for %(jid)s") % {'jid': target_jid}]
 
-        # version
-        def versionCb(version_data):
-            name, version, os = version_data
-            if name:
-                whois_msg.append(_("Client name: %s") % name)
-            if version:
-                whois_msg.append(_("Client version: %s") % version)
-            if os:
-                whois_msg.append(_("Operating system: %s") % os)
-
-        d = self.host.plugins['XEP-0092'].getVersion(target_jid, profile)
-        d.addCallback(versionCb)
-
-        #TODO: add informations here (vcard, etc)
+        d = defer.succeed(None)
+        for ignore, callback in self._whois:
+            d.addCallback(lambda ignore: callback(whois_msg, target_jid, profile))
 
         def feedBack(ignore):
-            self._feedBack(u"\n".join(whois_msg), mess_data, profile)
+            self.feedBack(u"\n".join(whois_msg), mess_data, profile)
             return False
 
         d.addCallback(feedBack)
@@ -295,14 +202,14 @@
         longuest = max([len(command) for command in commands])
         help_cmds = []
 
-        for command in commands:
-            method = getattr(self, command)
+        for command in self._commands:
+            method = self._commands[command]
             try:
                 help_str = method.__doc__.split('\n')[0]
             except AttributeError:
                 help_str = ''
             spaces = (longuest - len(command)) * ' '
-            help_cmds.append("    /%s: %s %s" % (command[4:], spaces, help_str))
+            help_cmds.append("    /%s: %s %s" % (command, spaces, help_str))
 
         help_mess = _(u"Text commands available:\n%s") % (u'\n'.join(help_cmds), )
-        self._feedBack(help_mess, mess_data, profile)
+        self.feedBack(help_mess, mess_data, profile)