fixbot/api.py
changeset 21 aa6df8f9c44a
child 23 67e71e9170e5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fixbot/api.py	Mon Sep 15 00:27:05 2008 +0300
@@ -0,0 +1,212 @@
+from twisted.internet import protocol, reactor
+from twisted.python import log
+from datetime import datetime
+import sys
+
+import buffer
+
+from api_secret import secret
+
+class ModuleInfo (object) :
+    """
+        Some info about a module
+    """
+
+    # module's name
+    name = None
+
+    # module's version, as a 16-bit integer
+    version = None
+
+    # list of valid event types (strings)
+    event_types = None
+
+    def __str__ (self) :
+        return "Module %s:%d" % (self.name, self.version)
+    
+    def __repr__ (self) :
+        return "<module %s:%d with events: %s>" % (self.name, self.version, ", ".join(self.event_types))
+
+class Event (object) :
+    # the ModuleInfo object
+    module = None
+
+    # the event type as a string
+    type = None
+
+    # event message as a string (under 255 bytes in length!)
+    msg = None
+
+    # timestamp as a datetime.datetime
+    when = None
+
+    def __init__ (self, module, type, msg) :
+        assert type in module.event_types, "Invalid event-type %s for %r" % (type, self.module)
+        
+        self.module = module
+        self.type = type
+        self.msg = msg
+
+        self.when = datetime.now()
+    
+    def __str__ (self) :
+        return "[%s] %s" % (self.type, self.msg)
+    
+    def __repr__ (self) :
+        return "%s @ %s" % (self.type, self.when)
+ 
+CLIENT_COMMANDS = [
+    "module_init",
+    "module_event",
+]
+
+SERVER_COMMANDS = [
+    "module_ok",
+]
+       
+class ServerProtocol (buffer.StreamProtocol, protocol.Protocol) :
+    RECV_COMMANDS = CLIENT_COMMANDS
+    SEND_COMMANDS = SERVER_COMMANDS    
+
+    VALID_STATES = [
+        "wait_init",
+        "wait_event"
+    ]
+    
+    # proto state
+    state = None
+
+    # module info
+    module = None
+    
+    def _assert (self, condition, msg) :
+        if not condition :
+            self.transport.loseConnection()
+            log.err("assert failed in APIProtocol for %s: %s" % (self.module, msg))
+    
+    def connectionMade (self) :
+        log.msg("Client connected")
+
+    def connectionLost (self, reason) :
+        log.msg("Connection lost: %s" % reason)
+        
+        if self.module :
+            self.factory.nexus.unregisterModule(self.module, reason.getErrorMessage())
+
+    def on_module_init (self, i) :
+        self._assert(not self.module, "module_init with non-None self.module")
+
+        peer_secret = i.readVarLen('B')
+        
+        self._assert(peer_secret == secret, "Mismatching API secrets!")
+
+        m = ModuleInfo()
+        
+        m.name = i.readVarLen('B')
+        m.version = i.readItem('H')
+
+        m.event_types = list(buffer.readStringStream(i, 'B'))
+        m.addr = self.transport.getPeer()
+
+        self.module_name = m.name
+
+        log.msg("Got mod_init for %r" % m)
+        
+        self.factory.nexus.registerModule(m, self)
+
+        self.module = m
+
+        o = self.startCommand('module_ok')
+
+        self.send(o)
+
+    def on_module_event (self, i) :
+        self._assert(self.module, "module_event with None self.module!")
+
+        event_type = i.readEnum(self.module.event_types)
+        event_msg = i.readVarLen('B')
+        
+        e = Event(self.module, event_type, event_msg)
+
+#        log.msg("Got mod_event of %r" % (e))
+
+        self.factory.nexus.handleEvent(e)
+
+    def logPrefix (self) :
+        if self.module :
+            return str(self.module)
+        else :
+            return super(ServerProtocol, self).logPrefix()
+
+class ClientProtocol (buffer.StreamProtocol, protocol.Protocol) :
+    RECV_COMMANDS = SERVER_COMMANDS
+    SEND_COMMANDS = CLIENT_COMMANDS
+
+    def connectionMade (self) :
+        log.msg("Connected to API server, sending module init message")
+
+        o = self.startCommand('module_init')
+        o.writeVarLen('B', secret)
+        o.writeVarLen('B', self.factory.name)
+        o.writeItem("H", self.factory.version)
+        buffer.writeStringStream(o, 'B', self.factory.event_types)
+
+        self.send(o)
+
+    def sendEvent (self, event) :
+        o = self.startCommand('module_event')
+        o.writeEnum(self.factory.event_types, event.type)
+        o.writeVarLen('B', event.msg)
+
+        self.send(o)
+
+    def on_module_ok (self, i) :
+        log.msg("Registration OK")
+
+        self.factory.connected(self)
+    
+    def logPrefix (self) :
+        return "module %s:%d client" % (self.factory.name, self.factory.version)
+
+class Module (ModuleInfo, protocol.ClientFactory) :
+    protocol = ClientProtocol
+
+    def __init__ (self) :
+        log.msg("Connecting to %s:%d" % (SERVER_HOST, PORT))
+        reactor.connectTCP(SERVER_HOST, PORT, self)
+
+        self.connection = None
+
+    def run (self) :
+        log.startLogging(sys.stderr)
+    
+        reactor.run()
+        
+    def connected (self, connection) :
+        log.msg("Connected!")
+        self.connection = connection
+
+        self.handleConnect()
+
+    def disconnect (self) :
+        self.connection.transport.loseConnection()
+    
+    def sendEvent (self, type, msg) :
+        self.connection.sendEvent(self.buildEvent(type, msg))
+
+    def buildEvent (self, type, msg) :
+        return Event(self, type, msg)
+
+    def handleConnect (self) :
+        """
+            Do something
+        """
+
+        pass
+
+class ServerFactory (protocol.ServerFactory) :
+    protocol = ServerProtocol
+
+    def __init__ (self, nexus) :
+        self.nexus = nexus
+