changeset 2070:4e123eb32380

Handle inbound xmlrpc traffic, send notifications. This is only a beginning. ;)
author Karol 'grzywacz' Nowak <grzywacz@sul.uni.lodz.pl>
date Thu, 31 May 2007 21:23:16 +0200
parents 09eeb9cb9afc
children aa761813d7bd
files MoinMoin/jabber/main.py MoinMoin/jabber/xmlrpcbot.py MoinMoin/jabber/xmppbot.py
diffstat 3 files changed, 104 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/jabber/main.py	Thu May 31 21:21:38 2007 +0200
+++ b/MoinMoin/jabber/main.py	Thu May 31 21:23:16 2007 +0200
@@ -11,19 +11,33 @@
 """
 
 import sys
-from config import JabberConfig
+import os
+
+from config import Config
 from xmppbot import XMPPBot
 from xmlrpcbot import XMLRPCServer, XMLRPCClient
 from Queue import Queue
 
-def main():
+if __name__ == "__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(JabberConfig, commands_from_xmpp, commands_to_xmpp)
-        xmlrpc_client = XMLRPCClient(commands_from_xmpp)
-        xmlrpc_server = XMLRPCServer(commands_to_xmpp)
+        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()
@@ -31,6 +45,4 @@
     
     except KeyboardInterrupt, i:
         print i
-        sys.exit(0)
-
-if __name__ == "__main__": main()
\ No newline at end of file
+        sys.exit(0)
\ No newline at end of file
--- a/MoinMoin/jabber/xmlrpcbot.py	Thu May 31 21:21:38 2007 +0200
+++ b/MoinMoin/jabber/xmlrpcbot.py	Thu May 31 21:23:16 2007 +0200
@@ -10,9 +10,11 @@
     @license: GNU GPL, see COPYING for details.
 """
 
+import time
 from threading import Thread
+from SimpleXMLRPCServer import SimpleXMLRPCServer 
 
-class Notification:
+class NotificationCommand:
     """Class representing a notification request"""
     
     def __init__(self, jid, text):
@@ -26,17 +28,41 @@
     a wiki, as inctructed by command objects received from
     the XMPP component"""
     
-    def __init__(self, commands):
+    def __init__(self, config, commands):
         Thread.__init__(self)
         self.commands = commands
+        
+    def run(self):
+        pass
 
-class XMLRPCServer(Thread):
+class XMLRPCServer(Thread, SimpleXMLRPCServer):
     """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"""
     
-    def __init__(self, commands):
+    def __init__(self, config, commands):
         Thread.__init__(self)
-        self.commands = commands
\ No newline at end of file
+        SimpleXMLRPCServer.__init__(self, (config.xmlrpc_host, config.xmlrpc_port) )
+        self.commands = commands
+        self.verbose = config.verbose
+        
+    def run(self):
+        """Starts the server / thread"""
+        
+        self.register_function(self.send_notification)
+        self.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 send_notification(self, jid, text):
+        """Instructs the XMPP component to send a notification"""
+        
+        n = NotificationCommand(jid, text)
+        self.commands.put_nowait(n)
+        return True
--- a/MoinMoin/jabber/xmppbot.py	Thu May 31 21:21:38 2007 +0200
+++ b/MoinMoin/jabber/xmppbot.py	Thu May 31 21:23:16 2007 +0200
@@ -6,11 +6,12 @@
     operations. Developed as a Google Summer of Code 
     project.
 
-@copyright: 2007 by Karol Nowak <grywacz@gmail.com>
-@license: GNU GPL, see COPYING for details.
+    @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
@@ -19,6 +20,8 @@
 from pyxmpp.message import Message
 from pyxmpp.presence import Presence
 
+from xmlrpcbot import NotificationCommand
+
 class XMPPBot(Client, Thread):
     """A simple XMPP bot"""
     
@@ -33,26 +36,59 @@
         
         self.from_commands = from_commands
         self.to_commands = to_commands   
-        jid = "%s@%s/%s" % (config.node, config.server, config.resource)
+        jid = "%s@%s/%s" % (config.xmpp_node, config.xmpp_server, config.xmpp_resource)
         
         self.config = config
-        self.jid = JID(node_or_jid = jid, domain = config.server, resource = config.resource)
+        self.jid = JID(node_or_jid = jid, domain = config.xmpp_server, resource = config.xmpp_resource)
         self.tlsconfig = TLSSettings(require = True, verify_peer = False)
-        Client.__init__(self, self.jid, self.config.password, self.config.server, tls_settings = self.tlsconfig)
+        Client.__init__(self, self.jid, self.config.xmpp_password, self.config.xmpp_server, tls_settings = self.tlsconfig)
             
     def run(self):
         """Start the bot - enter the event loop"""
         
-        if self.config.verbose == True:
+        if self.config:
             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:
+                self.poll_commands()
+                self.idle()
+        
+    def poll_commands(self):
+        """Checks for new commands in the input queue and executes them"""
+        
+        try:
+            command = self.to_commands.get_nowait()
+            self.handle_command(command)
+        except Queue.Empty:
+            pass
+        
+    def handle_command(self, command):
+        """Excecutes commands from other components"""
+        
+        if isinstance(command, NotificationCommand):
+            jid = JID(node_or_jid=command.jid)
+            text = command.text
+            self.send_message(jid, text)
+        
+    def send_message(self, to, text, type=u"chat"):
+        message = Message(to_jid = to, body = text, stanza_type=type)
+        self.get_stream().send(message)
+    
     def handle_message(self, message):
         """Handles incoming messages"""
         
-        if self.config.verbose == True:
+        if self.config:
             self.log( "Received a message from %s." % (message.get_from_jid().as_utf8(),) )
             
         text = message.get_body()
@@ -66,8 +102,7 @@
         response = self.reply_help()
         
         if not response == u"":
-            message = Message(to_jid = sender, body = response, stanza_type=u"chat")
-            self.get_stream().send(message)
+            self.send_message(sender, response)
         
     def handle_presence(self):
         pass
@@ -98,13 +133,13 @@
     def authenticated(self):
         """Called when authentication succeedes"""
         
-        if self.config.verbose == True:
+        if self.config:
             self.log("Authenticated.")
             
     def authorized(self):
         """Called when authorization succeedes"""
         
-        if self.config.verbose == True:
+        if self.config:
             self.log("Authorized.")
         
         stream = self.get_stream()
@@ -121,41 +156,41 @@
     def connected(self):
         """Called when connections has been established"""
         
-        if self.config.verbose == True:
+        if self.config:
             self.log("Connected.")
             
     def disconnected(self):
         """Called when disconnection occurs"""
         
-        if self.config.verbose == True:
+        if self.config:
             self.log("Disconnected.")
             
     def roster_updated(self, item = None):
         """Called when roster gets updated"""
         
-        if self.config.verbose == True:
+        if self.config:
             self.log("Updating roster.")
             
  #   def session_started(self):
  #       """Called when session has been successfully started"""
  #       
- #       if self.config.verbose == True:
+ #       if self.config.verbose:
  #           self.log("Session started.")
             
     def stream_closed(self, stream):
         """Called when stream closes"""
         
-        if self.config.verbose == True:
+        if self.config.verbose:
             self.log("Stream closed.")
             
     def stream_created(self, stream):
         """Called when stream gets created"""
         
-        if self.config.verbose == True:
+        if self.config.verbose:
             self.log("Stream created.")
             
     def stream_error(self, error):
         """Called when stream error gets received"""
         
-        if self.config.verbose == True:
+        if self.config.verbose:
             self.log("Received a stream error.")