rename plugin fixbot -> fixbot_nexus, add fixbot_logwatch plugin, fix some random bugs
from twisted.application import internet, service
from twisted.internet import protocol, reactor
from twisted.python import log, usage
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) :
self.connection = None
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
class ClientOptions (usage.Options) :
optParameters = [
( "api-server", "s", "127.0.0.1", "address of API server to connect to" ),
( "api-port", "P", 34888, "port of API server to connect to", int ),
]
optFlags = [
]
def makeService (client_module_factory, config) :
s = service.MultiService()
# the API client
module_factory = client_module_factory()
log.msg("Connecting to API server on [%s:%d]" % (config['api-server'], config['api-port']))
api_client = internet.TCPClient(config['api-server'], config['api-port'], module_factory)
api_client.setServiceParent(s)
return s