Mercurial > xib
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') |