fixbot/api.py
changeset 67 00907acd732a
parent 66 eb0545ec03e7
parent 64 8574aeff9b36
equal deleted inserted replaced
66:eb0545ec03e7 67:00907acd732a
     1 from twisted.application import internet, service
       
     2 from twisted.internet import protocol, reactor
       
     3 from twisted.python import log, usage
       
     4 from datetime import datetime
       
     5 import sys
       
     6 
       
     7 from fixbot import buffer, config
       
     8 
       
     9 class ModuleInfo (object) :
       
    10     """
       
    11         Some info about a module
       
    12     """
       
    13 
       
    14     # module's name
       
    15     name = None
       
    16 
       
    17     def __str__ (self) :
       
    18         return "Module %s:" % (self.name)
       
    19     
       
    20     def __repr__ (self) :
       
    21         return "<module %s>" % (self.name, )
       
    22 
       
    23 class Event (object) :
       
    24     # the ModuleInfo object
       
    25     module = None
       
    26 
       
    27     # the event type as a string
       
    28     type = None
       
    29 
       
    30     # event message as a string (up to 64k, although that won't fit onto IRC..)
       
    31     msg = None
       
    32 
       
    33     # timestamp as a datetime.datetime
       
    34     when = None
       
    35 
       
    36     def __init__ (self, module, type, msg) :
       
    37         self.module = module
       
    38         self.type = type
       
    39         self.msg = msg
       
    40 
       
    41         self.when = datetime.now()
       
    42     
       
    43     def __str__ (self) :
       
    44         return "[%s] %s" % (self.type, self.msg)
       
    45     
       
    46     def __repr__ (self) :
       
    47         return "%s @ %s" % (self.type, self.when)
       
    48  
       
    49 CLIENT_COMMANDS = [
       
    50     "module_init",
       
    51     "module_event",
       
    52 ]
       
    53 
       
    54 SERVER_COMMANDS = [
       
    55     "module_ok",
       
    56 ]
       
    57        
       
    58 class ServerProtocol (buffer.StreamProtocol, protocol.Protocol) :
       
    59     RECV_COMMANDS = CLIENT_COMMANDS
       
    60     SEND_COMMANDS = SERVER_COMMANDS    
       
    61 
       
    62     VALID_STATES = [
       
    63         "wait_init",
       
    64         "wait_event"
       
    65     ]
       
    66     
       
    67     # proto state
       
    68     state = None
       
    69 
       
    70     # module info
       
    71     module = None
       
    72     
       
    73     def _assert (self, condition, msg) :
       
    74         if not condition :
       
    75             self.transport.loseConnection()
       
    76             log.err("assert failed in APIProtocol for %s: %s" % (self.module, msg))
       
    77     
       
    78     def connectionMade (self) :
       
    79         log.msg("Client connected")
       
    80 
       
    81     def connectionLost (self, reason) :
       
    82         log.msg("Connection lost: %s" % reason)
       
    83         
       
    84         if self.module :
       
    85             self.factory.nexus.unregisterModule(self.module, reason.getErrorMessage())
       
    86 
       
    87     def on_module_init (self, i) :
       
    88         self._assert(not self.module, "module_init with non-None self.module")
       
    89 
       
    90         peer_secret = i.readVarLen('B')
       
    91         
       
    92         self._assert(peer_secret == self.factory.secret, "Mismatching API secrets!")
       
    93 
       
    94         m = ModuleInfo()
       
    95         
       
    96         m.name = i.readVarLen('B')
       
    97         m.addr = self.transport.getPeer()
       
    98 
       
    99         log.msg("Got mod_init for %r" % m)
       
   100         
       
   101         self.factory.nexus.registerModule(m, self)
       
   102 
       
   103         self.module = m
       
   104 
       
   105         o = self.startCommand('module_ok')
       
   106 
       
   107         self.send(o)
       
   108 
       
   109     def on_module_event (self, i) :
       
   110         self._assert(self.module, "module_event with None self.module!")
       
   111 
       
   112         event_type = i.readVarLen('B')
       
   113         event_msg = i.readVarLen('H')
       
   114         
       
   115         e = Event(self.module, event_type, event_msg)
       
   116 
       
   117         self.factory.nexus.handleEvent(e)
       
   118 
       
   119     def logPrefix (self) :
       
   120         if self.module :
       
   121             return str(self.module)
       
   122         else :
       
   123             return super(ServerProtocol, self).logPrefix()
       
   124 
       
   125 class ClientProtocol (buffer.StreamProtocol, protocol.Protocol) :
       
   126     RECV_COMMANDS = SERVER_COMMANDS
       
   127     SEND_COMMANDS = CLIENT_COMMANDS
       
   128 
       
   129     def connectionMade (self) :
       
   130         log.msg("Connected to API server, sending module init message")
       
   131 
       
   132         o = self.startCommand('module_init')
       
   133         o.writeVarLen('B', self.factory.secret)
       
   134         o.writeVarLen('B', self.factory.name)
       
   135 
       
   136         self.send(o)
       
   137 
       
   138     def sendEvent (self, event) :
       
   139         o = self.startCommand('module_event')
       
   140         o.writeVarLen('B', event.type)
       
   141         o.writeVarLen('H', event.msg[:2**16])
       
   142 
       
   143         self.send(o)
       
   144 
       
   145     def on_module_ok (self, i) :
       
   146         log.msg("Registration OK")
       
   147 
       
   148         self.factory.connected(self)
       
   149     
       
   150     def logPrefix (self) :
       
   151         return "module %s client" % (self.factory.name)
       
   152 
       
   153 class Module (ModuleInfo, protocol.ClientFactory) :
       
   154     protocol = ClientProtocol
       
   155 
       
   156     def __init__ (self, config) :
       
   157         self.connection = None
       
   158         self.secret = config['api-secret']
       
   159 
       
   160     def connected (self, connection) :
       
   161         log.msg("Connected!")
       
   162         self.connection = connection
       
   163 
       
   164         self.handleConnect()
       
   165 
       
   166     def disconnect (self) :
       
   167         self.connection.transport.loseConnection()
       
   168     
       
   169     def sendEvent (self, type, msg) :
       
   170         self.connection.sendEvent(self.buildEvent(type, msg))
       
   171 
       
   172     def buildEvent (self, type, msg) :
       
   173         return Event(self, type, msg)
       
   174 
       
   175     def handleConnect (self) :
       
   176         """
       
   177             Do something
       
   178         """
       
   179 
       
   180         pass
       
   181 
       
   182 class ServerFactory (protocol.ServerFactory) :
       
   183     protocol = ServerProtocol
       
   184 
       
   185     def __init__ (self, nexus, secret) :
       
   186         self.nexus = nexus
       
   187         self.secret = secret
       
   188 
       
   189 class ClientOptions (config.ConfigOptions) :
       
   190     optParameters = [
       
   191         (   "api-server",   "s",    "127.0.0.1",        "address of API server to connect to"           ),
       
   192         (   "api-port",     "P",    34888,              "port of API server to connect to",     int     ),
       
   193         (   "api-secret",   None,   None,               "secret key for API connections"                ),
       
   194     ]
       
   195 
       
   196     optFlags = [
       
   197 
       
   198     ]
       
   199 
       
   200 def makeService (module_class, config) :
       
   201     s = service.MultiService()
       
   202 
       
   203     # build factory
       
   204     factory = module_class(config)
       
   205     
       
   206     # the API client
       
   207     log.msg("Connecting to API server on [%s:%d]" % (config['api-server'], config['api-port']))
       
   208     api_client = internet.TCPClient(config['api-server'], config['api-port'], factory)
       
   209     
       
   210     api_client.setServiceParent(s)
       
   211 
       
   212     return s
       
   213