changeset 2100:0dbf808a6e42

Move jabber bot to a top-level package.
author Karol 'grzywacz' Nowak <grzywacz@sul.uni.lodz.pl>
date Wed, 06 Jun 2007 20:45:31 +0200
parents ab5e7026413a
children 3b0fca14c14c
files JabberBot/__init__.py JabberBot/commands.py JabberBot/main.py JabberBot/xmlrpcbot.py JabberBot/xmppbot.py MoinMoin/jabber/__init__.py MoinMoin/jabber/commands.py MoinMoin/jabber/main.py MoinMoin/jabber/xmlrpcbot.py MoinMoin/jabber/xmppbot.py
diffstat 10 files changed, 565 insertions(+), 565 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/JabberBot/__init__.py	Wed Jun 06 20:45:31 2007 +0200
@@ -0,0 +1,11 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - jabber bot
+
+    This is a bot for notification and simple editing
+    operations. Developed as a Google Summer of Code 
+    project.
+
+@copyright: 2007 by Karol Nowak <grywacz@gmail.com>
+@license: GNU GPL, see COPYING for details.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/JabberBot/commands.py	Wed Jun 06 20:45:31 2007 +0200
@@ -0,0 +1,27 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - inter-thread communication commands
+
+    This file defines command objects used by notification
+    bot's threads to communicate among each other.
+
+    @copyright: 2007 by Karol Nowak <grywacz@gmail.com>
+    @license: GNU GPL, see COPYING for details.
+"""
+
+# First, XML RPC -> XMPP commands
+class NotificationCommand:
+    """Class representing a notification request"""
+    def __init__(self, jid, text):
+        self.jid = jid
+        self.text = text
+        
+class AddJIDToRosterCommand:
+    """Class representing a request to add a new jid to roster"""
+    def __init__(self, jid):
+        self.jid = jid
+        
+class RemoveJIDFromRosterCommand:
+    """Class representing a request to remove a jid from roster"""
+    def __init__(self, jid):
+        self.jid = jid
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/JabberBot/main.py	Wed Jun 06 20:45:31 2007 +0200
@@ -0,0 +1,52 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - jabber bot main file
+
+    This is a bot for notification and simple editing
+    operations. Developed as a Google Summer of Code 
+    project.
+
+    @copyright: 2007 by Karol Nowak <grywacz@gmail.com>
+    @license: GNU GPL, see COPYING for details.
+"""
+
+import sys
+import os
+
+from config import Config
+from xmppbot import XMPPBot
+from xmlrpcbot import XMLRPCServer, XMLRPCClient
+from Queue import Queue
+
+
+def main():
+    args = sys.argv
+    
+    if "--help" in args:
+        print """MoinMoin notification bot
+        
+        Usage: %(myname)s [--server server] [--xmpp_port port] [--user user] [--resource resource] [--password pass] [--xmlrpc_host host] [--xmlrpc_port port]
+        """ % { "myname": os.path.basename(args[0]) }
+        
+        raise SystemExit
+    
+    # TODO: actually accept options from the help string
+
+    commands_from_xmpp = Queue()
+    commands_to_xmpp = Queue()
+    
+    try:
+        xmpp_bot = XMPPBot(Config, commands_from_xmpp, commands_to_xmpp)
+        xmlrpc_client = XMLRPCClient(Config, commands_from_xmpp)
+        xmlrpc_server = XMLRPCServer(Config, commands_to_xmpp)
+        
+        xmpp_bot.start()
+        xmlrpc_client.start()
+        xmlrpc_server.start()
+    
+    except KeyboardInterrupt, i:
+        print i
+        sys.exit(0)
+
+        
+if __name__ == "__main__": main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/JabberBot/xmlrpcbot.py	Wed Jun 06 20:45:31 2007 +0200
@@ -0,0 +1,111 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - a xmlrpc server and client for the notification bot
+
+    This is a bot for notification and simple editing
+    operations. Developed as a Google Summer of Code 
+    project.
+
+    @copyright: 2007 by Karol Nowak <grywacz@gmail.com>
+    @license: GNU GPL, see COPYING for details.
+"""
+
+import time, xmlrpclib
+from threading import Thread
+from SimpleXMLRPCServer import SimpleXMLRPCServer
+
+from commands import *
+
+
+class XMLRPCClient(Thread):
+    """XMLRPC Client
+    
+    It's responsible for performing XMLRPC operations on
+    a wiki, as inctructed by command objects received from
+    the XMPP component"""
+    
+    def __init__(self, config, commands):
+        """
+        @param commands: an output command queue
+        """
+        Thread.__init__(self)
+        self.commands = commands
+        self.config = config
+        
+    def run(self):
+        """Starts the server / thread"""
+        pass
+
+
+class XMLRPCServer(Thread):
+    """XMLRPC Server
+    
+    It waits for notifications requests coming from wiki,
+    creates command objects and puts them on a queue for
+    later processing by the XMPP component
+    
+    @param commands: an input command queue
+    """
+    
+    def __init__(self, config, commands):
+        Thread.__init__(self)
+        self.commands = commands
+        self.verbose = config.verbose
+        self.secret = config.secret
+        self.server = SimpleXMLRPCServer((config.xmlrpc_host, config.xmlrpc_port))
+        
+    def run(self):
+        """Starts the server / thread"""
+        
+        # Register methods having an "export" attribute as XML RPC functions and 
+        # decorate them with a check for a shared (wiki-bot) secret.
+        items = self.__class__.__dict__.items()
+        methods = [(name, func) for (name, func) in items if callable(func) 
+                   and "export" in func.__dict__]
+
+        for name, func in methods:
+            self.server.register_function(self.secret_check(func), name)
+        
+        self.server.serve_forever()
+        
+    def log(self, message):
+        """Logs a message and its timestamp"""
+        
+        t = time.localtime( time.time() )
+        print time.strftime("%H:%M:%S", t), message
+
+    def secret_check(self, function):
+        """Adds a check for a secret to a given function
+        
+        Using this one does not have to worry about checking for the secret
+        in every XML RPC function.
+        """
+        def protected_func(secret, *args):
+            if secret != self.secret:
+                raise xmlrpclib.Fault(1, "You are not allowed to use this bot!")
+            else:
+                return function(self, *args)
+            
+        return protected_func
+    
+    
+    def send_notification(self, jid, text):
+        """Instructs the XMPP component to send a notification"""           
+        c = NotificationCommand(jid, text)
+        self.commands.put_nowait(c)
+        return True
+    send_notification.export = True
+    
+    def addJIDToRoster(self, jid):
+        """Instructs the XMPP component to add a new JID to its roster"""  
+        c = AddJIDToRosterCommand(jid)
+        self.commands.put_nowait(c)
+        return True
+    addJIDToRoster.export = True
+    
+    def removeJIDFromRoster(self, jid):
+        """Instructs the XMPP component to remove a JID from its roster"""      
+        c = RemoveJIDFromRosterCommand(jid)
+        self.commands.put_nowait(c)
+        return True
+    removeJIDFromRoster.export = True
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/JabberBot/xmppbot.py	Wed Jun 06 20:45:31 2007 +0200
@@ -0,0 +1,364 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - a XMPP bot
+
+    This is a bot for notification and simple editing
+    operations. Developed as a Google Summer of Code 
+    project.
+
+    @copyright: 2007 by Karol Nowak <grywacz@gmail.com>
+    @license: GNU GPL, see COPYING for details.
+"""
+
+import time
+import Queue
+from threading import Thread
+
+from pyxmpp.client import Client
+from pyxmpp.jid import JID
+from pyxmpp.streamtls import TLSSettings
+from pyxmpp.message import Message
+
+from commands import *
+
+
+class Contact:
+    """Abstraction of a roster item / contact
+ 
+    This class handles some logic related to keeping track of
+    contact availability, status, etc."""
+    
+    def __init__(self, jid, resource, priority, show):
+        self.jid = jid
+        self.resources = { resource: {'show': show, 'priority': priority} }
+
+        # Queued messages, waiting for contact to change its "show"
+        # status to something different than "dnd". The messages should
+        # also be sent when contact becomes "unavailable" directly from
+        # "dnd", as we can't guarantee, that the bot will be up and running
+        # the next time she becomes "available".
+        self.messages = []
+        
+    def add_resource(self, resource, show, priority):
+        """Adds information about a connected resource"""
+        self.resources[resource] = {'show': show, 'priority': priority}
+    
+    def remove_resource(self, resource):
+        """Removes information about a connected resource"""
+        
+        if self.resources.has_key(resource):
+            del self.resources[resource]
+        else:
+            raise ValueError("No such resource!")
+        
+    def is_dnd(self):
+        """Checks if contact is DoNotDisturb
+        
+        The contact is DND if its resource with the highest priority is DND
+        """
+        
+        # Priority can't be lower than -128
+        max_prio = -129
+        max_prio_show = u"dnd"
+        
+        for resource in self.resources.itervalues():
+            # TODO: check RFC for behaviour of 2 resources with the same priority
+            if resource['priority'] > max_prio:
+                max_prio = resource['priority']
+                max_prio_show = resource['show']
+                
+        if max_prio_show == u'dnd':
+            return True
+        else:
+            return False
+        
+    def set_show(self, resource, show):
+        """Sets show property for a given resource
+        
+        @param resource: resource to alter
+        @param show: new value of the show property
+        @raise ValueError: no resource with given name has been found
+        """
+        if self.resources.has_key(resource):
+            self.resources[resource]['show'] = show
+        else:
+            raise ValueError("There's no such resource")
+    
+    def uses_resource(self, resource):
+        """Checks if contact uses a given resource"""
+        return self.resources.has_key(resource)
+        
+    def __str__(self):
+        retval = "%s (%s) has %d queued messages"
+        res = ", ".join([name + " is " + res['show'] for name, res in self.resources.items()])
+        return retval % (self.jid.as_utf8(), res, len(self.messages))
+
+
+class XMPPBot(Client, Thread):
+    """A simple XMPP bot"""
+       
+    def __init__(self, config, from_commands, to_commands):
+        """A constructor.
+        
+        @param from_commands: a Queue object used to send commands to other (xmlrpc) threads
+        @param to_commands: a Queue object used to receive commands from other threads
+        """
+        
+        Thread.__init__(self)
+        
+        self.from_commands = from_commands
+        self.to_commands = to_commands   
+        jid = u"%s@%s/%s" % (config.xmpp_node, config.xmpp_server, config.xmpp_resource)
+        
+        self.config = config
+        self.jid = JID(node_or_jid=jid, domain=config.xmpp_server, resource=config.xmpp_resource)
+        self.tlsconfig = TLSSettings(require = True, verify_peer=False)
+        
+        # A dictionary of contact objects, ordered by bare JID
+        self.contacts = { }
+        
+        Client.__init__(self, self.jid, config.xmpp_password, config.xmpp_server, tls_settings=self.tlsconfig)
+            
+    def run(self):
+        """Start the bot - enter the event loop"""
+        
+        if self.config.verbose:
+            self.log("Starting the jabber bot.")
+            
+        self.connect()
+        self.loop()
+        
+    def loop(self, timeout=1):
+        """Main event loop - stream and command handling"""
+        
+        while 1:
+            stream = self.get_stream()
+            if not stream:
+                break
+            
+            act = stream.loop_iter(timeout)
+            if not act:
+                # Process all available commands
+                while self.poll_commands(): pass
+                self.idle()
+        
+    def poll_commands(self):
+        """Checks for new commands in the input queue and executes them
+        
+        @return: True if any command has been executed, False otherwise."""
+        
+        try:
+            command = self.to_commands.get_nowait()
+            self.handle_command(command)
+            return True
+        except Queue.Empty:
+            return False
+        
+    def handle_command(self, command, ignore_dnd=False):
+        """Excecutes commands from other components"""
+        
+        if isinstance(command, NotificationCommand):
+            jid = JID(node_or_jid=command.jid)
+            jid_text = jid.bare().as_utf8()
+            text = command.text
+            
+            # Check if contact is DoNotDisturb. 
+            # If so, queue the message for delayed delivery.
+            try:
+                contact = self.contacts[jid_text]
+                if contact.is_dnd() and not ignore_dnd:
+                    contact.messages.append(command)
+                    return
+            except KeyError:
+                pass
+            
+            self.send_message(jid, text)
+        
+    def send_message(self, jid, text, msg_type=u"chat"):
+        """Sends a message
+        
+        @param jid: JID to send the message to
+        @param text: message's body
+        @param type: message type, as defined in RFC"""
+        
+        message = Message(to_jid=jid, body=text, stanza_type=msg_type)
+        self.get_stream().send(message)
+    
+    def handle_message(self, message):
+        """Handles incoming messages"""
+        
+        if self.config.verbose:
+            msg = "Message from %s." % (message.get_from_jid().as_utf8(),)
+            self.log(msg)
+            
+        text = message.get_body()
+        sender = message.get_from_jid()
+        command = text.split()
+        
+        # Ignore empty commands
+        if len(command) == 0:
+            return
+        
+        response = self.reply_help()
+        
+        if not response == u"":
+            self.send_message(sender, response)
+        
+    def handle_unavailable_presence(self, stanza):
+        """Handles unavailable presence stanzas"""
+        
+        if self.config.verbose:
+            self.log("Handling unavailable presence.")
+        
+        jid = stanza.get_from_jid()
+        bare_jid = jid.bare().as_utf8()
+        
+        # If we get presence, this contact should already be known
+        if bare_jid in self.contacts:    
+            contact = self.contacts[bare_jid]
+            
+            if self.config.verbose:
+                self.log("%s, going OFFLINE." % contact)
+            
+            try:
+                # Send queued messages now, as we can't guarantee to be 
+                # alive the next time this contact becomes available.
+                if len(contact.resources) == 1:    
+                    self.send_queued_messages(contact, ignore_dnd=True)
+                    del self.contacts[bare_jid]
+                else:
+                    contact.remove_resource(jid.resource)
+                    
+                    # The highest-priority resource, which used to be DnD might
+                    # have gone offline. If so, try to deliver messages now.
+                    if not contact.is_dnd():
+                        self.send_queued_messages(contact)
+                    
+            except ValueError:
+                self.log("Unknown contact (resource) going offline...")
+            
+        else:
+            self.log("Unavailable presence from unknown contact.")
+                
+        # Confirm that we've handled this stanza
+        return True
+    
+    def handle_available_presence(self, presence):
+        """Handles available presence stanzas"""
+        if self.config.verbose:
+            self.log("Handling available presence.")
+        
+        show = presence.get_show()
+        priority = presence.get_priority()
+        jid = presence.get_from_jid()
+        bare_jid = jid.bare().as_utf8()
+               
+        if bare_jid in self.contacts:    
+            contact = self.contacts[bare_jid]              
+                
+            # The resource is already known, so update it
+            if contact.uses_resource(jid.resource):
+                contact.set_show(jid.resource, show)
+            
+            # Unknown resource, add it to the list
+            else:
+                contact.add_resource(jid.resource, show, priority)
+
+            if self.config.verbose:
+                self.log(contact)  
+
+            # Either way check, if we can deliver queued messages now
+            if not contact.is_dnd():
+                self.send_queued_messages(contact)
+                
+        else:
+            self.contacts[bare_jid] = Contact(jid, jid.resource, priority, show)
+            
+            if self.config.verbose:
+                self.log(self.contacts[bare_jid])
+        
+        # Confirm that we've handled this stanza
+        return True
+    
+    def send_queued_messages(self, contact, ignore_dnd=False):
+        """Sends messages queued for the contact"""
+        for command in contact.messages:
+            self.handle_command(command, ignore_dnd)
+                    
+    def reply_help(self):
+        """Constructs a generic help message
+        
+        It's sent in response to an uknown message or the "help" command."""
+        
+        return u"""Hello there! I'm a MoinMoin Notification Bot. Too bad I can't say anything more (yet!)."""
+    
+    def log(self, message):
+        """Logs a message and its timestamp"""
+        
+        t = time.localtime( time.time() )
+        print time.strftime("%H:%M:%S", t), message
+    
+    def authenticated(self):
+        """Called when authentication succeedes"""
+        
+        if self.config.verbose:
+            self.log("Authenticated.")
+            
+    def authorized(self):
+        """Called when authorization succeedes"""
+        
+        if self.config.verbose:
+            self.log("Authorized.")
+        
+        stream = self.get_stream()
+        stream.set_message_handler("normal", self.handle_message)
+        stream.set_presence_handler("available", self.handle_available_presence)
+        stream.set_presence_handler("unavailable", self.handle_unavailable_presence)
+        
+        self.request_session()
+            
+    def connected(self):
+        """Called when connections has been established"""
+        
+        if self.config.verbose:
+            self.log("Connected.")
+            
+    def disconnected(self):
+        """Called when disconnection occurs"""
+        
+        if self.config.verbose:
+            self.log("Disconnected.")
+            
+    def roster_updated(self, item=None):
+        """Called when roster gets updated"""
+        
+        if self.config.verbose:
+            self.log("Updating roster.")
+            
+            contacts = [ str(c) for c in self.roster.get_items() ]
+            print "Groups:", self.roster.get_groups()
+            print "Contacts:", " ".join(contacts)
+            
+ #   def session_started(self):
+ #       """Called when session has been successfully started"""
+ #       
+ #       if self.config.verbose:
+ #           self.log("Session started.")
+            
+    def stream_closed(self, stream):
+        """Called when stream closes"""
+        
+        if self.config.verbose:
+            self.log("Stream closed.")
+            
+    def stream_created(self, stream):
+        """Called when stream gets created"""
+        
+        if self.config.verbose:
+            self.log("Stream created.")
+            
+    def stream_error(self, error):
+        """Called when stream error gets received"""
+        
+        if self.config.verbose:
+            self.log("Received a stream error.")
--- a/MoinMoin/jabber/__init__.py	Tue Jun 05 23:18:26 2007 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - jabber bot
-
-    This is a bot for notification and simple editing
-    operations. Developed as a Google Summer of Code 
-    project.
-
-@copyright: 2007 by Karol Nowak <grywacz@gmail.com>
-@license: GNU GPL, see COPYING for details.
-"""
--- a/MoinMoin/jabber/commands.py	Tue Jun 05 23:18:26 2007 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - inter-thread communication commands
-
-    This file defines command objects used by notification
-    bot's threads to communicate among each other.
-
-    @copyright: 2007 by Karol Nowak <grywacz@gmail.com>
-    @license: GNU GPL, see COPYING for details.
-"""
-
-# First, XML RPC -> XMPP commands
-class NotificationCommand:
-    """Class representing a notification request"""
-    def __init__(self, jid, text):
-        self.jid = jid
-        self.text = text
-        
-class AddJIDToRosterCommand:
-    """Class representing a request to add a new jid to roster"""
-    def __init__(self, jid):
-        self.jid = jid
-        
-class RemoveJIDFromRosterCommand:
-    """Class representing a request to remove a jid from roster"""
-    def __init__(self, jid):
-        self.jid = jid
--- a/MoinMoin/jabber/main.py	Tue Jun 05 23:18:26 2007 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - jabber bot main file
-
-    This is a bot for notification and simple editing
-    operations. Developed as a Google Summer of Code 
-    project.
-
-    @copyright: 2007 by Karol Nowak <grywacz@gmail.com>
-    @license: GNU GPL, see COPYING for details.
-"""
-
-import sys
-import os
-
-from config import Config
-from xmppbot import XMPPBot
-from xmlrpcbot import XMLRPCServer, XMLRPCClient
-from Queue import Queue
-
-
-def main():
-    args = sys.argv
-    
-    if "--help" in args:
-        print """MoinMoin notification bot
-        
-        Usage: %(myname)s [--server server] [--xmpp_port port] [--user user] [--resource resource] [--password pass] [--xmlrpc_host host] [--xmlrpc_port port]
-        """ % { "myname": os.path.basename(args[0]) }
-        
-        raise SystemExit
-    
-    # TODO: actually accept options from the help string
-
-    commands_from_xmpp = Queue()
-    commands_to_xmpp = Queue()
-    
-    try:
-        xmpp_bot = XMPPBot(Config, commands_from_xmpp, commands_to_xmpp)
-        xmlrpc_client = XMLRPCClient(Config, commands_from_xmpp)
-        xmlrpc_server = XMLRPCServer(Config, commands_to_xmpp)
-        
-        xmpp_bot.start()
-        xmlrpc_client.start()
-        xmlrpc_server.start()
-    
-    except KeyboardInterrupt, i:
-        print i
-        sys.exit(0)
-
-        
-if __name__ == "__main__": main()
--- a/MoinMoin/jabber/xmlrpcbot.py	Tue Jun 05 23:18:26 2007 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - a xmlrpc server and client for the notification bot
-
-    This is a bot for notification and simple editing
-    operations. Developed as a Google Summer of Code 
-    project.
-
-    @copyright: 2007 by Karol Nowak <grywacz@gmail.com>
-    @license: GNU GPL, see COPYING for details.
-"""
-
-import time, xmlrpclib
-from threading import Thread
-from SimpleXMLRPCServer import SimpleXMLRPCServer
-
-from commands import *
-
-
-class XMLRPCClient(Thread):
-    """XMLRPC Client
-    
-    It's responsible for performing XMLRPC operations on
-    a wiki, as inctructed by command objects received from
-    the XMPP component"""
-    
-    def __init__(self, config, commands):
-        """
-        @param commands: an output command queue
-        """
-        Thread.__init__(self)
-        self.commands = commands
-        self.config = config
-        
-    def run(self):
-        """Starts the server / thread"""
-        pass
-
-
-class XMLRPCServer(Thread):
-    """XMLRPC Server
-    
-    It waits for notifications requests coming from wiki,
-    creates command objects and puts them on a queue for
-    later processing by the XMPP component
-    
-    @param commands: an input command queue
-    """
-    
-    def __init__(self, config, commands):
-        Thread.__init__(self)
-        self.commands = commands
-        self.verbose = config.verbose
-        self.secret = config.secret
-        self.server = SimpleXMLRPCServer((config.xmlrpc_host, config.xmlrpc_port))
-        
-    def run(self):
-        """Starts the server / thread"""
-        
-        # Register methods having an "export" attribute as XML RPC functions and 
-        # decorate them with a check for a shared (wiki-bot) secret.
-        items = self.__class__.__dict__.items()
-        methods = [(name, func) for (name, func) in items if callable(func) 
-                   and "export" in func.__dict__]
-
-        for name, func in methods:
-            self.server.register_function(self.secret_check(func), name)
-        
-        self.server.serve_forever()
-        
-    def log(self, message):
-        """Logs a message and its timestamp"""
-        
-        t = time.localtime( time.time() )
-        print time.strftime("%H:%M:%S", t), message
-
-    def secret_check(self, function):
-        """Adds a check for a secret to a given function
-        
-        Using this one does not have to worry about checking for the secret
-        in every XML RPC function.
-        """
-        def protected_func(secret, *args):
-            if secret != self.secret:
-                raise xmlrpclib.Fault(1, "You are not allowed to use this bot!")
-            else:
-                return function(self, *args)
-            
-        return protected_func
-    
-    
-    def send_notification(self, jid, text):
-        """Instructs the XMPP component to send a notification"""           
-        c = NotificationCommand(jid, text)
-        self.commands.put_nowait(c)
-        return True
-    send_notification.export = True
-    
-    def addJIDToRoster(self, jid):
-        """Instructs the XMPP component to add a new JID to its roster"""  
-        c = AddJIDToRosterCommand(jid)
-        self.commands.put_nowait(c)
-        return True
-    addJIDToRoster.export = True
-    
-    def removeJIDFromRoster(self, jid):
-        """Instructs the XMPP component to remove a JID from its roster"""      
-        c = RemoveJIDFromRosterCommand(jid)
-        self.commands.put_nowait(c)
-        return True
-    removeJIDFromRoster.export = True
--- a/MoinMoin/jabber/xmppbot.py	Tue Jun 05 23:18:26 2007 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,364 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - a XMPP bot
-
-    This is a bot for notification and simple editing
-    operations. Developed as a Google Summer of Code 
-    project.
-
-    @copyright: 2007 by Karol Nowak <grywacz@gmail.com>
-    @license: GNU GPL, see COPYING for details.
-"""
-
-import time
-import Queue
-from threading import Thread
-
-from pyxmpp.client import Client
-from pyxmpp.jid import JID
-from pyxmpp.streamtls import TLSSettings
-from pyxmpp.message import Message
-
-from commands import *
-
-
-class Contact:
-    """Abstraction of a roster item / contact
- 
-    This class handles some logic related to keeping track of
-    contact availability, status, etc."""
-    
-    def __init__(self, jid, resource, priority, show):
-        self.jid = jid
-        self.resources = { resource: {'show': show, 'priority': priority} }
-
-        # Queued messages, waiting for contact to change its "show"
-        # status to something different than "dnd". The messages should
-        # also be sent when contact becomes "unavailable" directly from
-        # "dnd", as we can't guarantee, that the bot will be up and running
-        # the next time she becomes "available".
-        self.messages = []
-        
-    def add_resource(self, resource, show, priority):
-        """Adds information about a connected resource"""
-        self.resources[resource] = {'show': show, 'priority': priority}
-    
-    def remove_resource(self, resource):
-        """Removes information about a connected resource"""
-        
-        if self.resources.has_key(resource):
-            del self.resources[resource]
-        else:
-            raise ValueError("No such resource!")
-        
-    def is_dnd(self):
-        """Checks if contact is DoNotDisturb
-        
-        The contact is DND if its resource with the highest priority is DND
-        """
-        
-        # Priority can't be lower than -128
-        max_prio = -129
-        max_prio_show = u"dnd"
-        
-        for resource in self.resources.itervalues():
-            # TODO: check RFC for behaviour of 2 resources with the same priority
-            if resource['priority'] > max_prio:
-                max_prio = resource['priority']
-                max_prio_show = resource['show']
-                
-        if max_prio_show == u'dnd':
-            return True
-        else:
-            return False
-        
-    def set_show(self, resource, show):
-        """Sets show property for a given resource
-        
-        @param resource: resource to alter
-        @param show: new value of the show property
-        @raise ValueError: no resource with given name has been found
-        """
-        if self.resources.has_key(resource):
-            self.resources[resource]['show'] = show
-        else:
-            raise ValueError("There's no such resource")
-    
-    def uses_resource(self, resource):
-        """Checks if contact uses a given resource"""
-        return self.resources.has_key(resource)
-        
-    def __str__(self):
-        retval = "%s (%s) has %d queued messages"
-        res = ", ".join([name + " is " + res['show'] for name, res in self.resources.items()])
-        return retval % (self.jid.as_utf8(), res, len(self.messages))
-
-
-class XMPPBot(Client, Thread):
-    """A simple XMPP bot"""
-       
-    def __init__(self, config, from_commands, to_commands):
-        """A constructor.
-        
-        @param from_commands: a Queue object used to send commands to other (xmlrpc) threads
-        @param to_commands: a Queue object used to receive commands from other threads
-        """
-        
-        Thread.__init__(self)
-        
-        self.from_commands = from_commands
-        self.to_commands = to_commands   
-        jid = u"%s@%s/%s" % (config.xmpp_node, config.xmpp_server, config.xmpp_resource)
-        
-        self.config = config
-        self.jid = JID(node_or_jid=jid, domain=config.xmpp_server, resource=config.xmpp_resource)
-        self.tlsconfig = TLSSettings(require = True, verify_peer=False)
-        
-        # A dictionary of contact objects, ordered by bare JID
-        self.contacts = { }
-        
-        Client.__init__(self, self.jid, config.xmpp_password, config.xmpp_server, tls_settings=self.tlsconfig)
-            
-    def run(self):
-        """Start the bot - enter the event loop"""
-        
-        if self.config.verbose:
-            self.log("Starting the jabber bot.")
-            
-        self.connect()
-        self.loop()
-        
-    def loop(self, timeout=1):
-        """Main event loop - stream and command handling"""
-        
-        while 1:
-            stream = self.get_stream()
-            if not stream:
-                break
-            
-            act = stream.loop_iter(timeout)
-            if not act:
-                # Process all available commands
-                while self.poll_commands(): pass
-                self.idle()
-        
-    def poll_commands(self):
-        """Checks for new commands in the input queue and executes them
-        
-        @return: True if any command has been executed, False otherwise."""
-        
-        try:
-            command = self.to_commands.get_nowait()
-            self.handle_command(command)
-            return True
-        except Queue.Empty:
-            return False
-        
-    def handle_command(self, command, ignore_dnd=False):
-        """Excecutes commands from other components"""
-        
-        if isinstance(command, NotificationCommand):
-            jid = JID(node_or_jid=command.jid)
-            jid_text = jid.bare().as_utf8()
-            text = command.text
-            
-            # Check if contact is DoNotDisturb. 
-            # If so, queue the message for delayed delivery.
-            try:
-                contact = self.contacts[jid_text]
-                if contact.is_dnd() and not ignore_dnd:
-                    contact.messages.append(command)
-                    return
-            except KeyError:
-                pass
-            
-            self.send_message(jid, text)
-        
-    def send_message(self, jid, text, msg_type=u"chat"):
-        """Sends a message
-        
-        @param jid: JID to send the message to
-        @param text: message's body
-        @param type: message type, as defined in RFC"""
-        
-        message = Message(to_jid=jid, body=text, stanza_type=msg_type)
-        self.get_stream().send(message)
-    
-    def handle_message(self, message):
-        """Handles incoming messages"""
-        
-        if self.config.verbose:
-            msg = "Message from %s." % (message.get_from_jid().as_utf8(),)
-            self.log(msg)
-            
-        text = message.get_body()
-        sender = message.get_from_jid()
-        command = text.split()
-        
-        # Ignore empty commands
-        if len(command) == 0:
-            return
-        
-        response = self.reply_help()
-        
-        if not response == u"":
-            self.send_message(sender, response)
-        
-    def handle_unavailable_presence(self, stanza):
-        """Handles unavailable presence stanzas"""
-        
-        if self.config.verbose:
-            self.log("Handling unavailable presence.")
-        
-        jid = stanza.get_from_jid()
-        bare_jid = jid.bare().as_utf8()
-        
-        # If we get presence, this contact should already be known
-        if bare_jid in self.contacts:    
-            contact = self.contacts[bare_jid]
-            
-            if self.config.verbose:
-                self.log("%s, going OFFLINE." % contact)
-            
-            try:
-                # Send queued messages now, as we can't guarantee to be 
-                # alive the next time this contact becomes available.
-                if len(contact.resources) == 1:    
-                    self.send_queued_messages(contact, ignore_dnd=True)
-                    del self.contacts[bare_jid]
-                else:
-                    contact.remove_resource(jid.resource)
-                    
-                    # The highest-priority resource, which used to be DnD might
-                    # have gone offline. If so, try to deliver messages now.
-                    if not contact.is_dnd():
-                        self.send_queued_messages(contact)
-                    
-            except ValueError:
-                self.log("Unknown contact (resource) going offline...")
-            
-        else:
-            self.log("Unavailable presence from unknown contact.")
-                
-        # Confirm that we've handled this stanza
-        return True
-    
-    def handle_available_presence(self, presence):
-        """Handles available presence stanzas"""
-        if self.config.verbose:
-            self.log("Handling available presence.")
-        
-        show = presence.get_show()
-        priority = presence.get_priority()
-        jid = presence.get_from_jid()
-        bare_jid = jid.bare().as_utf8()
-               
-        if bare_jid in self.contacts:    
-            contact = self.contacts[bare_jid]              
-                
-            # The resource is already known, so update it
-            if contact.uses_resource(jid.resource):
-                contact.set_show(jid.resource, show)
-            
-            # Unknown resource, add it to the list
-            else:
-                contact.add_resource(jid.resource, show, priority)
-
-            if self.config.verbose:
-                self.log(contact)  
-
-            # Either way check, if we can deliver queued messages now
-            if not contact.is_dnd():
-                self.send_queued_messages(contact)
-                
-        else:
-            self.contacts[bare_jid] = Contact(jid, jid.resource, priority, show)
-            
-            if self.config.verbose:
-                self.log(self.contacts[bare_jid])
-        
-        # Confirm that we've handled this stanza
-        return True
-    
-    def send_queued_messages(self, contact, ignore_dnd=False):
-        """Sends messages queued for the contact"""
-        for command in contact.messages:
-            self.handle_command(command, ignore_dnd)
-                    
-    def reply_help(self):
-        """Constructs a generic help message
-        
-        It's sent in response to an uknown message or the "help" command."""
-        
-        return u"""Hello there! I'm a MoinMoin Notification Bot. Too bad I can't say anything more (yet!)."""
-    
-    def log(self, message):
-        """Logs a message and its timestamp"""
-        
-        t = time.localtime( time.time() )
-        print time.strftime("%H:%M:%S", t), message
-    
-    def authenticated(self):
-        """Called when authentication succeedes"""
-        
-        if self.config.verbose:
-            self.log("Authenticated.")
-            
-    def authorized(self):
-        """Called when authorization succeedes"""
-        
-        if self.config.verbose:
-            self.log("Authorized.")
-        
-        stream = self.get_stream()
-        stream.set_message_handler("normal", self.handle_message)
-        stream.set_presence_handler("available", self.handle_available_presence)
-        stream.set_presence_handler("unavailable", self.handle_unavailable_presence)
-        
-        self.request_session()
-            
-    def connected(self):
-        """Called when connections has been established"""
-        
-        if self.config.verbose:
-            self.log("Connected.")
-            
-    def disconnected(self):
-        """Called when disconnection occurs"""
-        
-        if self.config.verbose:
-            self.log("Disconnected.")
-            
-    def roster_updated(self, item=None):
-        """Called when roster gets updated"""
-        
-        if self.config.verbose:
-            self.log("Updating roster.")
-            
-            contacts = [ str(c) for c in self.roster.get_items() ]
-            print "Groups:", self.roster.get_groups()
-            print "Contacts:", " ".join(contacts)
-            
- #   def session_started(self):
- #       """Called when session has been successfully started"""
- #       
- #       if self.config.verbose:
- #           self.log("Session started.")
-            
-    def stream_closed(self, stream):
-        """Called when stream closes"""
-        
-        if self.config.verbose:
-            self.log("Stream closed.")
-            
-    def stream_created(self, stream):
-        """Called when stream gets created"""
-        
-        if self.config.verbose:
-            self.log("Stream created.")
-            
-    def stream_error(self, error):
-        """Called when stream error gets received"""
-        
-        if self.config.verbose:
-            self.log("Received a stream error.")