changeset 2957:bc85e5200dd3

GHOP: refactor hardcoded if...elif...else code to use dispatching.
author Karol 'grzywacz' Nowak <grzywacz@sul.uni.lodz.pl>
date Tue, 04 Dec 2007 22:24:53 +0100
parents a8809feee20c
children 6eebb112108b
files jabberbot/xmlrpcbot.py jabberbot/xmppbot.py
diffstat 2 files changed, 158 insertions(+), 133 deletions(-) [+]
line wrap: on
line diff
--- a/jabberbot/xmlrpcbot.py	Sat Dec 01 15:50:42 2007 +0100
+++ b/jabberbot/xmlrpcbot.py	Tue Dec 04 22:24:53 2007 +0100
@@ -15,10 +15,12 @@
 
 
 class ConfigurationError(Exception):
+
     def __init__(self, message):
         Exception.__init__(self)
         self.message = message
 
+
 def _xmlrpc_decorator(function):
     """A decorator function, which adds some maintenance code
 
@@ -95,6 +97,14 @@
         self.multicall = None
         self.stopping = False
 
+        self._cmd_handlers = {cmd.GetPage: self.get_page,
+                              cmd.GetPageHTML: self.get_page_html,
+                              cmd.GetPageList: self.get_page_list,
+                              cmd.GetPageInfo: self.get_page_info,
+                              cmd.GetUserLanguage: self.get_language_by_jid,
+                              cmd.Search: self.do_search,
+                              cmd.RevertPage: self.do_revert}
+
     def run(self):
         """Starts the server / thread"""
         while True:
@@ -117,21 +127,15 @@
     def execute_command(self, command):
         """Execute commands coming from the XMPP component"""
 
-        # FIXME: make this kind of automatic
-        if isinstance(command, cmd.GetPage):
-            self.get_page(command)
-        elif isinstance(command, cmd.GetPageHTML):
-            self.get_page_html(command)
-        elif isinstance(command, cmd.GetPageList):
-            self.get_page_list(command)
-        elif isinstance(command, cmd.GetPageInfo):
-            self.get_page_info(command)
-        elif isinstance(command, cmd.GetUserLanguage):
-            self.get_language_by_jid(command)
-        elif isinstance(command, cmd.Search):
-            self.do_search(command)
-        elif isinstance(command, cmd.RevertPage):
-            self.do_revert(command)
+        cmd_name = command.__class__
+
+        try:
+            handler = self._cmd_handlers[cmd_name]
+        except KeyError:
+            self.log.debug("No such command: " + cmd_name.__name__)
+            return
+
+        handler(command)
 
     def report_error(self, jid, text, data={}):
         """Reports an internal error
--- a/jabberbot/xmppbot.py	Sat Dec 01 15:50:42 2007 +0100
+++ b/jabberbot/xmppbot.py	Tue Dec 04 22:24:53 2007 +0100
@@ -158,6 +158,7 @@
         res = ", ".join([name + " is " + res['show'] for name, res in self.resources.items()])
         return retval % (self.jid.as_unicode(), res, len(self.messages))
 
+
 class XMPPBot(Client, Thread):
     """A simple XMPP bot"""
 
@@ -198,6 +199,19 @@
 
         Client.__init__(self, self.jid, config.xmpp_password, config.xmpp_server, tls_settings=self.tlsconfig)
 
+        # Setup message handlers
+
+        self._msg_handlers = {cmd.NotificationCommand: self._handle_notification,
+                              cmd.NotificationCommandI18n: self._handle_notification,
+                              cmd.AddJIDToRosterCommand: self._handle_add_contact,
+                              cmd.RemoveJIDFromRosterCommand: self._handle_remove_contact,
+                              cmd.GetPage: self._handle_get_page,
+                              cmd.GetPageHTML: self._handle_get_page,
+                              cmd.GetPageList: self._handle_get_page_list,
+                              cmd.GetPageInfo: self._handle_get_page_info,
+                              cmd.GetUserLanguage: self._handle_get_language,
+                              cmd.Search: self._handle_search}
+
     def run(self):
         """Start the bot - enter the event loop"""
 
@@ -277,7 +291,6 @@
         except Queue.Empty:
             return False
 
-    # XXX: refactor this, if-elif sequence is already too long
     def handle_command(self, command, ignore_dnd=False):
         """Excecutes commands from other components
 
@@ -286,123 +299,17 @@
         @param ignore_dnd: if command results in user interaction, should DnD be ignored?
 
         """
-        # Handle normal notifications
-        if isinstance(command, cmd.NotificationCommand):
-            cmd_data = command.notification
-            original_text = cmd_data.get('text', '')
-            original_subject = cmd_data.get('subject', '')
-
-            for recipient in command.jids:
-                jid = JID(recipient)
-                jid_text = jid.bare().as_unicode()
-
-                if isinstance(command, cmd.NotificationCommandI18n):
-                    # Translate&interpolate the message with data
-                    gettext_func = self.get_text(jid_text)
-                    text, subject = command.translate(gettext_func)
-                    cmd_data['text'] = text
-                    cmd_data['subject'] = subject
-                else:
-                    cmd_data['text'] = original_text
-                    cmd_data['subject'] = original_subject
 
-                # Check if contact is DoNotDisturb.
-                # If so, queue the message for delayed delivery.
-                contact = self.contacts.get(jid_text, '')
-                if contact:
-                    if command.async and contact.is_dnd() and not ignore_dnd:
-                        contact.messages.append(command)
-                        return
+        cmd_cls = command.__class__
 
-                action = cmd_data.get('action', '')
-                if action == u'page_changed':
-                    self.handle_changed_action(cmd_data, jid, contact)
-                elif action == u'page_deleted':
-                    self.handle_deleted_action(cmd_data, jid, contact)
-                elif action == u'file_attached':
-                    self.handle_attached_action(cmd_data, jid, contact)
-                elif action == u'page_renamed':
-                    self.handle_renamed_action(cmd_data, jid, contact)
-                elif action == u'user_created':
-                    self.handle_user_created_action(cmd_data, jid, contact)
-                else:
-                    self.send_message(jid, cmd_data, command.msg_type)
-
+        try:
+            handler = self._msg_handlers[cmd_cls]
+        except KeyError:
+            self.log.debug("No such command: " + cmd_cls.__name__)
             return
 
-        _ = self.get_text(command.jid)
-
-        # Handle subscribtion management commands
-        if isinstance(command, cmd.AddJIDToRosterCommand):
-            jid = JID(node_or_jid=command.jid)
-            self.ask_for_subscription(jid)
-
-        elif isinstance(command, cmd.RemoveJIDFromRosterCommand):
-            jid = JID(node_or_jid=command.jid)
-            self.remove_subscription(jid)
-
-        elif isinstance(command, cmd.GetPage) or isinstance(command, cmd.GetPageHTML):
-            msg = _(u"""Here's the page "%(pagename)s" that you've requested:\n\n%(data)s""")
-
-            cmd_data = {'text': msg % {'pagename': command.pagename, 'data': command.data}}
-            self.send_message(command.jid, cmd_data)
-
-        elif isinstance(command, cmd.GetPageList):
-            msg = _("That's the list of pages accesible to you:\n\n%s")
-            pagelist = u"\n".join(command.data)
-
-            self.send_message(command.jid, {'text': msg % (pagelist, )})
-
-        elif isinstance(command, cmd.GetPageInfo):
-            self.handle_page_info(command)
-
-        elif isinstance(command, cmd.GetUserLanguage):
-            if command.jid in self.contacts:
-                self.contacts[command.jid].language = command.language
-
-        elif isinstance(command, cmd.Search):
-            warnings = []
-            if not command.data:
-                warnings.append(_("There are no pages matching your search criteria!"))
-
-            # This hardcoded limitation relies on (mostly correct) assumption that Jabber
-            # servers have rather tight traffic limits. Sending more than 25 results is likely
-            # to take a second or two - users should not have to wait longer (+search time!).
-            elif len(command.data) > 25:
-                warnings.append(_("There are too many results (%(number)s). Limiting to first 25 entries.") % {'number': str(len(command.data))})
-                command.data = command.data[:25]
-
-            results = [{'description': result[0], 'url': result[2]} for result in command.data]
-
-            if command.presentation == u"text":
-                for warning in warnings:
-                    self.send_message(command.jid, {'text': warning})
-
-                if not results:
-                    return
-
-                data = {'text': _('Following pages match your search criteria:'), 'url_list': results}
-                self.send_message(command.jid, data, u"chat")
-            else:
-                form_title = _("Search results").encode("utf-8")
-                help_form = _("Submit this form to perform a wiki search").encode("utf-8")
-                form = forms.Form(xmlnode_or_type="result", title=form_title, instructions=help_form)
-
-                action_label = _("What to do next")
-                do_nothing = forms.Option("n", _("Do nothing"))
-                search_again = forms.Option("s", _("Search again"))
-
-                for no, warning in enumerate(warnings):
-                    form.add_field(name="warning", field_type="fixed", value=warning)
-
-                for no, result in enumerate(results):
-                    field_name = "url%d" % (no, )
-                    form.add_field(name=field_name, value=unicode(result["url"]), label=result["description"].encode("utf-8"), field_type="text-single")
-
-                # Selection of a following action
-                form.add_field(name="options", field_type="list-single", options=[do_nothing, search_again], label=action_label)
-
-                self.send_form(command.jid, form, _("Search results"))
+        # NOTE: handler is a method, so it takes self as a hidden arg
+        handler(command, ignore_dnd)
 
     def handle_changed_action(self, cmd_data, jid, contact):
         """Handles a notification command with 'page_changed' action
@@ -438,7 +345,6 @@
         else:
             self.send_deleted_text(jid.as_unicode(), cmd_data)
 
-
     def handle_attached_action(self, cmd_data, jid, contact):
         """Handles a notification cmd_data with 'file_attached' action
 
@@ -1073,7 +979,6 @@
 
                 self.handle_xmlrpc_command(jid, ["revertpage", form["page_name"].value, "%d" % (revision - 1, )])
 
-
     def handle_search_form(self, jid, form):
         """Handles a search form
 
@@ -1369,7 +1274,6 @@
         self.get_stream().set_response_handlers(query, self.handle_disco_result, None)
         self.get_stream().send(query)
 
-
     def handle_disco_result(self, stanza):
         """Handler for <iq> service discovery results
 
@@ -1389,7 +1293,6 @@
             jid = stanza.get_from_jid()
             self.contacts[jid.bare().as_unicode()].set_supports(jid.resource, u"jabber:x:oob")
 
-
     def send_queued_messages(self, contact, ignore_dnd=False):
         """Sends messages queued for the contact
 
@@ -1457,3 +1360,121 @@
     def stream_error(self, error):
         """Called when stream error gets received"""
         self.log.error("Received a stream error.")
+
+    # Message handlers
+
+    def _handle_notification(self, command, ignore_dnd):
+        cmd_data = command.notification
+        original_text = cmd_data.get('text', '')
+        original_subject = cmd_data.get('subject', '')
+
+        for recipient in command.jids:
+            jid = JID(recipient)
+            jid_text = jid.bare().as_unicode()
+
+            if isinstance(command, cmd.NotificationCommandI18n):
+                # Translate&interpolate the message with data
+                gettext_func = self.get_text(jid_text)
+                text, subject = command.translate(gettext_func)
+                cmd_data['text'] = text
+                cmd_data['subject'] = subject
+            else:
+                cmd_data['text'] = original_text
+                cmd_data['subject'] = original_subject
+
+            # Check if contact is DoNotDisturb.
+            # If so, queue the message for delayed delivery.
+            contact = self.contacts.get(jid_text, '')
+            if contact:
+                if command.async and contact.is_dnd() and not ignore_dnd:
+                    contact.messages.append(command)
+                    return
+
+            action = cmd_data.get('action', '')
+            if action == u'page_changed':
+                self.handle_changed_action(cmd_data, jid, contact)
+            elif action == u'page_deleted':
+                self.handle_deleted_action(cmd_data, jid, contact)
+            elif action == u'file_attached':
+                self.handle_attached_action(cmd_data, jid, contact)
+            elif action == u'page_renamed':
+                self.handle_renamed_action(cmd_data, jid, contact)
+            elif action == u'user_created':
+                self.handle_user_created_action(cmd_data, jid, contact)
+            else:
+                self.send_message(jid, cmd_data, command.msg_type)
+
+    def _handle_search(self, command, ignore_dnd):
+        warnings = []
+        _ = self.get_text(command.jid)
+
+        if not command.data:
+            warnings.append(_("There are no pages matching your search criteria!"))
+
+        # This hardcoded limitation relies on (mostly correct) assumption that Jabber
+        # servers have rather tight traffic limits. Sending more than 25 results is likely
+        # to take a second or two - users should not have to wait longer (+search time!).
+        elif len(command.data) > 25:
+            warnings.append(_("There are too many results (%(number)s). Limiting to first 25 entries.") % {'number': str(len(command.data))})
+            command.data = command.data[:25]
+
+        results = [{'description': result[0], 'url': result[2]} for result in command.data]
+
+        if command.presentation == u"text":
+            for warning in warnings:
+                self.send_message(command.jid, {'text': warning})
+
+            if not results:
+                return
+
+            data = {'text': _('Following pages match your search criteria:'), 'url_list': results}
+            self.send_message(command.jid, data, u"chat")
+        else:
+            form_title = _("Search results").encode("utf-8")
+            help_form = _("Submit this form to perform a wiki search").encode("utf-8")
+            form = forms.Form(xmlnode_or_type="result", title=form_title, instructions=help_form)
+
+            action_label = _("What to do next")
+            do_nothing = forms.Option("n", _("Do nothing"))
+            search_again = forms.Option("s", _("Search again"))
+
+            for no, warning in enumerate(warnings):
+                form.add_field(name="warning", field_type="fixed", value=warning)
+
+            for no, result in enumerate(results):
+                field_name = "url%d" % (no, )
+                form.add_field(name=field_name, value=unicode(result["url"]), label=result["description"].encode("utf-8"), field_type="text-single")
+
+            # Selection of a following action
+            form.add_field(name="options", field_type="list-single", options=[do_nothing, search_again], label=action_label)
+
+            self.send_form(command.jid, form, _("Search results"))
+
+    def _handle_add_contact(self, command, ignore_dnd):
+        jid = JID(node_or_jid = command.jid)
+        self.ask_for_subscription(jid)
+
+    def _handle_remove_contact(self, command, ignore_dnd):
+        jid = JID(node_or_jid = command.jid)
+        self.remove_subscription(jid)
+
+    def _handle_get_page(self, command, ignore_dnd):
+        _ = self.get_text(command.jid)
+        msg = _(u"""Here's the page "%(pagename)s" that you've requested:\n\n%(data)s""")
+
+        cmd_data = {'text': msg % {'pagename': command.pagename, 'data': command.data}}
+        self.send_message(command.jid, cmd_data)
+
+    def _handle_get_page_list(self, command, ignore_dnd):
+        _ = self.get_text(command.jid)
+        msg = _("That's the list of pages accesible to you:\n\n%s")
+        pagelist = u"\n".join(command.data)
+
+        self.send_message(command.jid, {'text': msg % (pagelist, )})
+
+    def _handle_get_page_info(self, command, ignore_dnd):
+        self.handle_page_info(command)
+
+    def _handle_get_language(self, command, ignore_dnd):
+        if command.jid in self.contacts:
+            self.contacts[command.jid].language = command.language