comparison bridge.py @ 17:32a35f7eff70

Rewrote/modified many things, multiple bridges should now work and are preferred over multiple bots. Signed-off-by: Charly COSTE <changaco@changaco.net>
author Charly COSTE <changaco@changaco.net>
date Thu, 20 Aug 2009 01:00:54 +0200
parents 5aa4399c0530
children 3cdf7bb580da
comparison
equal deleted inserted replaced
16:0c4a7452d66c 17:32a35f7eff70
24 24
25 class NoSuchParticipantException(Exception): pass 25 class NoSuchParticipantException(Exception): pass
26 26
27 27
28 class bridge: 28 class bridge:
29 def __init__(self, owner_bot, xmpp_room_jid, irc_room, irc_server, irc_port=6667, mode='normal'): 29 def __init__(self, owner_bot, xmpp_room_jid, irc_room, irc_server, mode, say_participants_list, irc_port=6667):
30 """Create a new bridge."""
30 self.bot = owner_bot 31 self.bot = owner_bot
31 self.irc_server = irc_server 32 self.irc_server = irc_server
32 self.irc_port = irc_port 33 self.irc_port = irc_port
33 self.irc_room = irc_room 34 self.irc_room = irc_room
35 self.say_participants_list = say_participants_list
34 self.participants = [] 36 self.participants = []
37 if mode not in ['normal', 'limited', 'minimal']:
38 raise Exception('Error: "'+mode+'" is not a correct value for a bridge\'s "mode" attribute')
35 self.mode = mode 39 self.mode = mode
36 40
41 # Join XMPP room
42 try:
43 self.xmpp_room = xmpp.muc(xmpp_room_jid)
44 self.xmpp_room.join(self.bot.xmpp_c, self.bot.nickname, callback=self._xmpp_join_callback)
45 except:
46 self.bot.error('Error: joining XMPP room failed')
47 raise
48
37 # Join IRC room 49 # Join IRC room
38 self.irc_connections_limit = -1
39 self.irc_connection = self.bot.irc.server()
40 self.irc_connection.nick_callback = self._irc_nick_callback
41 self.irc_connection.bridge = self
42 try: 50 try:
43 self.irc_connection.connect(irc_server, irc_port, self.bot.nickname) 51 self.irc_connection = self.bot.irc.server(irc_server, irc_port, self.bot.nickname)
52 self.irc_connection.connect(nick_callback=self._irc_nick_callback)
44 except: 53 except:
45 self.bot.error('Error: joining IRC room failed') 54 self.bot.error('Error: joining IRC room failed')
46 raise 55 raise
47 56 self.bot.error('[Notice] bridge "'+str(self)+'" is running in '+self.mode+' mode')
48 # Join XMPP room 57 self.say('[Notice] bridge "'+str(self)+'" is running in '+self.mode+' mode')
49 try: 58
50 self.xmpp_room = xmpp.muc(xmpp_room_jid) 59
51 self.xmpp_room.join(self.bot.xmpp_c, self.bot.nickname) 60 def _irc_nick_callback(self, error, arguments=[]):
52 except:
53 self.bot.error('Error: joining XMPP room failed')
54 raise
55
56
57 def _irc_nick_callback(self, error):
58 if error == None: 61 if error == None:
59 self.irc_connection.join(self.irc_room) 62 self.irc_connection.join(self.irc_room)
60 self.irc_connection.nick_callback = None
61 self.bot.error('===> Debug: successfully connected on IRC side of bridge "'+str(self)+'"', debug=True) 63 self.bot.error('===> Debug: successfully connected on IRC side of bridge "'+str(self)+'"', debug=True)
62 if error == 'nicknameinuse': 64 if error == 'nicknameinuse':
63 self.bot.error('Error: "'+self.bot.nickname+'" is already used in the IRC chan of bridge "'+str(self)+'"') 65 self.bot.error('Error: "'+self.bot.nickname+'" is already used in the IRC chan or reserved on the IRC server of bridge "'+str(self)+'"')
64 raise Exception('Error: "'+self.bot.nickname+'" is already used in the IRC chan of bridge "'+str(self)+'"') 66 raise Exception('Error: "'+self.bot.nickname+'" is already used in the IRC chan or reserved on the IRC server of bridge "'+str(self)+'"')
65 elif error == 'erroneusnickname': 67 elif error == 'erroneusnickname':
66 self.bot.error('Error: "'+self.bot.nickname+'" got "erroneusnickname" on bridge "'+str(self)+'"') 68 self.bot.error('Error: "'+self.bot.nickname+'" got "erroneusnickname" on bridge "'+str(self)+'"')
67 raise Exception('Error: "'+self.bot.nickname+'" got "erroneusnickname" on bridge "'+str(self)+'"') 69 raise Exception('Error: "'+self.bot.nickname+'" got "erroneusnickname" on bridge "'+str(self)+'"')
70 elif error == 'nicknametoolong':
71 self.bot.error('Error: "'+self.bot.nickname+'" got "nicknametoolong" on bridge "'+str(self)+'", limit seems to be '+str(arguments[0]))
72 raise Exception('Error: "'+self.bot.nickname+'" got "nicknametoolong" on bridge "'+str(self)+'", limit seems to be '+str(arguments[0]))
73
74
75 def _xmpp_join_callback(self, errors):
76 """Called by muc._xmpp_presence_handler"""
77 if len(errors) == 0:
78 self.bot.error('===> Debug: succesfully connected on XMPP side of bridge "'+str(self)+'"', debug=True)
79 for error in errors:
80 try:
81 raise error
82 except xmpp.muc.NicknameConflict:
83 self.bot.error('Error: "'+self.nickname+'" is already used in the XMPP MUC or reserved on the XMPP server of bridge "'+str(self)+'"')
84 raise Exception('Error: "'+self.nickname+'" is already used in the XMPP MUC or reserved on the XMPP server of bridge "'+str(self)+'"')
68 85
69 86
70 def addParticipant(self, protocol, nickname): 87 def addParticipant(self, protocol, nickname):
71 """Add a participant to the bridge.""" 88 """Add a participant to the bridge."""
72 if (protocol == 'irc' and nickname == self.irc_connection.get_nickname()) or (protocol == 'xmpp' and nickname == self.xmpp_room.nickname): 89 if (protocol == 'irc' and nickname == self.irc_connection.get_nickname()) or (protocol == 'xmpp' and nickname == self.xmpp_room.nickname):
73 raise Exception('Internal Error: cannot add self') 90 self.bot.error('===> Debug: not adding self ('+self.bot.nickname+') to bridge "'+str(self)+'"', debug=True)
91 return
74 try: 92 try:
75 p = self.getParticipant(nickname) 93 p = self.getParticipant(nickname)
76 if p.protocol != protocol: 94 if p.protocol != protocol:
77 if protocol == 'irc': 95 if protocol == 'irc':
78 p.createDuplicateOnXMPP() 96 p.createDuplicateOnXMPP()
85 pass 103 pass
86 self.bot.error('===> Debug: adding participant "'+nickname+'" from "'+protocol+'" to bridge "'+str(self)+'"', debug=True) 104 self.bot.error('===> Debug: adding participant "'+nickname+'" from "'+protocol+'" to bridge "'+str(self)+'"', debug=True)
87 p = participant(self, protocol, nickname) 105 p = participant(self, protocol, nickname)
88 self.participants.append(p) 106 self.participants.append(p)
89 if self.mode != 'normal' and protocol == 'xmpp': 107 if self.mode != 'normal' and protocol == 'xmpp':
90 xmpp_participants_nicknames = self.get_xmpp_participants_nicknames_list() 108 xmpp_participants_nicknames = self.get_participants_nicknames_list(protocols=['xmpp'])
91 self.say('[Info] Participants on XMPP: '+' '.join(xmpp_participants_nicknames), on_xmpp=False) 109 self.say('[Info] Participants on XMPP: '+' '.join(xmpp_participants_nicknames), on_xmpp=False)
92 return p 110 return p
93 111
94 112
95 def getParticipant(self, nickname): 113 def getParticipant(self, nickname):
98 if participant_.nickname == nickname: 116 if participant_.nickname == nickname:
99 return participant_ 117 return participant_
100 raise NoSuchParticipantException('there is no participant using the nickname "'+nickname+'" in this bridge') 118 raise NoSuchParticipantException('there is no participant using the nickname "'+nickname+'" in this bridge')
101 119
102 120
103 def get_xmpp_participants_nicknames_list(self): 121 def get_participants_nicknames_list(self, protocols=['irc', 'xmpp']):
104 xmpp_participants_nicknames = [] 122 """Returns a list of the nicknames of the bridge's participants that are connected on the XMPP side."""
105 for p in self.participants: 123 participants_nicknames = []
106 if p.protocol == 'xmpp': 124 for p in self.participants:
107 xmpp_participants_nicknames.append(p.nickname) 125 if p.protocol in protocols:
108 return xmpp_participants_nicknames 126 participants_nicknames.append(p.nickname)
109 127 return participants_nicknames
110 128
111 def removeParticipant(self, protocol, nickname, leave_message): 129
130 def removeParticipant(self, left_protocol, nickname, leave_message):
112 """Remove the participant using nickname from the bridge. Raises a NoSuchParticipantException if nickname is not used in the bridge.""" 131 """Remove the participant using nickname from the bridge. Raises a NoSuchParticipantException if nickname is not used in the bridge."""
132
133 was_on_both = None
113 p = self.getParticipant(nickname) 134 p = self.getParticipant(nickname)
114 if p.protocol == 'both': 135 if p.protocol == 'xmpp':
115 self.bot.error('===> Debug: "'+nickname+'" was on both sides of bridge "'+str(self)+'" but left '+protocol, debug=True) 136 if left_protocol == 'irc':
116 if protocol == 'xmpp': 137 was_on_both = True
117 p.protocol = 'irc' 138 elif left_protocol == 'xmpp':
118 p.createDuplicateOnXMPP() 139 if p.irc_connection == None and self.mode == 'normal':
119 elif protocol == 'irc': 140 was_on_both = True
120 p.protocol = 'xmpp' 141 p.protocol = 'irc'
121 p.createDuplicateOnIRC() 142 p.createDuplicateOnXMPP()
122 else: 143 else:
123 raise Exception('Internal Error: bad protocol') 144 was_on_both = False
145
146 elif p.protocol == 'irc':
147 if left_protocol == 'xmpp':
148 was_on_both = True
149 elif left_protocol == 'irc':
150 if p.xmpp_c == None and self.mode != 'minimal':
151 was_on_both = True
152 p.protocol = 'xmpp'
153 p.createDuplicateOnIRC()
154 else:
155 was_on_both = False
156
124 else: 157 else:
158 raise Exception('Internal Error: bad protocol')
159
160 if was_on_both == True:
161 self.bot.error('===> Debug: "'+nickname+'" was on both sides of bridge "'+str(self)+'" but left '+left_protocol, debug=True)
162
163 elif was_on_both == False:
125 self.bot.error('===> Debug: removing participant "'+nickname+'" from bridge "'+str(self)+'"', debug=True) 164 self.bot.error('===> Debug: removing participant "'+nickname+'" from bridge "'+str(self)+'"', debug=True)
126 self.participants.remove(p) 165 self.participants.remove(p)
127 p.leave(leave_message) 166 p.leave(leave_message)
167 del p
128 i = 0 168 i = 0
129 for p in self.participants: 169 for p in self.participants:
130 if p.protocol == 'xmpp': 170 if p.protocol == 'xmpp':
131 i += 1 171 i += 1
132 if protocol == 'xmpp' and self.irc_connections_limit != -1 and self.irc_connections_limit > i: 172 if left_protocol == 'xmpp':
133 self.switchToNormalMode() 173 if self.irc_connections_limit != -1 and self.irc_connections_limit > i:
134 del p 174 self.switchFromLimitedToNormalMode()
135 if self.mode != 'normal': 175 if self.mode != 'normal' and self.say_participants_list == True:
136 xmpp_participants_nicknames = self.get_xmpp_participants_nicknames_list() 176 xmpp_participants_nicknames = self.get_participants_nicknames_list(protocols=['xmpp'])
137 self.say('[Info] Participants on XMPP: '+' '.join(xmpp_participants_nicknames), on_xmpp=False) 177 self.say('[Info] Participants on XMPP: '+' '.join(xmpp_participants_nicknames), on_xmpp=False)
178 elif left_protocol == 'irc':
179 if self.mode == 'minimal' and self.say_participants_list == True:
180 irc_participants_nicknames = self.get_participants_nicknames_list(protocols=['irc'])
181 self.say('[Info] Participants on IRC: '+' '.join(irc_participants_nicknames), on_irc=False)
182
183 else:
184 self.bot.error('=> Debug: Bad decision tree, p.protocol='+p.protocol+' left_protocol='+left_protocol+'\np.xmpp_c='+str(p.xmpp_c)+'\np.irc_connection='+str(p.irc_connection), debug=True)
138 185
139 186
140 def say(self, message, on_irc=True, on_xmpp=True): 187 def say(self, message, on_irc=True, on_xmpp=True):
188 """Make the bot say something."""
141 if on_xmpp == True: 189 if on_xmpp == True:
142 self.xmpp_room.say(message) 190 self.xmpp_room.say(message)
143 if on_irc == True: 191 if on_irc == True:
144 self.irc_connection.privmsg(self.irc_room, auto_encode(message)) 192 self.irc_connection.privmsg(self.irc_room, auto_encode(message))
145 193
146 194
147 def switchToNormalMode(self): 195 def switchFromLimitedToNormalMode(self):
148 if self.mode == 'normal': 196 if self.mode != 'normal-limited':
149 return 197 return
150 prev_mode = self.mode 198 self.bot.error('===> Bridge is switching to normal mode.')
199 self.say('[Notice] Bridge is switching to normal mode.')
151 self.mode = 'normal' 200 self.mode = 'normal'
152 for p in self.participants: 201 for p in self.participants:
153 if p.protocol == 'xmpp': 202 if p.protocol == 'xmpp':
154 p.createDuplicateOnIRC() 203 p.createDuplicateOnIRC()
155 elif p.protocol == 'irc' and prev_mode == 'minimal': 204
156 p.createDuplicateOnXMPP() 205
157 self.bot.error('===> Bridge is switching to normal mode.') 206 def switchFromNormalToLimitedMode(self):
158 self.say('[Notice] Bridge is switching to normal mode.') 207 if self.mode != 'normal':
159 208 return
160 209 self.mode = 'normal-limited'
161 def switchToLimitedMode(self):
162 if self.mode == 'limited':
163 return
164 self.mode = 'limited'
165 i = 0 210 i = 0
166 for p in self.participants: 211 for p in self.participants:
167 if p.protocol == 'xmpp': 212 if p.protocol == 'xmpp':
168 i += 1 213 i += 1
169 if p.irc_connection: 214 if p.irc_connection != None:
170 p.irc_connection.closing = True 215 p.irc_connection.close('Bridge is switching to limited mode')
171 p.irc_connection.disconnect('Bridge is switching to limited mode')
172 p.irc_connection = None 216 p.irc_connection = None
173 self.irc_connections_limit = i 217 self.irc_connections_limit = i
174 self.bot.error('===> Bridge is switching to limited mode. Limit seems to be '+str(self.irc_connections_limit)+' on "'+self.irc_server+'".') 218 self.bot.error('===> Bridge is switching to limited mode. Limit seems to be '+str(self.irc_connections_limit)+' on "'+self.irc_server+'".')
175 self.say('[Warning] Bridge is switching to limited mode, it means that it will be transparent for XMPP users but not for IRC users, this is due to the IRC servers\' per-IP-address connections\' limit number which seems to be '+str(self.irc_connections_limit)+' on "'+self.irc_server+'".') 219 self.say('[Warning] Bridge is switching to limited mode, it means that it will be transparent for XMPP users but not for IRC users, this is due to the IRC servers\' per-IP-address connections\' limit number which seems to be '+str(self.irc_connections_limit)+' on "'+self.irc_server+'".')
176 xmpp_participants_nicknames = self.get_xmpp_participants_nicknames_list() 220 xmpp_participants_nicknames = self.get_participants_nicknames_list(protocols=['xmpp'])
177 self.say('[Info] Participants on XMPP: '+' '.join(xmpp_participants_nicknames), on_xmpp=False) 221 self.say('[Info] Participants on XMPP: '+' '.join(xmpp_participants_nicknames), on_xmpp=False)
178 222
179 223
180 def __str__(self): 224 def __str__(self):
181 return self.irc_room+'@'+self.irc_server+' <-> '+self.xmpp_room.room_jid 225 return self.irc_room+'@'+self.irc_server+' <-> '+self.xmpp_room.room_jid
184 def __del__(self): 228 def __del__(self):
185 # Delete participants objects 229 # Delete participants objects
186 for p in self.participants: 230 for p in self.participants:
187 p.leave('Removing bridge') 231 p.leave('Removing bridge')
188 del p 232 del p
189 # Leave IRC room 233 del self.participants
190 self.irc_connection.quit('Removing bridge') 234
191 # Close IRC connection 235 # Close IRC connection if not used by an other bridge, just leave the room otherwise
192 self.irc_connection.close() 236 self.irc_connection.used_by -= 1
237 if self.irc_connection.used_by < 1:
238 self.irc_connection.close('Removing bridge')
239 else:
240 self.irc_connection.part('Removing bridge')
193 del self.irc_connection 241 del self.irc_connection
242
194 # Leave XMPP room 243 # Leave XMPP room
195 self.xmpp_room.leave('Removing bridge') 244 self.xmpp_room.leave('Removing bridge')