Mercurial > xib
comparison bot.py @ 180:102f895347ff
added a required "importance" argument to Bot.error()
Signed-off-by: Charly COSTE <changaco@changaco.net>
author | Charly COSTE <changaco@changaco.net> |
---|---|
date | Sat, 13 Feb 2010 16:32:28 +0100 |
parents | f6c6708c6c0e |
children | 803e00d72cb7 |
comparison
equal
deleted
inserted
replaced
179:f6c6708c6c0e | 180:102f895347ff |
---|---|
28 del muc | 28 del muc |
29 | 29 |
30 from bridge import Bridge | 30 from bridge import Bridge |
31 from participant import Participant | 31 from participant import Participant |
32 import commands | 32 import commands |
33 import say_levels | |
33 | 34 |
34 | 35 |
35 class Bot(threading.Thread): | 36 class Bot(threading.Thread): |
36 | 37 |
37 def __init__(self, jid, password, nickname, admins_jid=[], error_fd=sys.stderr, debug=False): | 38 def __init__(self, jid, password, nickname, admins=[], error_fd=sys.stderr, debug=False): |
38 threading.Thread.__init__(self) | 39 threading.Thread.__init__(self) |
39 self.halt = False | 40 self.halt = False |
40 self.bridges = [] | 41 self.bridges = [] |
41 self.bare_jid = xmpp.protocol.JID(jid=jid) | 42 self.bare_jid = xmpp.protocol.JID(jid=jid) |
42 self.bare_jid.setResource('') | 43 self.bare_jid.setResource('') |
43 self.nickname = nickname | 44 self.nickname = nickname |
44 self.password = password | 45 self.password = password |
45 self.error_fd = error_fd | 46 self.error_fd = error_fd |
46 self.debug = debug | 47 self.debug = debug |
47 self.admins_jid = admins_jid | 48 self.admins = admins |
48 self.xmpp_connections = {} | 49 self.xmpp_connections = {} |
49 self.irc = irclib.IRC() | 50 self.irc = irclib.IRC() |
50 self.irc.bot = self | 51 self.irc.bot = self |
51 self.irc.add_global_handler('all_events', self._irc_event_handler) | 52 self.irc.add_global_handler('all_events', self._irc_event_handler) |
52 self.irc_thread = threading.Thread(target=self.irc.process_forever) | 53 self.irc_thread = threading.Thread(target=self.irc.process_forever) |
53 self.irc_thread.start() | 54 self.irc_thread.start() |
54 # Open connection with XMPP server | 55 # Open connection with XMPP server |
55 try: | 56 try: |
56 self.xmpp_c = self.get_xmpp_connection(self.nickname) | 57 self.xmpp_c = self.get_xmpp_connection(self.nickname) |
57 except: | 58 except: |
58 self.error('[Error] XMPP Connection failed') | 59 self.error(say_levels.error, 'XMPP Connection failed') |
59 raise | 60 raise |
60 self.xmpp_thread = threading.Thread(target=self._xmpp_loop) | 61 self.xmpp_thread = threading.Thread(target=self._xmpp_loop) |
61 self.xmpp_thread.start() | 62 self.xmpp_thread.start() |
62 | 63 |
63 | 64 |
64 def error(self, s, debug=False, send_to_admins=False): | 65 def error(self, importance, message, debug=False, send_to_admins=False): |
65 """Output an error message.""" | 66 """Output an error message.""" |
66 if send_to_admins == True: | 67 if send_to_admins == True: |
67 self._send_message_to_admins(s) | 68 self._send_message_to_admins(importance, message) |
68 if not debug or debug and self.debug: | 69 if importance == -1: |
69 self.error_fd.write(s.encode('utf-8')+"\n") | 70 return |
71 if not debug: | |
72 self.error_fd.write(self.format_message(importance, message).encode('utf-8')+'\n') | |
73 if debug and self.debug: | |
74 self.error_fd.write('='*importance+'> '+message.encode('utf-8')+'\n') | |
70 | 75 |
71 | 76 |
72 def _xmpp_loop(self): | 77 def _xmpp_loop(self): |
73 """[Internal] XMPP infinite loop.""" | 78 """[Internal] XMPP infinite loop.""" |
74 i = 1 | 79 i = 1 |
87 if hasattr(c, 'lock'): | 92 if hasattr(c, 'lock'): |
88 c.lock.acquire() | 93 c.lock.acquire() |
89 if i == j: | 94 if i == j: |
90 ping = xmpp.protocol.Iq(typ='get') | 95 ping = xmpp.protocol.Iq(typ='get') |
91 ping.addChild(name='ping', namespace='urn:xmpp:ping') | 96 ping.addChild(name='ping', namespace='urn:xmpp:ping') |
92 self.error('=> Debug: sending XMPP ping', debug=True) | 97 self.error(1, 'sending XMPP ping', debug=True) |
93 c.pings.append(c.send(ping)) | 98 c.pings.append(c.send(ping)) |
94 if hasattr(c, 'Process'): | 99 if hasattr(c, 'Process'): |
95 c.Process(0.01) | 100 c.Process(0.01) |
96 c.lock.release() | 101 c.lock.release() |
97 if i > 5000: | 102 if i > 5000: |
98 i = 0 | 103 i = 0 |
99 except RuntimeError: | 104 except RuntimeError: |
100 pass | 105 pass |
101 except (xml.parsers.expat.ExpatError, xmpp.protocol.XMLNotWellFormed): | 106 except (xml.parsers.expat.ExpatError, xmpp.protocol.XMLNotWellFormed): |
102 self.error('=> Debug: invalid stanza', debug=True) | 107 self.error(1, 'invalid stanza', debug=True) |
103 self.reopen_xmpp_connection(c) | 108 self.reopen_xmpp_connection(c) |
104 unlock = True | 109 unlock = True |
105 except xmpp.Conflict: | 110 except xmpp.Conflict: |
106 self.error('=> Debug: conflict', debug=True) | 111 self.error(1, 'conflict', debug=True) |
107 self.reopen_xmpp_connection(c) | 112 self.reopen_xmpp_connection(c) |
108 unlock = True | 113 unlock = True |
109 except: | 114 except: |
110 error = '[Error] Unknown exception on XMPP thread:\n' | 115 error = 'Unknown exception on XMPP thread:\n'+traceback.format_exc() |
111 error += traceback.format_exc() | 116 self.error(say_levels.error, error, send_to_admins=True) |
112 self.error(error, send_to_admins=True) | |
113 unlock = True | 117 unlock = True |
114 if unlock == True: | 118 if unlock == True: |
115 c.lock.release() | 119 c.lock.release() |
116 | 120 |
117 | 121 |
119 """[Internal] Manage XMPP presence.""" | 123 """[Internal] Manage XMPP presence.""" |
120 | 124 |
121 xmpp_c = dispatcher._owner | 125 xmpp_c = dispatcher._owner |
122 | 126 |
123 if xmpp_c.nickname != self.nickname: | 127 if xmpp_c.nickname != self.nickname: |
124 self.error('=> Debug: Skipping XMPP presence not received on bot connection.', debug=True) | 128 self.error(1, 'Skipping XMPP presence not received on bot connection.', debug=True) |
125 return | 129 return |
126 | 130 |
127 self.error('==> Debug: Received XMPP presence.', debug=True) | 131 self.error(2, 'Received XMPP presence.\n'+presence.__str__(fancy=1), debug=True) |
128 self.error(presence.__str__(fancy=1), debug=True) | |
129 | 132 |
130 from_ = xmpp.protocol.JID(presence.getFrom()) | 133 from_ = xmpp.protocol.JID(presence.getFrom()) |
131 bare_jid = unicode(from_.getNode()+'@'+from_.getDomain()) | 134 bare_jid = unicode(from_.getNode()+'@'+from_.getDomain()) |
132 for bridge in self.bridges: | 135 for bridge in self.bridges: |
133 if bare_jid == bridge.xmpp_room_jid: | 136 if bare_jid == bridge.xmpp_room_jid: |
151 if reason: | 154 if reason: |
152 r = reason.getData() | 155 r = reason.getData() |
153 if r == 'The conference component is shutting down': | 156 if r == 'The conference component is shutting down': |
154 # MUC server is going down, try to restart the bridges in 1 minute | 157 # MUC server is going down, try to restart the bridges in 1 minute |
155 bridges = self.findBridges([from_.getDomain()]) | 158 bridges = self.findBridges([from_.getDomain()]) |
156 error_message = '[Warning] The MUC server '+from_.getDomain()+' seems to be going down, the bot will try to recreate all bridges related to this server in 1 minute' | 159 m = 'The MUC server '+from_.getDomain()+' seems to be going down, the bot will try to recreate all bridges related to this server in 1 minute' |
157 self.restart_bridges_delayed(bridges, 60, error_message) | 160 error = (say_levels.warning, m) |
158 self.error(presence.__str__(fancy=1).encode('utf-8'), debug=True) | 161 self.restart_bridges_delayed(bridges, 60, error) |
159 return | 162 return |
160 elif r == '': | 163 elif r == '': |
161 r = 'None given' | 164 r = 'None given' |
162 else: | 165 else: |
163 r = 'None given' | 166 r = 'None given' |
164 | 167 |
165 # room has been destroyed, stop the bridge | 168 # room has been destroyed, stop the bridge |
166 self.error('[Error] The MUC room of the bridge '+str(bridge)+' has been destroyed with reason "'+r+'", stopping the bridge', send_to_admins=True) | 169 self.error(say_levels.error, 'The MUC room of the bridge '+str(bridge)+' has been destroyed with reason "'+r+'", stopping the bridge', send_to_admins=True) |
167 bridge.stop(message='The MUC room of the bridge has been destroyed with reason "'+r+'", stopping the bridge') | 170 bridge.stop(message='The MUC room of the bridge has been destroyed with reason "'+r+'", stopping the bridge') |
168 | 171 |
169 else: | 172 else: |
170 # presence comes from a participant of the muc | 173 # presence comes from a participant of the muc |
171 | 174 |
186 return | 189 return |
187 if p.protocol != 'xmpp': | 190 if p.protocol != 'xmpp': |
188 return | 191 return |
189 item = x.getTag('item') | 192 item = x.getTag('item') |
190 if not item: | 193 if not item: |
191 self.error('=> Debug: bad stanza, no item element', debug=True) | 194 self.error(1, 'bad stanza, no item element', debug=True) |
192 return | 195 return |
193 new_nick = item.getAttr('nick') | 196 new_nick = item.getAttr('nick') |
194 if not new_nick: | 197 if not new_nick: |
195 self.error('=> Debug: bad stanza, new nick is not given', debug=True) | 198 self.error(1, 'bad stanza, new nick is not given', debug=True) |
196 return | 199 return |
197 p.changeNickname(new_nick, 'irc') | 200 p.changeNickname(new_nick, 'irc') |
198 | 201 |
199 elif x and x.getTag('status', attrs={'code': '307'}): | 202 elif x and x.getTag('status', attrs={'code': '307'}): |
200 # participant was kicked | 203 # participant was kicked |
223 bridge.removeParticipant('xmpp', p.nickname, s1+s2) | 226 bridge.removeParticipant('xmpp', p.nickname, s1+s2) |
224 | 227 |
225 elif x and x.getTag('status', attrs={'code': '301'}): | 228 elif x and x.getTag('status', attrs={'code': '301'}): |
226 # participant was banned | 229 # participant was banned |
227 if p == None: | 230 if p == None: |
228 m = '[Error] bot got banned from XMPP' | 231 bridge.say(say_levels.error, 'bot got banned from XMPP', on_xmpp=False, send_to_admins=True) |
229 self.error(m) | |
230 bridge.say(m, on_xmpp=False) | |
231 self.removeBridge(bridge) | 232 self.removeBridge(bridge) |
232 return | 233 return |
233 if item: | 234 if item: |
234 reason = item.getTag('reason') | 235 reason = item.getTag('reason') |
235 actor = item.getTag('actor') | 236 actor = item.getTag('actor') |
263 # Remote server not found | 264 # Remote server not found |
264 # Stop bridges that depend on this server | 265 # Stop bridges that depend on this server |
265 bridges = self.findBridges([from_.getDomain()]) | 266 bridges = self.findBridges([from_.getDomain()]) |
266 error_message = '[Error] XMPP Remote server not found: '+from_.getDomain() | 267 error_message = '[Error] XMPP Remote server not found: '+from_.getDomain() |
267 self.restart_bridges_delayed(bridges, 60, error_message) | 268 self.restart_bridges_delayed(bridges, 60, error_message) |
268 self.error(presence.__str__(fancy=1).encode('utf-8'), debug=True) | |
269 else: | 269 else: |
270 raise Exception(presence.__str__(fancy=1).encode('utf-8')) | 270 raise Exception(presence.__str__(fancy=1).encode('utf-8')) |
271 | 271 |
272 elif resource != bridge.bot.nickname: | 272 elif resource != bridge.bot.nickname: |
273 real_jid = None | 273 real_jid = None |
276 | 276 |
277 p = bridge.addParticipant('xmpp', resource, real_jid) | 277 p = bridge.addParticipant('xmpp', resource, real_jid) |
278 | 278 |
279 # if we have the real jid check if the participant is a bot admin | 279 # if we have the real jid check if the participant is a bot admin |
280 if real_jid and isinstance(p, Participant): | 280 if real_jid and isinstance(p, Participant): |
281 for jid in self.admins_jid: | 281 for admin in self.admins: |
282 if xmpp.protocol.JID(jid).bareMatch(real_jid): | 282 if xmpp.protocol.JID(admin.jid).bareMatch(real_jid): |
283 p.bot_admin = True | 283 p.bot_admin = True |
284 break | 284 break |
285 | 285 |
286 return | 286 return |
287 | 287 |
294 xmpp_c = dispatcher._owner | 294 xmpp_c = dispatcher._owner |
295 | 295 |
296 # Ignore pongs | 296 # Ignore pongs |
297 if iq.getType() in ['result', 'error'] and iq.getID() in xmpp_c.pings: | 297 if iq.getType() in ['result', 'error'] and iq.getID() in xmpp_c.pings: |
298 xmpp_c.pings.remove(iq.getID()) | 298 xmpp_c.pings.remove(iq.getID()) |
299 self.error('=> Debug: received XMPP pong', debug=True) | 299 self.error(1, 'received XMPP pong', debug=True) |
300 return | 300 return |
301 | 301 |
302 self.error('==> Debug: Received XMPP iq.', debug=True) | 302 self.error(2, 'Received XMPP iq.\n'+iq.__str__(fancy=1), debug=True) |
303 self.error(iq.__str__(fancy=1), debug=True) | |
304 | 303 |
305 | 304 |
306 def _xmpp_message_handler(self, dispatcher, message): | 305 def _xmpp_message_handler(self, dispatcher, message): |
307 """[Internal] Manage XMPP messages.""" | 306 """[Internal] Manage XMPP messages.""" |
308 | 307 |
315 from_bare_jid = unicode(message.getFrom().getNode()+'@'+message.getFrom().getDomain()) | 314 from_bare_jid = unicode(message.getFrom().getNode()+'@'+message.getFrom().getDomain()) |
316 for bridge in self.bridges: | 315 for bridge in self.bridges: |
317 if from_bare_jid == bridge.xmpp_room_jid: | 316 if from_bare_jid == bridge.xmpp_room_jid: |
318 # message comes from a room participant | 317 # message comes from a room participant |
319 | 318 |
320 self.error('==> Debug: Received XMPP chat message.', debug=True) | 319 self.error(2, 'Received XMPP chat message.\n'+message.__str__(fancy=1), debug=True) |
321 self.error(message.__str__(fancy=1), debug=True) | |
322 | 320 |
323 try: | 321 try: |
324 from_ = bridge.getParticipant(message.getFrom().getResource()) | 322 from_ = bridge.getParticipant(message.getFrom().getResource()) |
325 to_ = bridge.getParticipant(xmpp_c.nickname) | 323 to_ = bridge.getParticipant(xmpp_c.nickname) |
326 | 324 |
329 except Bridge.NoSuchParticipantException: | 327 except Bridge.NoSuchParticipantException: |
330 if xmpp_c.nickname == self.nickname: | 328 if xmpp_c.nickname == self.nickname: |
331 r = self.respond(str(message.getBody()), participant=from_) | 329 r = self.respond(str(message.getBody()), participant=from_) |
332 if isinstance(r, basestring) and len(r) > 0: | 330 if isinstance(r, basestring) and len(r) > 0: |
333 s = xmpp.protocol.Message(to=message.getFrom(), body=r, typ='chat') | 331 s = xmpp.protocol.Message(to=message.getFrom(), body=r, typ='chat') |
334 self.error('==> Debug: Sending', debug=True) | 332 self.error(2, 'Sending\n'+s.__str__(fancy=1), debug=True) |
335 self.error(s.__str__(fancy=1), debug=True) | |
336 xmpp_c.send(s) | 333 xmpp_c.send(s) |
337 else: | 334 else: |
338 self.error('=> Debug: won\'t answer.', debug=True) | 335 self.error(1, 'won\'t answer.', debug=True) |
339 return | 336 return |
340 self.error('=> Debug: XMPP chat message not relayed', debug=True) | 337 self.error(1, 'XMPP chat message not relayed', debug=True) |
341 return | 338 return |
342 | 339 |
343 # message does not come from a room | 340 # message does not come from a room |
344 if xmpp_c.nickname == self.nickname: | 341 if xmpp_c.nickname == self.nickname: |
345 self.error('==> Debug: Received XMPP chat message.', debug=True) | 342 self.error(2, 'Received XMPP chat message.\n'+message.__str__(fancy=1), debug=True) |
346 self.error(message.__str__(fancy=1), debug=True) | |
347 | 343 |
348 # Find out if the message comes from a bot admin | 344 # Find out if the message comes from a bot admin |
349 bot_admin = False | 345 bot_admin = False |
350 for jid in self.admins_jid: | 346 for admin in self.admins: |
351 if xmpp.protocol.JID(jid).bareMatch(message.getFrom()): | 347 if xmpp.protocol.JID(admin.jid).bareMatch(message.getFrom()): |
352 bot_admin = True | 348 bot_admin = True |
353 break | 349 break |
354 | 350 |
355 # Respond | 351 # Respond |
356 r = self.respond(str(message.getBody()), bot_admin=bot_admin) | 352 r = self.respond(str(message.getBody()), bot_admin=bot_admin) |
357 if isinstance(r, basestring) and len(r) > 0: | 353 if isinstance(r, basestring) and len(r) > 0: |
358 s = xmpp.protocol.Message(to=message.getFrom(), body=r, typ='chat') | 354 s = xmpp.protocol.Message(to=message.getFrom(), body=r, typ='chat') |
359 self.error('==> Debug: Sending', debug=True) | 355 self.error(2, 'Sending\n'+s.__str__(fancy=1), debug=True) |
360 self.error(s.__str__(fancy=1), debug=True) | |
361 xmpp_c.send(s) | 356 xmpp_c.send(s) |
362 | 357 |
363 else: | 358 else: |
364 self.error('=> Debug: Ignoring XMPP chat message not received on bot connection.', debug=True) | 359 self.error(1, 'Ignoring XMPP chat message not received on bot connection.', debug=True) |
365 | 360 |
366 elif message.getType() == 'groupchat': | 361 elif message.getType() == 'groupchat': |
367 # message comes from a room | 362 # message comes from a room |
368 | 363 |
369 for child in message.getChildren(): | 364 for child in message.getChildren(): |
370 if child.getName() == 'delay': | 365 if child.getName() == 'delay': |
371 # MUC delayed message | 366 # MUC delayed message |
372 return | 367 return |
373 | 368 |
374 if xmpp_c.nickname != self.nickname: | 369 if xmpp_c.nickname != self.nickname: |
375 self.error('=> Debug: Ignoring XMPP MUC message not received on bot connection.', debug=True) | 370 self.error(1, 'Ignoring XMPP MUC message not received on bot connection.', debug=True) |
376 return | 371 return |
377 | 372 |
378 | 373 |
379 from_ = xmpp.protocol.JID(message.getFrom()) | 374 from_ = xmpp.protocol.JID(message.getFrom()) |
380 | 375 |
381 if unicode(from_.getResource()) == self.nickname: | 376 if unicode(from_.getResource()) == self.nickname: |
382 self.error('=> Debug: Ignoring XMPP MUC message sent by self.', debug=True) | 377 self.error(1, 'Ignoring XMPP MUC message sent by self.', debug=True) |
383 return | 378 return |
384 | 379 |
385 room_jid = unicode(from_.getNode()+'@'+from_.getDomain()) | 380 room_jid = unicode(from_.getNode()+'@'+from_.getDomain()) |
386 for bridge in self.bridges: | 381 for bridge in self.bridges: |
387 if room_jid == bridge.xmpp_room_jid: | 382 if room_jid == bridge.xmpp_room_jid: |
388 resource = unicode(from_.getResource()) | 383 resource = unicode(from_.getResource()) |
389 if resource == '': | 384 if resource == '': |
390 # message comes from the room itself | 385 # message comes from the room itself |
391 self.error('=> Debug: Ignoring XMPP groupchat message sent by the room.', debug=True) | 386 self.error(1, 'Ignoring XMPP groupchat message sent by the room.', debug=True) |
392 return | 387 return |
393 else: | 388 else: |
394 # message comes from a participant of the room | 389 # message comes from a participant of the room |
395 self.error('==> Debug: Received XMPP groupchat message.', debug=True) | 390 self.error(2, 'Received XMPP groupchat message.\n'+message.__str__(fancy=1), debug=True) |
396 self.error(message.__str__(fancy=1), debug=True) | |
397 | 391 |
398 try: | 392 try: |
399 participant = bridge.getParticipant(resource) | 393 participant = bridge.getParticipant(resource) |
400 except Bridge.NoSuchParticipantException: | 394 except Bridge.NoSuchParticipantException: |
401 if resource != self.nickname: | 395 if resource != self.nickname: |
402 self.error('=> Debug: NoSuchParticipantException "'+resource+'" on "'+str(bridge)+'", WTF ?', debug=True) | 396 self.error(1, 'NoSuchParticipantException "'+resource+'" on "'+str(bridge)+'", WTF ?', debug=True) |
403 return | 397 return |
404 | 398 |
405 participant.sayOnIRC(message.getBody()) | 399 participant.sayOnIRC(message.getBody()) |
406 return | 400 return |
407 | 401 |
420 # let's restart the bot | 414 # let's restart the bot |
421 self.restart() | 415 self.restart() |
422 elif err == 'forbidden': | 416 elif err == 'forbidden': |
423 # we don't have the permission to speak | 417 # we don't have the permission to speak |
424 # let's remove the bridge and tell admins | 418 # let's remove the bridge and tell admins |
425 self.error('[Error] Not allowed to speak on the XMPP MUC of bridge '+str(b)+', stopping it', send_to_admins=True) | 419 self.error(say_levels.error, 'Not allowed to speak on the XMPP MUC of bridge '+str(b)+', stopping it', send_to_admins=True) |
426 b.stop(message='Not allowed to speak on the XMPP MUC, stopping bridge.') | 420 b.stop(message='Not allowed to speak on the XMPP MUC, stopping bridge.') |
427 else: | 421 else: |
428 self.error('==> Debug: recevied unknown error message', debug=True) | 422 self.error(2, 'recevied unknown error message\n'+message.__str__(fancy=1), debug=True) |
429 self.error(message.__str__(fancy=1), debug=True) | |
430 return | 423 return |
431 | 424 |
432 self.error('==> Debug: recevied unknown error message', debug=True) | 425 self.error(2, 'recevied unknown error message\n'+message.__str__(fancy=1), debug=True) |
433 self.error(message.__str__(fancy=1), debug=True) | |
434 | 426 |
435 else: | 427 else: |
436 self.error('==> Debug: Received XMPP message of unknown type "'+str(message.getType())+'".', debug=True) | 428 self.error(2, 'Received XMPP message of unknown type "'+str(message.getType())+'".\n'+message.__str__(fancy=1), debug=True) |
437 self.error(message.__str__(fancy=1), debug=True) | |
438 | 429 |
439 | 430 |
440 def _irc_event_handler(self, connection, event): | 431 def _irc_event_handler(self, connection, event): |
441 """[Internal] Manage IRC events""" | 432 """[Internal] Manage IRC events""" |
442 | 433 |
448 | 439 |
449 # Events we always want to ignore | 440 # Events we always want to ignore |
450 if 'all' in event.eventtype() or 'motd' in event.eventtype() or event.eventtype() in ['nicknameinuse', 'nickcollision', 'erroneusnickname']: | 441 if 'all' in event.eventtype() or 'motd' in event.eventtype() or event.eventtype() in ['nicknameinuse', 'nickcollision', 'erroneusnickname']: |
451 return | 442 return |
452 if event.eventtype() in ['pong', 'privnotice', 'ctcp', 'nochanmodes', 'notexttosend', 'currenttopic', 'topicinfo', '328']: | 443 if event.eventtype() in ['pong', 'privnotice', 'ctcp', 'nochanmodes', 'notexttosend', 'currenttopic', 'topicinfo', '328']: |
453 self.error('=> Debug: ignoring IRC '+event.eventtype(), debug=True) | 444 self.error(1, 'ignoring IRC '+event.eventtype(), debug=True) |
454 return | 445 return |
455 | 446 |
456 | 447 |
457 nickname = None | 448 nickname = None |
458 if event.source() != None: | 449 if event.source() != None: |
465 if connection.really_connected == False: | 456 if connection.really_connected == False: |
466 if event.target() == connection.nickname: | 457 if event.target() == connection.nickname: |
467 connection.really_connected = True | 458 connection.really_connected = True |
468 connection._call_nick_callbacks(None) | 459 connection._call_nick_callbacks(None) |
469 elif len(connection.nick_callbacks) > 0: | 460 elif len(connection.nick_callbacks) > 0: |
470 self.error('===> Debug: event target ('+event.target()+') and connection nickname ('+connection.nickname+') don\'t match') | 461 self.error(3, 'event target ('+event.target()+') and connection nickname ('+connection.nickname+') don\'t match') |
471 connection._call_nick_callbacks('nicknametoolong', arguments=[len(event.target())]) | 462 connection._call_nick_callbacks('nicknametoolong', arguments=[len(event.target())]) |
472 self.error('=> Debug: ignoring '+event.eventtype(), debug=True) | 463 self.error(1, 'ignoring '+event.eventtype(), debug=True) |
473 return | 464 return |
474 | 465 |
475 | 466 |
476 # A string representation of the event | 467 # A string representation of the event |
477 event_str = 'connection='+connection.__str__()+'\neventtype='+event.eventtype()+'\nsource='+repr(event.source())+'\ntarget='+repr(event.target())+'\narguments='+repr(event.arguments()) | 468 event_str = 'connection='+connection.__str__()+'\neventtype='+event.eventtype()+'\nsource='+repr(event.source())+'\ntarget='+repr(event.target())+'\narguments='+repr(event.arguments()) |
478 debug_str = '==> Debug: Received IRC event.\n'+event_str | 469 debug_str = 'Received IRC event.\n'+event_str |
479 printed_event = False | 470 printed_event = False |
480 | 471 |
481 | 472 |
482 if event.eventtype() in ['pubmsg', 'action', 'privmsg', 'quit', 'part', 'nick', 'kick']: | 473 if event.eventtype() in ['pubmsg', 'action', 'privmsg', 'quit', 'part', 'nick', 'kick']: |
483 if nickname == None: | 474 if nickname == None: |
488 if event.eventtype() in ['quit', 'part'] and nickname == self.nickname: | 479 if event.eventtype() in ['quit', 'part'] and nickname == self.nickname: |
489 return | 480 return |
490 | 481 |
491 if event.eventtype() in ['quit', 'part', 'nick', 'kick']: | 482 if event.eventtype() in ['quit', 'part', 'nick', 'kick']: |
492 if connection.get_nickname() != self.nickname: | 483 if connection.get_nickname() != self.nickname: |
493 self.error('=> Debug: ignoring IRC '+event.eventtype()+' not received on bot connection', debug=True) | 484 self.error(1, 'ignoring IRC '+event.eventtype()+' not received on bot connection', debug=True) |
494 return | 485 return |
495 else: | 486 else: |
496 self.error(debug_str, debug=True) | 487 self.error(2, debug_str, debug=True) |
497 printed_event = True | 488 printed_event = True |
498 | 489 |
499 if event.eventtype() == 'kick' and len(event.arguments()) < 1: | 490 if event.eventtype() == 'kick' and len(event.arguments()) < 1: |
500 self.error('=> Debug: length of arguments should be greater than 0 for a '+event.eventtype()+' event') | 491 self.error(1, 'length of arguments should be greater than 0 for a '+event.eventtype()+' event') |
501 return | 492 return |
502 | 493 |
503 if event.eventtype() in ['pubmsg', 'action']: | 494 if event.eventtype() in ['pubmsg', 'action']: |
504 if connection.get_nickname() != self.nickname: | 495 if connection.get_nickname() != self.nickname: |
505 self.error('=> Debug: ignoring IRC '+event.eventtype()+' not received on bot connection', debug=True) | 496 self.error(1, 'ignoring IRC '+event.eventtype()+' not received on bot connection', debug=True) |
506 return | 497 return |
507 if nickname == self.nickname: | 498 if nickname == self.nickname: |
508 self.error('=> Debug: ignoring IRC '+event.eventtype()+' sent by self', debug=True) | 499 self.error(1, 'ignoring IRC '+event.eventtype()+' sent by self', debug=True) |
509 return | 500 return |
510 | 501 |
511 # TODO: lock self.bridges for thread safety | 502 # TODO: lock self.bridges for thread safety |
512 for bridge in self.bridges: | 503 for bridge in self.bridges: |
513 if connection.server != bridge.irc_server: | 504 if connection.server != bridge.irc_server: |
525 if event.target() == None: | 516 if event.target() == None: |
526 return | 517 return |
527 | 518 |
528 try: | 519 try: |
529 to_ = bridge.getParticipant(event.target().split('!')[0]) | 520 to_ = bridge.getParticipant(event.target().split('!')[0]) |
530 self.error(debug_str, debug=True) | 521 self.error(2, debug_str, debug=True) |
531 from_.sayOnXMPPTo(to_.nickname, event.arguments()[0]) | 522 from_.sayOnXMPPTo(to_.nickname, event.arguments()[0]) |
532 return | 523 return |
533 | 524 |
534 except Bridge.NoSuchParticipantException: | 525 except Bridge.NoSuchParticipantException: |
535 if event.target().split('!')[0] == self.nickname: | 526 if event.target().split('!')[0] == self.nickname: |
536 # Message is for the bot | 527 # Message is for the bot |
537 self.error(debug_str, debug=True) | 528 self.error(2, debug_str, debug=True) |
538 connection.privmsg(from_.nickname, self.respond(event.arguments()[0])) | 529 connection.privmsg(from_.nickname, self.respond(event.arguments()[0])) |
539 return | 530 return |
540 else: | 531 else: |
541 continue | 532 continue |
542 | 533 |
553 bridge.removeParticipant('irc', kicked.nickname, 'Kicked by '+nickname+' with reason: '+event.arguments()[1]) | 544 bridge.removeParticipant('irc', kicked.nickname, 'Kicked by '+nickname+' with reason: '+event.arguments()[1]) |
554 else: | 545 else: |
555 bridge.removeParticipant('irc', kicked.nickname, 'Kicked by '+nickname+' (no reason was given)') | 546 bridge.removeParticipant('irc', kicked.nickname, 'Kicked by '+nickname+' (no reason was given)') |
556 return | 547 return |
557 except Bridge.NoSuchParticipantException: | 548 except Bridge.NoSuchParticipantException: |
558 self.error('=> Debug: a participant that was not here has been kicked ? WTF ?') | 549 self.error(1, 'a participant that was not here has been kicked ? WTF ?') |
559 return | 550 return |
560 else: | 551 else: |
561 continue | 552 continue |
562 | 553 |
563 | 554 |
586 | 577 |
587 | 578 |
588 # Chan message | 579 # Chan message |
589 if event.eventtype() in ['pubmsg', 'action']: | 580 if event.eventtype() in ['pubmsg', 'action']: |
590 if bridge.irc_room == event.target().lower() and bridge.irc_server == connection.server: | 581 if bridge.irc_room == event.target().lower() and bridge.irc_server == connection.server: |
591 self.error(debug_str, debug=True) | 582 self.error(2, debug_str, debug=True) |
592 message = event.arguments()[0] | 583 message = event.arguments()[0] |
593 if event.eventtype() == 'action': | 584 if event.eventtype() == 'action': |
594 message = '/me '+message | 585 message = '/me '+message |
595 from_.sayOnXMPP(message) | 586 from_.sayOnXMPP(message) |
596 return | 587 return |
602 | 593 |
603 | 594 |
604 # Handle bannedfromchan | 595 # Handle bannedfromchan |
605 if event.eventtype() == 'bannedfromchan': | 596 if event.eventtype() == 'bannedfromchan': |
606 if len(event.arguments()) < 1: | 597 if len(event.arguments()) < 1: |
607 self.error('=> Debug: length of arguments should be greater than 0 for a '+event.eventtype()+' event') | 598 self.error(1, 'length of arguments should be greater than 0 for a '+event.eventtype()+' event') |
608 return | 599 return |
609 | 600 |
610 for bridge in self.bridges: | 601 for bridge in self.bridges: |
611 if connection.server != bridge.irc_server or event.arguments()[0].lower() != bridge.irc_room: | 602 if connection.server != bridge.irc_server or event.arguments()[0].lower() != bridge.irc_room: |
612 continue | 603 continue |
613 | 604 |
614 if event.target() == self.nickname: | 605 if event.target() == self.nickname: |
615 self.error('[Error] the nickname "'+event.target()+'" is banned from the IRC chan of bridge "'+str(bridge)+'"') | 606 self.error(say_levels.error, 'the nickname "'+event.target()+'" is banned from the IRC chan of bridge "'+str(bridge)+'"') |
616 raise Exception('[Error] the nickname "'+event.target()+'" is banned from the IRC chan of bridge "'+str(bridge)+'"') | 607 raise Exception('[Error] the nickname "'+event.target()+'" is banned from the IRC chan of bridge "'+str(bridge)+'"') |
617 else: | 608 else: |
618 try: | 609 try: |
619 banned = bridge.getParticipant(event.target()) | 610 banned = bridge.getParticipant(event.target()) |
620 if banned.irc_connection != 'bannedfromchan': | 611 if banned.irc_connection != 'bannedfromchan': |
621 banned.irc_connection = 'bannedfromchan' | 612 banned.irc_connection = 'bannedfromchan' |
622 self.error(debug_str, debug=True) | 613 self.error(2, debug_str, debug=True) |
623 bridge.say('[Warning] the nickname "'+event.target()+'" is banned from the IRC chan', log=True) | 614 bridge.say(say_levels.warning, 'the nickname "'+event.target()+'" is banned from the IRC chan', log=True) |
624 else: | 615 else: |
625 self.error('=> Debug: ignoring '+event.eventtype(), debug=True) | 616 self.error(1, 'ignoring '+event.eventtype(), debug=True) |
626 except Bridge.NoSuchParticipantException: | 617 except Bridge.NoSuchParticipantException: |
627 self.error('=> Debug: no such participant. WTF ?') | 618 self.error(1, 'no such participant. WTF ?') |
628 return | 619 return |
629 | 620 |
630 return | 621 return |
631 | 622 |
632 | 623 |
633 # Joining events | 624 # Joining events |
634 if event.eventtype() in ['namreply', 'join']: | 625 if event.eventtype() in ['namreply', 'join']: |
635 if connection.get_nickname() != self.nickname: | 626 if connection.get_nickname() != self.nickname: |
636 self.error('=> Debug: ignoring IRC '+event.eventtype()+' not received on bridge connection', debug=True) | 627 self.error(1, 'ignoring IRC '+event.eventtype()+' not received on bridge connection', debug=True) |
637 return | 628 return |
638 | 629 |
639 if event.eventtype() == 'namreply': | 630 if event.eventtype() == 'namreply': |
640 for bridge in self.getBridges(irc_room=event.arguments()[1].lower(), irc_server=connection.server): | 631 for bridge in self.getBridges(irc_room=event.arguments()[1].lower(), irc_server=connection.server): |
641 for nickname in re.split('(?:^[&@\+%]?|(?: [&@\+%]?)*)', event.arguments()[2].strip()): | 632 for nickname in re.split('(?:^[&@\+%]?|(?: [&@\+%]?)*)', event.arguments()[2].strip()): |
644 bridge.addParticipant('irc', nickname) | 635 bridge.addParticipant('irc', nickname) |
645 return | 636 return |
646 elif event.eventtype() == 'join': | 637 elif event.eventtype() == 'join': |
647 bridges = self.getBridges(irc_room=event.target().lower(), irc_server=connection.server) | 638 bridges = self.getBridges(irc_room=event.target().lower(), irc_server=connection.server) |
648 if len(bridges) == 0: | 639 if len(bridges) == 0: |
649 self.error(debug_str, debug=True) | 640 self.error(2, debug_str, debug=True) |
650 self.error('===> Debug: no bridge found for "'+event.target().lower()+' at '+connection.server+'"', debug=True) | 641 self.error(3, 'no bridge found for "'+event.target().lower()+' at '+connection.server+'"', debug=True) |
651 return | 642 return |
652 for bridge in bridges: | 643 for bridge in bridges: |
653 bridge.addParticipant('irc', nickname, irc_id=event.source()) | 644 bridge.addParticipant('irc', nickname, irc_id=event.source()) |
654 return | 645 return |
655 | 646 |
656 | 647 |
657 if event.eventtype() in ['disconnect', 'kill', 'error']: | 648 if event.eventtype() in ['disconnect', 'kill', 'error']: |
658 if len(event.arguments()) > 0 and event.arguments()[0] == 'Connection reset by peer': | 649 if len(event.arguments()) > 0 and event.arguments()[0] == 'Connection reset by peer': |
659 self.error(debug_str, debug=True) | 650 self.error(2, debug_str, debug=True) |
660 else: | 651 else: |
661 self.error(debug_str, send_to_admins=True) | 652 self.error(2, debug_str, send_to_admins=True) |
662 return | 653 return |
663 | 654 |
664 | 655 |
665 if event.eventtype() in ['cannotsendtochan', 'notonchannel']: | 656 if event.eventtype() in ['cannotsendtochan', 'notonchannel']: |
666 self.error(debug_str, debug=True) | 657 self.error(2, debug_str, debug=True) |
667 bridges = self.getBridges(irc_room=event.arguments()[0], irc_server=connection.server) | 658 bridges = self.getBridges(irc_room=event.arguments()[0], irc_server=connection.server) |
668 if len(bridges) > 1: | 659 if len(bridges) > 1: |
669 raise Exception, 'more than one bridge for one irc chan, WTF ?' | 660 raise Exception, 'more than one bridge for one irc chan, WTF ?' |
670 bridge = bridges[0] | 661 bridge = bridges[0] |
671 if connection.get_nickname() == self.nickname: | 662 if connection.get_nickname() == self.nickname: |
677 return | 668 return |
678 | 669 |
679 | 670 |
680 # Unhandled events | 671 # Unhandled events |
681 if not printed_event: | 672 if not printed_event: |
682 self.error('[Debug] The following IRC event was not handled:\n'+event_str+'\n', send_to_admins=True) | 673 self.error(say_levels.debug, 'The following IRC event was not handled:\n'+event_str+'\n', send_to_admins=True) |
683 else: | 674 else: |
684 self.error('=> Debug: event not handled', debug=True) | 675 self.error(1, 'event not handled', debug=True) |
685 self._send_message_to_admins('[Debug] The following IRC event was not handled:\n'+event_str) | 676 self._send_message_to_admins('[Debug] The following IRC event was not handled:\n'+event_str) |
686 | 677 |
687 | 678 |
688 def _send_message_to_admins(self, message): | 679 def _send_message_to_admins(self, importance, message): |
689 """[Internal] Send XMPP Message to bot admin(s)""" | 680 """[Internal] Send XMPP Message to bot admin(s)""" |
690 for admin_jid in self.admins_jid: | 681 for admin in self.admins: |
682 if importance != -1: | |
683 if admin.say_level == say_levels.nothing or importance < admin.say_level: | |
684 continue | |
685 message = self.format_message(importance, message) | |
686 | |
691 try: | 687 try: |
692 self.xmpp_c.send(xmpp.protocol.Message(to=admin_jid, body=message, typ='chat')) | 688 self.xmpp_c.send(xmpp.protocol.Message(to=admin.jid, body=message, typ='chat')) |
693 except: | 689 except: |
694 pass | 690 pass |
695 | 691 |
696 | 692 |
697 def new_bridge(self, xmpp_room, irc_room, irc_server, mode, say_level, irc_port=6667, irc_connection_interval=1, irc_charsets=None): | 693 def new_bridge(self, xmpp_room, irc_room, irc_server, mode, say_level, irc_port=6667, irc_connection_interval=1, irc_charsets=None): |
708 for s in str_array: | 704 for s in str_array: |
709 if not s in str(bridge): | 705 if not s in str(bridge): |
710 bridges.remove(bridge) | 706 bridges.remove(bridge) |
711 break | 707 break |
712 return bridges | 708 return bridges |
709 | |
710 | |
711 def format_message(self, importance, message): | |
712 if importance < 0 or importance >= len(say_levels.levels): | |
713 raise Exception('[Internal Error] unknown message importance') | |
714 return'['+str(say_levels.get(importance))+'] '+message | |
713 | 715 |
714 | 716 |
715 def getBridges(self, irc_room=None, irc_server=None, xmpp_room_jid=None): | 717 def getBridges(self, irc_room=None, irc_server=None, xmpp_room_jid=None): |
716 # TODO: lock self.bridges for thread safety | 718 # TODO: lock self.bridges for thread safety |
717 bridges = [b for b in self.bridges] | 719 bridges = [b for b in self.bridges] |
730 | 732 |
731 def get_xmpp_connection(self, nickname): | 733 def get_xmpp_connection(self, nickname): |
732 if self.xmpp_connections.has_key(nickname): | 734 if self.xmpp_connections.has_key(nickname): |
733 c = self.xmpp_connections[nickname] | 735 c = self.xmpp_connections[nickname] |
734 c.used_by += 1 | 736 c.used_by += 1 |
735 self.error('===> Debug: using existing XMPP connection for "'+nickname+'", now used by '+str(c.used_by)+' bridges', debug=True) | 737 self.error(3, 'using existing XMPP connection for "'+nickname+'", now used by '+str(c.used_by)+' bridges', debug=True) |
736 return c | 738 return c |
737 self.error('===> Debug: opening new XMPP connection for "'+nickname+'"', debug=True) | 739 self.error(3, 'opening new XMPP connection for "'+nickname+'"', debug=True) |
738 c = xmpp.client.Client(self.bare_jid.getDomain(), debug=[]) | 740 c = xmpp.client.Client(self.bare_jid.getDomain(), debug=[]) |
739 c.lock = threading.RLock() | 741 c.lock = threading.RLock() |
740 c.lock.acquire() | 742 c.lock.acquire() |
741 self.xmpp_connections[nickname] = c | 743 self.xmpp_connections[nickname] = c |
742 c.used_by = 1 | 744 c.used_by = 1 |
768 for b in self.bridges: | 770 for b in self.bridges: |
769 for p in b.participants: | 771 for p in b.participants: |
770 if p.xmpp_c == c: | 772 if p.xmpp_c == c: |
771 participants.append(p) | 773 participants.append(p) |
772 p.xmpp_c = None | 774 p.xmpp_c = None |
773 self.error('===> Debug: reopening XMPP connection for "'+nickname+'"', debug=True) | 775 self.error(3, 'reopening XMPP connection for "'+nickname+'"', debug=True) |
774 if self.xmpp_connections.has_key(nickname): | 776 if self.xmpp_connections.has_key(nickname): |
775 self.xmpp_connections.pop(nickname) | 777 self.xmpp_connections.pop(nickname) |
776 c.send(xmpp.protocol.Presence(typ='unavailable')) | 778 c.send(xmpp.protocol.Presence(typ='unavailable')) |
777 del c | 779 del c |
778 c = self.get_xmpp_connection(nickname) | 780 c = self.get_xmpp_connection(nickname) |
792 return | 794 return |
793 c = self.xmpp_connections[nickname] | 795 c = self.xmpp_connections[nickname] |
794 c.lock.acquire() | 796 c.lock.acquire() |
795 c.used_by -= 1 | 797 c.used_by -= 1 |
796 if c.used_by < 1 or force: | 798 if c.used_by < 1 or force: |
797 self.error('===> Debug: closing XMPP connection for "'+nickname+'"', debug=True) | 799 self.error(3, 'closing XMPP connection for "'+nickname+'"', debug=True) |
798 self.xmpp_connections.pop(nickname) | 800 self.xmpp_connections.pop(nickname) |
799 c.send(xmpp.protocol.Presence(typ='unavailable')) | 801 c.send(xmpp.protocol.Presence(typ='unavailable')) |
800 c.lock.release() | 802 c.lock.release() |
801 del c | 803 del c |
802 else: | 804 else: |
803 c.lock.release() | 805 c.lock.release() |
804 self.error('===> Debug: XMPP connection for "'+nickname+'" is now used by '+str(c.used_by)+' bridges', debug=True) | 806 self.error(3, 'XMPP connection for "'+nickname+'" is now used by '+str(c.used_by)+' bridges', debug=True) |
805 | 807 |
806 | 808 |
807 def removeBridge(self, bridge, message='Removing bridge'): | 809 def removeBridge(self, bridge, message='Removing bridge'): |
808 self.bridges.remove(bridge) | 810 self.bridges.remove(bridge) |
809 bridge.stop(message) | 811 bridge.stop(message) |
830 | 832 |
831 # Restart the bridges | 833 # Restart the bridges |
832 for b in self.bridges: | 834 for b in self.bridges: |
833 b.init2() | 835 b.init2() |
834 | 836 |
835 self.error('Bot restarted.', send_to_admins=True) | 837 self.error(-1, 'Bot restarted.', send_to_admins=True) |
836 | 838 |
837 | 839 |
838 def restart_bridges_delayed(self, bridges, delay, error_message, protocol='xmpp'): | 840 def restart_bridges_delayed(self, bridges, delay, error, protocol='xmpp'): |
839 if len(bridges) > 0: | 841 if len(bridges) > 0: |
840 error_message += '\nThese bridges will be stopped:' | 842 error[1] += '\nThese bridges will be stopped:' |
841 for b in bridges: | 843 for b in bridges: |
842 error_message += '\n'+str(b) | 844 error[1] += '\n'+str(b) |
843 | 845 |
844 if protocol == 'xmpp': | 846 if protocol == 'xmpp': |
845 leave_message = 'Could not connect to the MUC server ('+b.xmpp_room_jid+')' | 847 leave_message = 'Could not connect to the MUC server ('+b.xmpp_room_jid+')' |
846 else: | 848 else: |
847 leave_message = 'Could not connect to the IRC server ('+b.irc_connection._server_str()+')' | 849 leave_message = 'Could not connect to the IRC server ('+b.irc_connection._server_str()+')' |
851 b.reconnecting = True | 853 b.reconnecting = True |
852 self.irc.execute_delayed(delay, b.init2) | 854 self.irc.execute_delayed(delay, b.init2) |
853 | 855 |
854 b.stop(message=leave_message) | 856 b.stop(message=leave_message) |
855 | 857 |
856 self.error(error_message, send_to_admins=True) | 858 self.error(error[0], error[1], send_to_admins=True) |
857 | 859 |
858 | 860 |
859 def stop(self, message='Stopping bot'): | 861 def stop(self, message='Stopping bot'): |
860 for bridge in self.bridges: | 862 for bridge in self.bridges: |
861 bridge.stop(message=message) | 863 bridge.stop(message=message) |