comparison bot.py @ 22:e2bd4de698e5

Solved an XMPP resource conflict that would have happened when someone on IRC changed its nickname and later its old nickname would be used again. In other words, the bot no longer uses nicknames as XMPP resources. Signed-off-by: Charly COSTE <changaco@changaco.net>
author Charly COSTE <changaco@changaco.net>
date Thu, 20 Aug 2009 17:49:40 +0200
parents 801160b4136f
children abdb7a2b6c6d
comparison
equal deleted inserted replaced
21:801160b4136f 22:e2bd4de698e5
37 def __init__(self, jid, password, nickname, error_fd=sys.stderr, debug=False): 37 def __init__(self, jid, password, nickname, error_fd=sys.stderr, debug=False):
38 Thread.__init__(self) 38 Thread.__init__(self)
39 self.commands = ['!xmpp_participants', '!irc_participants'] 39 self.commands = ['!xmpp_participants', '!irc_participants']
40 self.bare_jid = xmpp.protocol.JID(jid=jid) 40 self.bare_jid = xmpp.protocol.JID(jid=jid)
41 self.bare_jid.setResource('') 41 self.bare_jid.setResource('')
42 self.jid = xmpp.protocol.JID(jid=jid)
43 self.nickname = nickname 42 self.nickname = nickname
44 self.jid.setResource(self.nickname)
45 self.password = password 43 self.password = password
46 self.error_fd = error_fd 44 self.error_fd = error_fd
47 self.debug = debug 45 self.debug = debug
48 self.bridges = [] 46 self.bridges = []
49 self.xmpp_connections = {} 47 self.xmpp_connections = {}
52 self.irc.add_global_handler('all_events', self._irc_event_handler) 50 self.irc.add_global_handler('all_events', self._irc_event_handler)
53 self.irc_thread = Thread(target=self.irc.process_forever) 51 self.irc_thread = Thread(target=self.irc.process_forever)
54 self.irc_thread.start() 52 self.irc_thread.start()
55 # Open connection with XMPP server 53 # Open connection with XMPP server
56 try: 54 try:
57 self.xmpp_c = self.get_xmpp_connection(self.jid.getResource()) 55 self.xmpp_c = self.get_xmpp_connection(self.nickname)
58 except: 56 except:
59 self.error('[Error] XMPP Connection failed') 57 self.error('[Error] XMPP Connection failed')
60 raise 58 raise
61 self.xmpp_thread = Thread(target=self._xmpp_loop) 59 self.xmpp_thread = Thread(target=self._xmpp_loop)
62 self.xmpp_thread.start() 60 self.xmpp_thread.start()
87 except xml.parsers.expat.ExpatError: 85 except xml.parsers.expat.ExpatError:
88 self.error('=> Debug: received invalid stanza', debug=True) 86 self.error('=> Debug: received invalid stanza', debug=True)
89 continue 87 continue
90 88
91 89
92 def _xmpp_presence_handler(self, xmpp_c, presence): 90 def _xmpp_presence_handler(self, dispatcher, presence):
93 """[Internal] Manage XMPP presence.""" 91 """[Internal] Manage XMPP presence."""
94 92
95 if presence.getTo() != self.jid: 93 xmpp_c = dispatcher._owner
94
95 if xmpp_c.nickname != self.nickname:
96 self.error('=> Debug: Skipping XMPP presence not received on bot connection.', debug=True) 96 self.error('=> Debug: Skipping XMPP presence not received on bot connection.', debug=True)
97 return 97 return
98 98
99 self.error('==> Debug: Received XMPP presence.', debug=True) 99 self.error('==> Debug: Received XMPP presence.', debug=True)
100 self.error(presence.__str__(fancy=1), debug=True) 100 self.error(presence.__str__(fancy=1), debug=True)
141 bridge.removeParticipant('xmpp', resource, presence.getStatus()) 141 bridge.removeParticipant('xmpp', resource, presence.getStatus())
142 142
143 return 143 return
144 144
145 145
146 def _xmpp_iq_handler(self, xmpp_c, iq): 146 def _xmpp_iq_handler(self, dispatcher, iq):
147 """[Internal] Manage XMPP IQs.""" 147 """[Internal] Manage XMPP IQs."""
148 self.error('=> Debug: Received XMPP iq.', debug=True) 148 self.error('=> Debug: Received XMPP iq.', debug=True)
149 self.error(iq.__str__(fancy=1), debug=True) 149 self.error(iq.__str__(fancy=1), debug=True)
150 150
151 151
152 def _xmpp_message_handler(self, xmpp_c, message): 152 def _xmpp_message_handler(self, dispatcher, message):
153 """[Internal] Manage XMPP messages.""" 153 """[Internal] Manage XMPP messages."""
154
155 xmpp_c = dispatcher._owner
156
154 if message.getType() == 'chat': 157 if message.getType() == 'chat':
155 self.error('==> Debug: Received XMPP chat message.', debug=True) 158 self.error('==> Debug: Received XMPP chat message.', debug=True)
156 self.error(message.__str__(fancy=1), debug=True) 159 self.error(message.__str__(fancy=1), debug=True)
157 from_bare_jid = unicode(message.getFrom().getNode()+'@'+message.getFrom().getDomain()) 160 from_bare_jid = unicode(message.getFrom().getNode()+'@'+message.getFrom().getDomain())
158 for bridge in self.bridges: 161 for bridge in self.bridges:
164 to_ = bridge.getParticipant(message.getTo().getResource()) 167 to_ = bridge.getParticipant(message.getTo().getResource())
165 168
166 if from_.protocol == 'xmpp': 169 if from_.protocol == 'xmpp':
167 from_.sayOnIRCTo(to_.nickname, message.getBody()) 170 from_.sayOnIRCTo(to_.nickname, message.getBody())
168 else: 171 else:
169 self.error('==> Debug: received XMPP chat message from a non-XMPP participant, WTF ?', debug=True) 172 self.error('=> Debug: received XMPP chat message from a non-XMPP participant, WTF ?', debug=True)
170 173
171 except NoSuchParticipantException: 174 except NoSuchParticipantException:
172 if message.getTo() == self.jid: 175 if xmpp_c.nickname == self.nickname:
173 xmpp_c.send(xmpp.protocol.Message(to=message.getFrom(), body=self.respond(message.getBody(), participant=from_), typ='chat')) 176 xmpp_c.send(xmpp.protocol.Message(to=message.getFrom(), body=self.respond(message.getBody(), participant=from_), typ='chat'))
174 return 177 return
175 self.error('==> Debug: XMPP chat message not relayed, from_bare_jid='+from_bare_jid+' to='+str(message.getTo().getResource())+' from='+message.getFrom().getResource(), debug=True) 178 self.error('=> Debug: XMPP chat message not relayed', debug=True)
176 return 179 return
177 180
178 elif message.getType() == 'groupchat': 181 elif message.getType() == 'groupchat':
179 # message comes from a room 182 # message comes from a room
180 183
181 for child in message.getChildren(): 184 for child in message.getChildren():
182 if child.getName() == 'delay': 185 if child.getName() == 'delay':
183 # MUC delayed message 186 # MUC delayed message
184 return 187 return
185 188
186 if message.getTo() != self.jid: 189 if xmpp_c.nickname != self.nickname:
187 self.error('=> Debug: Ignoring XMPP MUC message not received on bot connection.', debug=True) 190 self.error('=> Debug: Ignoring XMPP MUC message not received on bot connection.', debug=True)
188 return 191 return
189 192
190 193
191 from_ = xmpp.protocol.JID(message.getFrom()) 194 from_ = xmpp.protocol.JID(message.getFrom())
397 if bridge in bridges: 400 if bridge in bridges:
398 bridges.remove(bridge) 401 bridges.remove(bridge)
399 return bridges 402 return bridges
400 403
401 404
402 def get_xmpp_connection(self, resource): 405 def get_xmpp_connection(self, nickname):
403 if self.xmpp_connections.has_key(resource): 406 if self.xmpp_connections.has_key(nickname):
404 c = self.xmpp_connections[resource] 407 c = self.xmpp_connections[nickname]
405 c.used_by += 1 408 c.used_by += 1
406 self.error('===> Debug: using existing XMPP connection for "'+str(self.bare_jid)+'/'+resource+'", now used by '+str(c.used_by)+' bridges', debug=True) 409 self.error('===> Debug: using existing XMPP connection for "'+nickname+'", now used by '+str(c.used_by)+' bridges', debug=True)
407 return c 410 return c
408 self.error('===> Debug: opening new XMPP connection for "'+str(self.bare_jid)+'/'+resource+'"', debug=True) 411 self.error('===> Debug: opening new XMPP connection for "'+nickname+'"', debug=True)
409 c = xmpp.client.Client(self.jid.getDomain(), debug=[]) 412 c = xmpp.client.Client(self.bare_jid.getDomain(), debug=[])
410 self.xmpp_connections[resource] = c 413 self.xmpp_connections[nickname] = c
411 c.used_by = 1 414 c.used_by = 1
415 c.nickname = nickname
412 c.connect() 416 c.connect()
413 c.auth(self.jid.getNode(), self.password, resource=resource) 417 c.auth(self.bare_jid.getNode(), self.password)
414 c.RegisterHandler('presence', self._xmpp_presence_handler) 418 c.RegisterHandler('presence', self._xmpp_presence_handler)
415 c.RegisterHandler('iq', self._xmpp_iq_handler) 419 c.RegisterHandler('iq', self._xmpp_iq_handler)
416 c.RegisterHandler('message', self._xmpp_message_handler) 420 c.RegisterHandler('message', self._xmpp_message_handler)
417 c.sendInitPresence() 421 c.sendInitPresence()
418 return c 422 return c
419 423
420 424
421 def close_xmpp_connection(self, resource): 425 def close_xmpp_connection(self, nickname):
422 self.xmpp_connections[resource].used_by -= 1 426 self.xmpp_connections[nickname].used_by -= 1
423 if self.xmpp_connections[resource].used_by < 1: 427 if self.xmpp_connections[nickname].used_by < 1:
424 self.error('===> Debug: closing XMPP connection for "'+str(self.bare_jid)+'/'+resource+'"', debug=True) 428 self.error('===> Debug: closing XMPP connection for "'+nickname+'"', debug=True)
425 del self.xmpp_connections[resource] 429 del self.xmpp_connections[nickname]
426 else: 430 else:
427 self.error('===> Debug: XMPP connection for "'+str(self.bare_jid)+'/'+resource+'" is now used by '+str(self.xmpp_connections[resource].used_by)+' bridges', debug=True) 431 self.error('===> Debug: XMPP connection for "'+nickname+'" is now used by '+str(self.xmpp_connections[nickname].used_by)+' bridges', debug=True)
428 432
429 433
430 def removeBridge(self, bridge): 434 def removeBridge(self, bridge):
431 self.bridges.remove(bridge) 435 self.bridges.remove(bridge)
432 del bridge 436 del bridge