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 |
|