changeset 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
files admin.py bot.py bridge.py commands.py irclib.py participant.py say_levels.py start_bots_from_xml_config.py weighted_string.py
diffstat 9 files changed, 293 insertions(+), 183 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/admin.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+class Admin:
+	def __init__(self, jid=None, say_level=None):
+		if jid:
+			self.jid = jid
+		if say_level:
+			self.say_level = say_level
--- a/bot.py
+++ b/bot.py
@@ -30,11 +30,12 @@ del muc
 from bridge import Bridge
 from participant import Participant
 import commands
+import say_levels
 
 
 class Bot(threading.Thread):
 	
-	def __init__(self, jid, password, nickname, admins_jid=[], error_fd=sys.stderr, debug=False):
+	def __init__(self, jid, password, nickname, admins=[], error_fd=sys.stderr, debug=False):
 		threading.Thread.__init__(self)
 		self.halt = False
 		self.bridges = []
@@ -44,7 +45,7 @@ class Bot(threading.Thread):
 		self.password = password
 		self.error_fd = error_fd
 		self.debug = debug
-		self.admins_jid = admins_jid
+		self.admins = admins
 		self.xmpp_connections = {}
 		self.irc = irclib.IRC()
 		self.irc.bot = self
@@ -55,18 +56,22 @@ class Bot(threading.Thread):
 		try:
 			self.xmpp_c = self.get_xmpp_connection(self.nickname)
 		except:
-			self.error('[Error] XMPP Connection failed')
+			self.error(say_levels.error, 'XMPP Connection failed')
 			raise
 		self.xmpp_thread = threading.Thread(target=self._xmpp_loop)
 		self.xmpp_thread.start()
 	
 	
-	def error(self, s, debug=False, send_to_admins=False):
+	def error(self, importance, message, debug=False, send_to_admins=False):
 		"""Output an error message."""
 		if send_to_admins == True:
-			self._send_message_to_admins(s)
-		if not debug or debug and self.debug:
-			self.error_fd.write(s.encode('utf-8')+"\n")
+			self._send_message_to_admins(importance, message)
+		if importance == -1:
+			return
+		if not debug:
+			self.error_fd.write(self.format_message(importance, message).encode('utf-8')+'\n')
+		if debug and self.debug:
+			self.error_fd.write('='*importance+'> '+message.encode('utf-8')+'\n')
 	
 	
 	def _xmpp_loop(self):
@@ -89,7 +94,7 @@ class Bot(threading.Thread):
 						if i == j:
 							ping = xmpp.protocol.Iq(typ='get')
 							ping.addChild(name='ping', namespace='urn:xmpp:ping')
-							self.error('=> Debug: sending XMPP ping', debug=True)
+							self.error(1, 'sending XMPP ping', debug=True)
 							c.pings.append(c.send(ping))
 						if hasattr(c, 'Process'):
 							c.Process(0.01)
@@ -99,17 +104,16 @@ class Bot(threading.Thread):
 			except RuntimeError:
 				pass
 			except (xml.parsers.expat.ExpatError, xmpp.protocol.XMLNotWellFormed):
-				self.error('=> Debug: invalid stanza', debug=True)
+				self.error(1, 'invalid stanza', debug=True)
 				self.reopen_xmpp_connection(c)
 				unlock = True
 			except xmpp.Conflict:
-				self.error('=> Debug: conflict', debug=True)
+				self.error(1, 'conflict', debug=True)
 				self.reopen_xmpp_connection(c)
 				unlock = True
 			except:
-				error = '[Error] Unknown exception on XMPP thread:\n'
-				error += traceback.format_exc()
-				self.error(error, send_to_admins=True)
+				error = 'Unknown exception on XMPP thread:\n'+traceback.format_exc()
+				self.error(say_levels.error, error, send_to_admins=True)
 				unlock = True
 			if unlock == True:
 				c.lock.release()
@@ -121,11 +125,10 @@ class Bot(threading.Thread):
 		xmpp_c = dispatcher._owner
 		
 		if xmpp_c.nickname != self.nickname:
-			self.error('=> Debug: Skipping XMPP presence not received on bot connection.', debug=True)
+			self.error(1, 'Skipping XMPP presence not received on bot connection.', debug=True)
 			return
 		
-		self.error('==> Debug: Received XMPP presence.', debug=True)
-		self.error(presence.__str__(fancy=1), debug=True)
+		self.error(2, 'Received XMPP presence.\n'+presence.__str__(fancy=1), debug=True)
 		
 		from_ = xmpp.protocol.JID(presence.getFrom())
 		bare_jid = unicode(from_.getNode()+'@'+from_.getDomain())
@@ -153,9 +156,9 @@ class Bot(threading.Thread):
 								if r == 'The conference component is shutting down':
 									# MUC server is going down, try to restart the bridges in 1 minute
 									bridges = self.findBridges([from_.getDomain()])
-									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'
-									self.restart_bridges_delayed(bridges, 60, error_message)
-									self.error(presence.__str__(fancy=1).encode('utf-8'), debug=True)
+									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'
+									error = (say_levels.warning, m)
+									self.restart_bridges_delayed(bridges, 60, error)
 									return
 								elif r == '':
 									r = 'None given'
@@ -163,7 +166,7 @@ class Bot(threading.Thread):
 								r = 'None given'
 							
 							# room has been destroyed, stop the bridge
-							self.error('[Error] The MUC room of the bridge '+str(bridge)+' has been destroyed with reason "'+r+'", stopping the bridge', send_to_admins=True)
+							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)
 							bridge.stop(message='The MUC room of the bridge has been destroyed with reason "'+r+'", stopping the bridge')
 				
 				else:
@@ -188,11 +191,11 @@ class Bot(threading.Thread):
 								return
 							item = x.getTag('item')
 							if not item:
-								self.error('=> Debug: bad stanza, no item element', debug=True)
+								self.error(1, 'bad stanza, no item element', debug=True)
 								return
 							new_nick = item.getAttr('nick')
 							if not new_nick:
-								self.error('=> Debug: bad stanza, new nick is not given', debug=True)
+								self.error(1, 'bad stanza, new nick is not given', debug=True)
 								return
 							p.changeNickname(new_nick, 'irc')
 							
@@ -225,9 +228,7 @@ class Bot(threading.Thread):
 						elif x and x.getTag('status', attrs={'code': '301'}):
 							# participant was banned
 							if p == None:
-								m = '[Error] bot got banned from XMPP'
-								self.error(m)
-								bridge.say(m, on_xmpp=False)
+								bridge.say(say_levels.error, 'bot got banned from XMPP', on_xmpp=False, send_to_admins=True)
 								self.removeBridge(bridge)
 								return
 							if item:
@@ -265,7 +266,6 @@ class Bot(threading.Thread):
 										bridges = self.findBridges([from_.getDomain()])
 										error_message = '[Error] XMPP Remote server not found: '+from_.getDomain()
 										self.restart_bridges_delayed(bridges, 60, error_message)
-										self.error(presence.__str__(fancy=1).encode('utf-8'), debug=True)
 									else:
 										raise Exception(presence.__str__(fancy=1).encode('utf-8'))
 					
@@ -278,8 +278,8 @@ class Bot(threading.Thread):
 						
 						# if we have the real jid check if the participant is a bot admin
 						if real_jid and isinstance(p, Participant):
-							for jid in self.admins_jid:
-								if xmpp.protocol.JID(jid).bareMatch(real_jid):
+							for admin in self.admins:
+								if xmpp.protocol.JID(admin.jid).bareMatch(real_jid):
 									p.bot_admin = True
 									break
 						
@@ -296,11 +296,10 @@ class Bot(threading.Thread):
 		# Ignore pongs
 		if iq.getType() in ['result', 'error'] and iq.getID() in xmpp_c.pings:
 			xmpp_c.pings.remove(iq.getID())
-			self.error('=> Debug: received XMPP pong', debug=True)
+			self.error(1, 'received XMPP pong', debug=True)
 			return
 		
-		self.error('==> Debug: Received XMPP iq.', debug=True)
-		self.error(iq.__str__(fancy=1), debug=True)
+		self.error(2, 'Received XMPP iq.\n'+iq.__str__(fancy=1), debug=True)
 	
 	
 	def _xmpp_message_handler(self, dispatcher, message):
@@ -317,8 +316,7 @@ class Bot(threading.Thread):
 				if from_bare_jid == bridge.xmpp_room_jid:
 					# message comes from a room participant
 					
-					self.error('==> Debug: Received XMPP chat message.', debug=True)
-					self.error(message.__str__(fancy=1), debug=True)
+					self.error(2, 'Received XMPP chat message.\n'+message.__str__(fancy=1), debug=True)
 					
 					try:
 						from_ = bridge.getParticipant(message.getFrom().getResource())
@@ -331,24 +329,22 @@ class Bot(threading.Thread):
 							r = self.respond(str(message.getBody()), participant=from_)
 							if isinstance(r, basestring) and len(r) > 0:
 								s = xmpp.protocol.Message(to=message.getFrom(), body=r, typ='chat')
-								self.error('==> Debug: Sending', debug=True)
-								self.error(s.__str__(fancy=1), debug=True)
+								self.error(2, 'Sending\n'+s.__str__(fancy=1), debug=True)
 								xmpp_c.send(s)
 							else:
-								self.error('=> Debug: won\'t answer.', debug=True)
+								self.error(1, 'won\'t answer.', debug=True)
 							return
-						self.error('=> Debug: XMPP chat message not relayed', debug=True)
+						self.error(1, 'XMPP chat message not relayed', debug=True)
 						return
 			
 			# message does not come from a room
 			if xmpp_c.nickname == self.nickname:
-				self.error('==> Debug: Received XMPP chat message.', debug=True)
-				self.error(message.__str__(fancy=1), debug=True)
+				self.error(2, 'Received XMPP chat message.\n'+message.__str__(fancy=1), debug=True)
 				
 				# Find out if the message comes from a bot admin
 				bot_admin = False
-				for jid in self.admins_jid:
-					if xmpp.protocol.JID(jid).bareMatch(message.getFrom()):
+				for admin in self.admins:
+					if xmpp.protocol.JID(admin.jid).bareMatch(message.getFrom()):
 						bot_admin = True
 						break
 				
@@ -356,12 +352,11 @@ class Bot(threading.Thread):
 				r = self.respond(str(message.getBody()), bot_admin=bot_admin)
 				if isinstance(r, basestring) and len(r) > 0:
 					s = xmpp.protocol.Message(to=message.getFrom(), body=r, typ='chat')
-					self.error('==> Debug: Sending', debug=True)
-					self.error(s.__str__(fancy=1), debug=True)
+					self.error(2, 'Sending\n'+s.__str__(fancy=1), debug=True)
 					xmpp_c.send(s)
 			
 			else:
-				self.error('=> Debug: Ignoring XMPP chat message not received on bot connection.', debug=True)
+				self.error(1, 'Ignoring XMPP chat message not received on bot connection.', debug=True)
 		
 		elif message.getType() == 'groupchat':
 			# message comes from a room
@@ -372,14 +367,14 @@ class Bot(threading.Thread):
 					return
 			
 			if xmpp_c.nickname != self.nickname:
-				self.error('=> Debug: Ignoring XMPP MUC message not received on bot connection.', debug=True)
+				self.error(1, 'Ignoring XMPP MUC message not received on bot connection.', debug=True)
 				return
 			
 			
 			from_ = xmpp.protocol.JID(message.getFrom())
 			
 			if unicode(from_.getResource()) == self.nickname:
-				self.error('=> Debug: Ignoring XMPP MUC message sent by self.', debug=True)
+				self.error(1, 'Ignoring XMPP MUC message sent by self.', debug=True)
 				return
 			
 			room_jid = unicode(from_.getNode()+'@'+from_.getDomain())
@@ -388,18 +383,17 @@ class Bot(threading.Thread):
 					resource = unicode(from_.getResource())
 					if resource == '':
 						# message comes from the room itself
-						self.error('=> Debug: Ignoring XMPP groupchat message sent by the room.', debug=True)
+						self.error(1, 'Ignoring XMPP groupchat message sent by the room.', debug=True)
 						return
 					else:
 						# message comes from a participant of the room
-						self.error('==> Debug: Received XMPP groupchat message.', debug=True)
-						self.error(message.__str__(fancy=1), debug=True)
+						self.error(2, 'Received XMPP groupchat message.\n'+message.__str__(fancy=1), debug=True)
 						
 						try:
 							participant = bridge.getParticipant(resource)
 						except Bridge.NoSuchParticipantException:
 							if resource != self.nickname:
-								self.error('=> Debug: NoSuchParticipantException "'+resource+'" on "'+str(bridge)+'", WTF ?', debug=True)
+								self.error(1, 'NoSuchParticipantException "'+resource+'" on "'+str(bridge)+'", WTF ?', debug=True)
 							return
 						
 						participant.sayOnIRC(message.getBody())
@@ -422,19 +416,16 @@ class Bot(threading.Thread):
 									elif err == 'forbidden':
 										# we don't have the permission to speak
 										# let's remove the bridge and tell admins
-										self.error('[Error] Not allowed to speak on the XMPP MUC of bridge '+str(b)+', stopping it', send_to_admins=True)
+										self.error(say_levels.error, 'Not allowed to speak on the XMPP MUC of bridge '+str(b)+', stopping it', send_to_admins=True)
 										b.stop(message='Not allowed to speak on the XMPP MUC, stopping bridge.')
 									else:
-										self.error('==> Debug: recevied unknown error message', debug=True)
-										self.error(message.__str__(fancy=1), debug=True)
+										self.error(2, 'recevied unknown error message\n'+message.__str__(fancy=1), debug=True)
 					return
 			
-			self.error('==> Debug: recevied unknown error message', debug=True)
-			self.error(message.__str__(fancy=1), debug=True)
+			self.error(2, 'recevied unknown error message\n'+message.__str__(fancy=1), debug=True)
 		
 		else:
-			self.error('==> Debug: Received XMPP message of unknown type "'+str(message.getType())+'".', debug=True)
-			self.error(message.__str__(fancy=1), debug=True)
+			self.error(2, 'Received XMPP message of unknown type "'+str(message.getType())+'".\n'+message.__str__(fancy=1), debug=True)
 	
 	
 	def _irc_event_handler(self, connection, event):
@@ -450,7 +441,7 @@ class Bot(threading.Thread):
 		if 'all' in event.eventtype() or 'motd' in event.eventtype() or event.eventtype() in ['nicknameinuse', 'nickcollision', 'erroneusnickname']:
 			return
 		if event.eventtype() in ['pong', 'privnotice', 'ctcp', 'nochanmodes', 'notexttosend', 'currenttopic', 'topicinfo', '328']:
-			self.error('=> Debug: ignoring IRC '+event.eventtype(), debug=True)
+			self.error(1, 'ignoring IRC '+event.eventtype(), debug=True)
 			return
 		
 		
@@ -467,15 +458,15 @@ class Bot(threading.Thread):
 					connection.really_connected = True
 					connection._call_nick_callbacks(None)
 				elif len(connection.nick_callbacks) > 0:
-					self.error('===> Debug: event target ('+event.target()+') and connection nickname ('+connection.nickname+') don\'t match')
+					self.error(3, 'event target ('+event.target()+') and connection nickname ('+connection.nickname+') don\'t match')
 					connection._call_nick_callbacks('nicknametoolong', arguments=[len(event.target())])
-			self.error('=> Debug: ignoring '+event.eventtype(), debug=True)
+			self.error(1, 'ignoring '+event.eventtype(), debug=True)
 			return
 		
 		
 		# A string representation of the event
 		event_str = 'connection='+connection.__str__()+'\neventtype='+event.eventtype()+'\nsource='+repr(event.source())+'\ntarget='+repr(event.target())+'\narguments='+repr(event.arguments())
-		debug_str = '==> Debug: Received IRC event.\n'+event_str
+		debug_str = 'Received IRC event.\n'+event_str
 		printed_event = False
 		
 		
@@ -490,22 +481,22 @@ class Bot(threading.Thread):
 			
 			if event.eventtype() in ['quit', 'part', 'nick', 'kick']:
 				if connection.get_nickname() != self.nickname:
-					self.error('=> Debug: ignoring IRC '+event.eventtype()+' not received on bot connection', debug=True)
+					self.error(1, 'ignoring IRC '+event.eventtype()+' not received on bot connection', debug=True)
 					return
 				else:
-					self.error(debug_str, debug=True)
+					self.error(2, debug_str, debug=True)
 					printed_event = True
 			
 			if event.eventtype() == 'kick' and len(event.arguments()) < 1:
-				self.error('=> Debug: length of arguments should be greater than 0 for a '+event.eventtype()+' event')
+				self.error(1, 'length of arguments should be greater than 0 for a '+event.eventtype()+' event')
 				return
 			
 			if event.eventtype() in ['pubmsg', 'action']:
 				if connection.get_nickname() != self.nickname:
-					self.error('=> Debug: ignoring IRC '+event.eventtype()+' not received on bot connection', debug=True)
+					self.error(1, 'ignoring IRC '+event.eventtype()+' not received on bot connection', debug=True)
 					return
 				if nickname == self.nickname:
-					self.error('=> Debug: ignoring IRC '+event.eventtype()+' sent by self', debug=True)
+					self.error(1, 'ignoring IRC '+event.eventtype()+' sent by self', debug=True)
 					return
 			
 			# TODO: lock self.bridges for thread safety
@@ -527,14 +518,14 @@ class Bot(threading.Thread):
 					
 					try:
 						to_ = bridge.getParticipant(event.target().split('!')[0])
-						self.error(debug_str, debug=True)
+						self.error(2, debug_str, debug=True)
 						from_.sayOnXMPPTo(to_.nickname, event.arguments()[0])
 						return
 						
 					except Bridge.NoSuchParticipantException:
 						if event.target().split('!')[0] == self.nickname:
 							# Message is for the bot
-							self.error(debug_str, debug=True)
+							self.error(2, debug_str, debug=True)
 							connection.privmsg(from_.nickname, self.respond(event.arguments()[0]))
 							return
 						else:
@@ -555,7 +546,7 @@ class Bot(threading.Thread):
 									bridge.removeParticipant('irc', kicked.nickname, 'Kicked by '+nickname+' (no reason was given)')
 							return
 						except Bridge.NoSuchParticipantException:
-							self.error('=> Debug: a participant that was not here has been kicked ? WTF ?')
+							self.error(1, 'a participant that was not here has been kicked ? WTF ?')
 							return
 					else:
 						continue
@@ -588,7 +579,7 @@ class Bot(threading.Thread):
 				# Chan message
 				if event.eventtype() in ['pubmsg', 'action']:
 					if bridge.irc_room == event.target().lower() and bridge.irc_server == connection.server:
-						self.error(debug_str, debug=True)
+						self.error(2, debug_str, debug=True)
 						message = event.arguments()[0]
 						if event.eventtype() == 'action':
 							message = '/me '+message
@@ -604,7 +595,7 @@ class Bot(threading.Thread):
 		# Handle bannedfromchan
 		if event.eventtype() == 'bannedfromchan':
 			if len(event.arguments()) < 1:
-				self.error('=> Debug: length of arguments should be greater than 0 for a '+event.eventtype()+' event')
+				self.error(1, 'length of arguments should be greater than 0 for a '+event.eventtype()+' event')
 				return
 			
 			for bridge in self.bridges:
@@ -612,19 +603,19 @@ class Bot(threading.Thread):
 					continue
 				
 				if event.target() == self.nickname:
-					self.error('[Error] the nickname "'+event.target()+'" is banned from the IRC chan of bridge "'+str(bridge)+'"')
+					self.error(say_levels.error, 'the nickname "'+event.target()+'" is banned from the IRC chan of bridge "'+str(bridge)+'"')
 					raise Exception('[Error] the nickname "'+event.target()+'" is banned from the IRC chan of bridge "'+str(bridge)+'"')
 				else:
 					try:
 						banned = bridge.getParticipant(event.target())
 						if banned.irc_connection != 'bannedfromchan':
 							banned.irc_connection = 'bannedfromchan'
-							self.error(debug_str, debug=True)
-							bridge.say('[Warning] the nickname "'+event.target()+'" is banned from the IRC chan', log=True)
+							self.error(2, debug_str, debug=True)
+							bridge.say(say_levels.warning, 'the nickname "'+event.target()+'" is banned from the IRC chan', log=True)
 						else:
-							self.error('=> Debug: ignoring '+event.eventtype(), debug=True)
+							self.error(1, 'ignoring '+event.eventtype(), debug=True)
 					except Bridge.NoSuchParticipantException:
-						self.error('=> Debug: no such participant. WTF ?')
+						self.error(1, 'no such participant. WTF ?')
 						return
 			
 			return
@@ -633,7 +624,7 @@ class Bot(threading.Thread):
 		# Joining events
 		if event.eventtype() in ['namreply', 'join']:
 			if connection.get_nickname() != self.nickname:
-				self.error('=> Debug: ignoring IRC '+event.eventtype()+' not received on bridge connection', debug=True)
+				self.error(1, 'ignoring IRC '+event.eventtype()+' not received on bridge connection', debug=True)
 				return
 			
 			if event.eventtype() == 'namreply':
@@ -646,8 +637,8 @@ class Bot(threading.Thread):
 			elif event.eventtype() == 'join':
 				bridges = self.getBridges(irc_room=event.target().lower(), irc_server=connection.server)
 				if len(bridges) == 0:
-					self.error(debug_str, debug=True)
-					self.error('===> Debug: no bridge found for "'+event.target().lower()+' at '+connection.server+'"', debug=True)
+					self.error(2, debug_str, debug=True)
+					self.error(3, 'no bridge found for "'+event.target().lower()+' at '+connection.server+'"', debug=True)
 					return
 				for bridge in bridges:
 					bridge.addParticipant('irc', nickname, irc_id=event.source())
@@ -656,14 +647,14 @@ class Bot(threading.Thread):
 		
 		if event.eventtype() in ['disconnect', 'kill', 'error']:
 			if len(event.arguments()) > 0 and event.arguments()[0] == 'Connection reset by peer':
-				self.error(debug_str, debug=True)
+				self.error(2, debug_str, debug=True)
 			else:
-				self.error(debug_str, send_to_admins=True)
+				self.error(2, debug_str, send_to_admins=True)
 			return
 		
 		
 		if event.eventtype() in ['cannotsendtochan', 'notonchannel']:
-			self.error(debug_str, debug=True)
+			self.error(2, debug_str, debug=True)
 			bridges = self.getBridges(irc_room=event.arguments()[0], irc_server=connection.server)
 			if len(bridges) > 1:
 				raise Exception, 'more than one bridge for one irc chan, WTF ?'
@@ -679,17 +670,22 @@ class Bot(threading.Thread):
 		
 		# Unhandled events
 		if not printed_event:
-			self.error('[Debug] The following IRC event was not handled:\n'+event_str+'\n', send_to_admins=True)
+			self.error(say_levels.debug, 'The following IRC event was not handled:\n'+event_str+'\n', send_to_admins=True)
 		else:
-			self.error('=> Debug: event not handled', debug=True)
+			self.error(1, 'event not handled', debug=True)
 			self._send_message_to_admins('[Debug] The following IRC event was not handled:\n'+event_str)
 	
 	
-	def _send_message_to_admins(self, message):
+	def _send_message_to_admins(self, importance, message):
 		"""[Internal] Send XMPP Message to bot admin(s)"""
-		for admin_jid in self.admins_jid:
+		for admin in self.admins:
+			if importance != -1:
+				if admin.say_level == say_levels.nothing or importance < admin.say_level:
+					continue
+				message = self.format_message(importance, message)
+			
 			try:
-				self.xmpp_c.send(xmpp.protocol.Message(to=admin_jid, body=message, typ='chat'))
+				self.xmpp_c.send(xmpp.protocol.Message(to=admin.jid, body=message, typ='chat'))
 			except:
 				pass
 	
@@ -712,6 +708,12 @@ class Bot(threading.Thread):
 		return bridges
 	
 	
+	def format_message(self, importance, message):
+		if importance < 0 or importance >= len(say_levels.levels):
+			raise Exception('[Internal Error] unknown message importance')
+		return'['+str(say_levels.get(importance))+'] '+message
+	
+	
 	def getBridges(self, irc_room=None, irc_server=None, xmpp_room_jid=None):
 		# TODO: lock self.bridges for thread safety
 		bridges = [b for b in self.bridges]
@@ -732,9 +734,9 @@ class Bot(threading.Thread):
 		if self.xmpp_connections.has_key(nickname):
 			c = self.xmpp_connections[nickname]
 			c.used_by += 1
-			self.error('===> Debug: using existing XMPP connection for "'+nickname+'", now used by '+str(c.used_by)+' bridges', debug=True)
+			self.error(3, 'using existing XMPP connection for "'+nickname+'", now used by '+str(c.used_by)+' bridges', debug=True)
 			return c
-		self.error('===> Debug: opening new XMPP connection for "'+nickname+'"', debug=True)
+		self.error(3, 'opening new XMPP connection for "'+nickname+'"', debug=True)
 		c = xmpp.client.Client(self.bare_jid.getDomain(), debug=[])
 		c.lock = threading.RLock()
 		c.lock.acquire()
@@ -770,7 +772,7 @@ class Bot(threading.Thread):
 				if p.xmpp_c == c:
 					participants.append(p)
 					p.xmpp_c = None
-		self.error('===> Debug: reopening XMPP connection for "'+nickname+'"', debug=True)
+		self.error(3, 'reopening XMPP connection for "'+nickname+'"', debug=True)
 		if self.xmpp_connections.has_key(nickname):
 			self.xmpp_connections.pop(nickname)
 		c.send(xmpp.protocol.Presence(typ='unavailable'))
@@ -794,14 +796,14 @@ class Bot(threading.Thread):
 		c.lock.acquire()
 		c.used_by -= 1
 		if c.used_by < 1 or force:
-			self.error('===> Debug: closing XMPP connection for "'+nickname+'"', debug=True)
+			self.error(3, 'closing XMPP connection for "'+nickname+'"', debug=True)
 			self.xmpp_connections.pop(nickname)
 			c.send(xmpp.protocol.Presence(typ='unavailable'))
 			c.lock.release()
 			del c
 		else:
 			c.lock.release()
-			self.error('===> Debug: XMPP connection for "'+nickname+'" is now used by '+str(c.used_by)+' bridges', debug=True)
+			self.error(3, 'XMPP connection for "'+nickname+'" is now used by '+str(c.used_by)+' bridges', debug=True)
 	
 	
 	def removeBridge(self, bridge, message='Removing bridge'):
@@ -832,14 +834,14 @@ class Bot(threading.Thread):
 		for b in self.bridges:
 			b.init2()
 		
-		self.error('Bot restarted.', send_to_admins=True)
+		self.error(-1, 'Bot restarted.', send_to_admins=True)
 	
 	
-	def restart_bridges_delayed(self, bridges, delay, error_message, protocol='xmpp'):
+	def restart_bridges_delayed(self, bridges, delay, error, protocol='xmpp'):
 		if len(bridges) > 0:
-			error_message += '\nThese bridges will be stopped:'
+			error[1] += '\nThese bridges will be stopped:'
 			for b in bridges:
-				error_message += '\n'+str(b)
+				error[1] += '\n'+str(b)
 				
 				if protocol == 'xmpp':
 					leave_message = 'Could not connect to the MUC server ('+b.xmpp_room_jid+')'
@@ -853,7 +855,7 @@ class Bot(threading.Thread):
 				
 				b.stop(message=leave_message)
 		
-		self.error(error_message, send_to_admins=True)
+		self.error(error[0], error[1], send_to_admins=True)
 	
 	
 	def stop(self, message='Stopping bot'):
--- a/bridge.py
+++ b/bridge.py
@@ -25,19 +25,12 @@ xmpp = muc.xmpp
 del muc
 
 from participant import Participant
+import say_levels
 
 
 class Bridge:
 	
-	_all = 0
-	_info = 1
-	_notice = 2
-	_warning = 3
-	_error = 4
-	_nothing = 5
-	_say_levels = ['all', 'info', 'notice', 'warning', 'error', 'nothing']
-	_modes = ['normal', 'bypass', 'limited', 'minimal']
-	
+	modes = ['bypass', 'normal', 'limited', 'minimal']
 	
 	class NoSuchParticipantException(Exception): pass
 	
@@ -51,12 +44,9 @@ class Bridge:
 		self.irc_connection_interval = irc_connection_interval
 		self.irc_charsets = irc_charsets
 		self.xmpp_room_jid = xmpp_room_jid
-		if hasattr(self.__class__, '_'+say_level):
-			self.say_level = getattr(self.__class__, '_'+say_level)
-		else:
-			raise Exception('[Error] "'+say_level+'" is not a correct value for a bridge\'s "say_level" attribute')
+		self.say_level = say_level
 		self.participants = []
-		if mode not in self.__class__._modes:
+		if mode not in self.__class__.modes:
 			raise Exception('[Error] "'+mode+'" is not a correct value for a bridge\'s "mode" attribute')
 		self.mode = mode
 		
@@ -74,11 +64,11 @@ class Bridge:
 		self.irc_connection = self.bot.irc.open_connection(self.irc_server, self.irc_port, self.bot.nickname, delay=self.irc_connection_interval)
 		self.irc_connection.connect(nick_callback=self._irc_nick_callback, charsets=self.irc_charsets)
 		
-		self.bot.error('[Notice] bridge "'+str(self)+'" is running in '+self.mode+' mode and a say_level of "'+self.__class__._say_levels[self.say_level]+'"')
+		self.bot.error(say_levels.notice, 'bridge "'+str(self)+'" is running in '+self.mode+' mode and a say_level of "'+str(self.say_level)+'"')
 	
 	
 	def _join_irc_failed(self):
-		self.bot.error('[Error] failed to connect to the IRC chan of bridge "'+str(self)+'", stopping bridge', send_to_admins=True)
+		self.bot.error(say_levels.error, 'failed to connect to the IRC chan of bridge "'+str(self)+'", stopping bridge', send_to_admins=True)
 		self.stop(message='failed to connect to the IRC chan')
 	
 	
@@ -87,14 +77,14 @@ class Bridge:
 			if self.mode == None:
 				return
 			self.irc_connection.join(self.irc_room)
-			self.bot.error('===> Debug: successfully connected on IRC side of bridge "'+str(self)+'"', debug=True)
-			self.say('[Notice] bridge "'+str(self)+'" is running in '+self.mode+' mode', on_xmpp=False)
+			self.bot.error(3, 'successfully connected on IRC side of bridge "'+str(self)+'"', debug=True)
+			self.say(say_levels.notice, 'bridge "'+str(self)+'" is running in '+self.mode+' mode', on_xmpp=False)
 			if self.mode not in ['normal', 'bypass']:
 				self.show_participants_list_on(protocols=['irc'])
 		else:
 			self.mode = None
 			if self.xmpp_room.connected == True:
-				self.say('[Error] failed to connect to the IRC chan, leaving ...', on_irc=False)
+				self.say(say_levels.error, 'failed to connect to the IRC chan, leaving ...', on_irc=False)
 			try:
 				if error == 'nicknameinuse':
 					raise Exception('[Error] "'+self.bot.nickname+'" is already used in the IRC chan or reserved on the IRC server of bridge "'+str(self)+'"')
@@ -125,12 +115,12 @@ class Bridge:
 				del self.reconnecting
 			if self.mode == None:
 				return
-			self.bot.error('===> Debug: succesfully connected on XMPP side of bridge "'+str(self)+'"', debug=True)
-			self.say('[Notice] bridge "'+str(self)+'" is running in '+self.mode+' mode', on_irc=False)
+			self.bot.error(3, 'succesfully connected on XMPP side of bridge "'+str(self)+'"', debug=True)
+			self.say(say_levels.notice, 'bridge "'+str(self)+'" is running in '+self.mode+' mode', on_irc=False)
 		else:
 			self.mode = None
 			if self.irc_connection.really_connected == True:
-				self.say('[Error] failed to connect to the XMPP room, leaving ...', on_xmpp=False)
+				self.say(say_levels.error, 'failed to connect to the XMPP room, leaving ...', on_xmpp=False)
 			for error in errors:
 				try:
 					raise error
@@ -138,14 +128,14 @@ class Bridge:
 					self._RemoteServerNotFound_handler()
 				except:
 					trace = traceback.format_exc()
-			self.bot.error('[Error] failed to connect to the XMPP room of bridge "'+str(self)+'", stopping bridge\n'+trace, send_to_admins=True)
+			self.bot.error(say_levels.error, 'failed to connect to the XMPP room of bridge "'+str(self)+'", stopping bridge\n'+trace, send_to_admins=True)
 			self.stop(message='failed to connect to the XMPP room')
 	
 	
 	def addParticipant(self, from_protocol, nickname, real_jid=None, irc_id=None):
 		"""Add a participant to the bridge."""
 		if (from_protocol == 'irc' and nickname == self.bot.nickname) or (from_protocol == 'xmpp' and nickname == self.bot.nickname):
-			self.bot.error('===> Debug: not adding self ('+self.bot.nickname+') to bridge "'+str(self)+'"', debug=True)
+			self.bot.error(3, 'not adding self ('+self.bot.nickname+') to bridge "'+str(self)+'"', debug=True)
 			return
 		try:
 			p = self.getParticipant(nickname)
@@ -163,14 +153,14 @@ class Bridge:
 			return
 		
 		self.lock.acquire()
-		self.bot.error('===> Debug: adding participant "'+nickname+'" from "'+from_protocol+'" to bridge "'+str(self)+'"', debug=True)
+		self.bot.error(3, 'adding participant "'+nickname+'" from "'+from_protocol+'" to bridge "'+str(self)+'"', debug=True)
 		try:
 			p = Participant(self, from_protocol, nickname, real_jid=real_jid)
 		except IOError:
-			self.bot.error('===> Debug: IOError while adding participant "'+nickname+'" from "'+from_protocol+'" to bridge "'+str(self)+'", reconnectiong ...', debug=True)
+			self.bot.error(3, 'IOError while adding participant "'+nickname+'" from "'+from_protocol+'" to bridge "'+str(self)+'", reconnectiong ...', debug=True)
 			p.xmpp_c.reconnectAndReauth()
 		except:
-			self.bot.error('===> Debug: unknown error while adding participant "'+nickname+'" from "'+from_protocol+'" to bridge "'+str(self)+'"', debug=True)
+			self.bot.error(3, 'unknown error while adding participant "'+nickname+'" from "'+from_protocol+'" to bridge "'+str(self)+'"', debug=True)
 			traceback.print_exc()
 			return
 		self.participants.append(p)
@@ -249,7 +239,7 @@ class Bridge:
 				if p.nickname != p.duplicate_nickname:
 					p.leave('Bridge is switching to '+new_mode+' mode')
 		
-		self.say('[Notice] Bridge is switching from '+old_mode+' to '+new_mode+' mode.', log=True)
+		self.say(say_levels.notice, 'Bridge is switching from '+old_mode+' to '+new_mode+' mode.', log=True)
 	
 	
 	def getParticipant(self, nickname):
@@ -328,11 +318,11 @@ class Bridge:
 			raise Exception('[Internal Error] bad protocol')
 		
 		if was_on_both == True:
-			self.bot.error('===> Debug: "'+nickname+'" was on both sides of bridge "'+str(self)+'" but left '+left_protocol, debug=True)
+			self.bot.error(3, '"'+nickname+'" was on both sides of bridge "'+str(self)+'" but left '+left_protocol, debug=True)
 		
 		elif was_on_both == False:
 			self.lock.acquire()
-			self.bot.error('===> Debug: removing participant "'+nickname+'" from bridge "'+str(self)+'"', debug=True)
+			self.bot.error(3, 'removing participant "'+nickname+'" from bridge "'+str(self)+'"', debug=True)
 			self.participants.remove(p)
 			p.leave(leave_message)
 			del p
@@ -345,14 +335,14 @@ class Bridge:
 					self.show_participants_list_on(protocols=['xmpp'])
 		
 		else:
-			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)
+			self.bot.error(1, '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)
 	
 	
 	def restart(self):
 		"""Restart the bridge"""
 		
 		# Tell admins
-		self.bot.error('Restarting bridge '+str(self), send_to_admins=True)
+		self.bot.error(-1, 'Restarting bridge '+str(self), send_to_admins=True)
 		
 		# Stop the bridge
 		self.stop(message='Restarting bridge')
@@ -361,19 +351,12 @@ class Bridge:
 		self.init2()
 	
 	
-	def say(self, message, on_irc=True, on_xmpp=True, log=False):
+	def say(self, importance, message, on_irc=True, on_xmpp=True, log=False, send_to_admins=False):
 		"""Make the bot say something."""
-		if message[0] != '[':
-			raise Exception('[Internal Error] message does not start with "["')
-		if log:
-			self.bot.error(message+' ('+str(self)+')')
-		if self.say_level == self.__class__._nothing:
-			return
-		level = re.findall('^\[(Info|Notice|Warning|Error)\]', message)
-		if len(level) == 0:
-			raise Exception('[Internal Error] unknown message importance "'+re.findall('^\[([^[\]]+)', message)[0]+'"')
-		level = level[0].lower()
-		if getattr(self.__class__, '_'+level) < self.say_level:
+		message = self.bot.format_message(importance, message)
+		if log or send_to_admins:
+			self.bot.error(importance, message+' ('+str(self)+')', send_to_admins=send_to_admins)
+		if importance < self.say_level:
 			return
 		if on_xmpp == True:
 			self.xmpp_room.say(message)
@@ -384,10 +367,10 @@ class Bridge:
 	def show_participants_list_on(self, protocols=[]):
 		if 'irc' in protocols and self.irc_connection.really_connected:
 			xmpp_participants_nicknames = self.get_participants_nicknames_list(protocols=['xmpp'])
-			self.say('[Info] Participants on XMPP: '+'  '.join(xmpp_participants_nicknames), on_xmpp=False)
+			self.say(say_levels.info, 'Participants on XMPP: '+'  '.join(xmpp_participants_nicknames), on_xmpp=False)
 		if 'xmpp' in protocols:
 			irc_participants_nicknames = self.get_participants_nicknames_list(protocols=['irc'])
-			self.say('[Info] Participants on IRC: '+'  '.join(irc_participants_nicknames), on_irc=False)
+			self.say(say_levels.info, 'Participants on IRC: '+'  '.join(irc_participants_nicknames), on_irc=False)
 	
 	
 	def stop(self, message='Stopping bridge'):
--- a/commands.py
+++ b/commands.py
@@ -22,7 +22,9 @@ import muc
 xmpp = muc.xmpp
 del muc
 
+from admin import Admin
 from bridge import Bridge
+import say_levels
 
 
 commands = ['xmpp-participants', 'irc-participants', 'xmpp-connections', 'irc-connections', 'connections', 'bridges']
@@ -80,8 +82,8 @@ def add_bridge(bot, command, args_array,
 	parser.add_argument('xmpp_room_jid', type=str)
 	parser.add_argument('irc_chan', type=str)
 	parser.add_argument('irc_server', type=str)
-	parser.add_argument('--mode', choices=Bridge._modes, default='normal')
-	parser.add_argument('--say-level', choices=Bridge._say_levels, default='all')
+	parser.add_argument('--mode', choices=Bridge.modes, default=Bridge.modes[0])
+	parser.add_argument('--say-level', choices=say_levels.levels, default=say_levels.levels[1])
 	parser.add_argument('--irc-port', type=int, default=6667)
 	try:
 		args = parser.parse_args(args_array)
@@ -96,11 +98,15 @@ def add_bridge(bot, command, args_array,
 def add_xmpp_admin(bot, command, args_array, bridge):
 	parser = ArgumentParser(prog=command)
 	parser.add_argument('jid', type=str)
+	parser.add_argument('--say-level', choices=say_levels.levels, default=say_levels.levels[1])
 	try:
 		args = parser.parse_args(args_array)
 	except Exception as e:
 		return '\n'+e.args[1]
-	bot.admins_jid.append(args.jid)
+	admin = Admin()
+	admin.jid = args.jid
+	admin.say_level = args.say_level
+	bot.admins.append(admin)
 	for b in bot.bridges:
 		for p in b.participants:
 			if p.real_jid != None and xmpp.protocol.JID(args.jid).bareMatch(p.real_jid):
@@ -124,7 +130,7 @@ def bridges(bot, command, args_array, br
 		if args.show_mode:
 			ret += ' - mode='+b.mode
 		if args.show_say_level:
-			ret += ' - say_level='+Bridge._say_levels[b.say_level]
+			ret += ' - say_level='+str(b.say_level)
 		if args.show_participants:
 			xmpp_participants_nicknames = b.get_participants_nicknames_list(protocols=['xmpp'])
 			ret += '\nparticipants on XMPP ('+str(len(xmpp_participants_nicknames))+'): '+' '.join(xmpp_participants_nicknames)
@@ -138,7 +144,7 @@ def bridges(bot, command, args_array, br
 def change_bridges_mode(bot, command, args_array, bridge):
 	parser = ArgumentParser(prog=command)
 	parser.add_argument('bridge_id', nargs='+')
-	parser.add_argument('new_mode', choices=Bridge._modes)
+	parser.add_argument('new_mode', choices=Bridge.modes)
 	try:
 		args = parser.parse_args(args_array)
 	except Exception as e:
--- a/irclib.py
+++ b/irclib.py
@@ -73,6 +73,8 @@ import threading
 import traceback
 import math
 
+import say_levels
+
 VERSION = 0, 4, 8
 DEBUG = 0
 
@@ -176,7 +178,7 @@ class IRC:
             stack[0][0](*stack[0][1])
             stack.pop(0)
             delay = self.connection_interval(server=server_str)
-            self.bot.error('==> Debug: waiting '+str(delay)+' seconds before next connection on '+server_str, debug=True)
+            self.bot.error(2, 'waiting '+str(delay)+' seconds before next connection on '+server_str, debug=True)
             self.execute_delayed(delay, self._connection_loop, (server_str,))
         else:
             self.connection_stacks.pop(server_str)
@@ -293,7 +295,7 @@ class IRC:
             except ServerNotConnectedError:
                 self.bot.restart()
             except:
-                self.bot.error('[Error] Unkonwn exception on IRC thread:\n'+traceback.format_exc(), send_to_admins=True)
+                self.bot.error(say_levels.error, 'Unkonwn exception on IRC thread:\n'+traceback.format_exc(), send_to_admins=True)
 
     def disconnect_all(self, message="", volontary=True):
         """Disconnects all connections."""
@@ -473,7 +475,7 @@ class ServerConnection(Connection):
         self.irclibobj.execute_delayed(60, self._ping)
         if self.connected == False:
             return
-        self.irclibobj.bot.error('=> Debug: sending IRC ping', debug=True)
+        self.irclibobj.bot.error(1, 'sending IRC ping', debug=True)
         self.ping(self.get_server_name())
 
 
@@ -513,7 +515,7 @@ class ServerConnection(Connection):
         
         if self.used_by > 0:
             self.used_by += 1
-            self.irclibobj.bot.error('===> Debug: using existing IRC connection for '+self.__str__()+', this connection is now used by '+str(self.used_by)+' bridges', debug=True)
+            self.irclibobj.bot.error(3, 'using existing IRC connection for '+self.__str__()+', this connection is now used by '+str(self.used_by)+' bridges', debug=True)
             if self.really_connected:
                 self._call_nick_callbacks(None)
             self.lock.release()
@@ -550,9 +552,9 @@ class ServerConnection(Connection):
         self.lock.acquire()
 
         if self.socket != 'closed':
-            self.irclibobj.bot.error('===> Debug: opening new IRC connection for '+self.__str__(), debug=True)
+            self.irclibobj.bot.error(3, 'opening new IRC connection for '+self.__str__(), debug=True)
         else:
-            self.irclibobj.bot.error('===> Debug: reopening IRC connection for '+self.__str__(), debug=True)
+            self.irclibobj.bot.error(3, 'reopening IRC connection for '+self.__str__(), debug=True)
 
         if self.ipv6:
             self.socket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
@@ -588,9 +590,9 @@ class ServerConnection(Connection):
             f(error, arguments=arguments)
         self.nick_callbacks = []
         if i == 0:
-            self.irclibobj.bot.error('=> Debug: no nick callback for "'+self.__str__()+'"', debug=True)
+            self.irclibobj.bot.error(1, 'no nick callback for "'+self.__str__()+'"', debug=True)
         else:
-            self.irclibobj.bot.error('=> Debug: called '+str(i)+' callback(s) for "'+self.__str__()+'"', debug=True)
+            self.irclibobj.bot.error(1, 'called '+str(i)+' callback(s) for "'+self.__str__()+'"', debug=True)
 
 
     def add_nick_callback(self, callback):
--- a/participant.py
+++ b/participant.py
@@ -23,6 +23,8 @@ import muc
 xmpp = muc.xmpp
 del muc
 
+import say_levels
+
 
 class Participant:
 	def __init__(self, owner_bridge, protocol, nickname, real_jid=None):
@@ -64,11 +66,11 @@ class Participant:
 	
 	def _xmpp_join_callback(self, errors):
 		if len(errors) == 0:
-			m = '===> Debug: "'+self.nickname+'" duplicate succesfully created on XMPP side of bridge "'+str(self.bridge)+'"'
+			m = '"'+self.nickname+'" duplicate succesfully created on XMPP side of bridge "'+str(self.bridge)+'"'
 			if self.nickname != self.duplicate_nickname:
 				m += ' using nickname "'+self.duplicate_nickname+'"'
-				self.bridge.say('[Info] "'+self.nickname+'" will appear as "'+self.duplicate_nickname+'" on XMPP because its real nickname is reserved or contains unauthorized characters')
-			self.bridge.bot.error(m, debug=True)
+				self.bridge.say(say_levels.info, '"'+self.nickname+'" will appear as "'+self.duplicate_nickname+'" on XMPP because its real nickname is reserved or contains unauthorized characters')
+			self.bridge.bot.error(3, m, debug=True)
 		elif self.xmpp_c != 'both':
 			for error in errors:
 				try:
@@ -80,9 +82,9 @@ class Participant:
 					if self.bridge.mode == 'bypass':
 						new_duplicate_nickname = self._get_new_duplicate_nickname()
 						if new_duplicate_nickname != None:
-							self.bridge.bot.error('===> Debug: "'+self.duplicate_nickname+'" is already used in the XMPP MUC or reserved on the XMPP server of bridge "'+str(self.bridge)+'", trying "'+new_duplicate_nickname+'"', debug=True)
+							self.bridge.bot.error(3, '"'+self.duplicate_nickname+'" is already used in the XMPP MUC or reserved on the XMPP server of bridge "'+str(self.bridge)+'", trying "'+new_duplicate_nickname+'"', debug=True)
 							if self.duplicate_nickname == self.nickname:
-								self.bridge.say('[Info] The nickname "'+self.duplicate_nickname+'" is used on both rooms or reserved on the XMPP server')
+								self.bridge.say(say_levels.info, 'The nickname "'+self.duplicate_nickname+'" is used on both rooms or reserved on the XMPP server')
 							self.duplicate_nickname = new_duplicate_nickname
 							if isinstance(self.xmpp_c, xmpp.client.Client):
 								self.bridge.bot.close_xmpp_connection(self.nickname)
@@ -91,11 +93,11 @@ class Participant:
 							return
 					
 					else:
-						self.bridge.say('[Warning] The nickname "'+self.nickname+'" is used on both rooms or reserved on the XMPP server', log=True)
+						self.bridge.say(say_levels.warning, 'The nickname "'+self.nickname+'" is used on both rooms or reserved on the XMPP server', log=True)
 						if self.muc.connected == True:
 							self.muc.leave('Changed nickname to "'+self.nickname+'"')
 				except xmpp.muc.RoomIsFull:
-					self.bridge.say('[Warning] XMPP room is full', log=True)
+					self.bridge.say(say_levels.warning, 'XMPP room is full', log=True)
 				except xmpp.muc.RemoteServerNotFound:
 					self.bridge._RemoteServerNotFound_handler()
 				
@@ -114,11 +116,11 @@ class Participant:
 	def _irc_nick_callback(self, error, arguments=[]):
 		if error == None:
 			self.irc_connection.join(self.bridge.irc_room)
-			m = '===> Debug: "'+self.nickname+'" duplicate succesfully created on IRC side of bridge "'+str(self.bridge)+'"'
+			m = '"'+self.nickname+'" duplicate succesfully created on IRC side of bridge "'+str(self.bridge)+'"'
 			if self.nickname != self.duplicate_nickname:
 				m += ' using nickname "'+self.duplicate_nickname+'"'
-				self.bridge.say('[Info] "'+self.nickname+'" will appear as "'+self.duplicate_nickname+'" on IRC because its real nickname is reserved or contains unauthorized characters')
-			self.bridge.bot.error(m, debug=True)
+				self.bridge.say(say_levels.info, '"'+self.nickname+'" will appear as "'+self.duplicate_nickname+'" on IRC because its real nickname is reserved or contains unauthorized characters')
+			self.bridge.bot.error(3, m, debug=True)
 		
 		elif self.irc_connection != 'both':
 			
@@ -129,9 +131,9 @@ class Participant:
 				if self.bridge.mode == 'bypass':
 					new_duplicate_nickname = self._get_new_duplicate_nickname()
 					if new_duplicate_nickname != None:
-						self.bridge.bot.error('===> Debug: "'+self.duplicate_nickname+'" is already used or reserved on the IRC server of bridge "'+str(self.bridge)+'", trying "'+new_duplicate_nickname+'"', debug=True)
+						self.bridge.bot.error(3, '"'+self.duplicate_nickname+'" is already used or reserved on the IRC server of bridge "'+str(self.bridge)+'", trying "'+new_duplicate_nickname+'"', debug=True)
 						if self.duplicate_nickname == self.nickname:
-							self.bridge.say('[Info] The nickname "'+self.duplicate_nickname+'" is used or reserved on the IRC server')
+							self.bridge.say(say_levels.info, 'The nickname "'+self.duplicate_nickname+'" is used or reserved on the IRC server')
 						self.duplicate_nickname = new_duplicate_nickname
 						if isinstance(self.irc_connection, ServerConnection):
 							self.irc_connection.close('')
@@ -140,7 +142,7 @@ class Participant:
 						return
 				
 				else:
-					self.bridge.say('[Warning] The nickname "'+self.nickname+'" is used or reserved on the IRC server', log=True)
+					self.bridge.say(say_levels.warning, 'The nickname "'+self.nickname+'" is used or reserved on the IRC server', log=True)
 			
 			elif error == 'erroneusnickname':
 				if self.bridge.mode == 'bypass':
@@ -151,13 +153,13 @@ class Participant:
 					self.createDuplicateOnIRC()
 					return
 				else:
-					self.bridge.say('[Warning] The nickname "'+self.nickname+'" contains unauthorized characters and cannot be used in the IRC channel', log=True)
+					self.bridge.say(say_levels.warning, 'The nickname "'+self.nickname+'" contains unauthorized characters and cannot be used in the IRC channel', log=True)
 			
 			elif error == 'nicknametoolong':
-				self.bridge.say('[Warning] The nickname "'+self.nickname+'" is too long (limit seems to be '+str(arguments[0])+') and cannot be used in the IRC channel', log=True)
+				self.bridge.say(say_levels.warning, 'The nickname "'+self.nickname+'" is too long (limit seems to be '+str(arguments[0])+') and cannot be used in the IRC channel', log=True)
 			
 			else:
-				self.bridge.say('[Warning] unknown error while adding "'+self.nickname+'" to IRC side of bridge', log=True)
+				self.bridge.say(say_levels.warning, 'unknown error while adding "'+self.nickname+'" to IRC side of bridge', log=True)
 			
 			if isinstance(self.irc_connection, ServerConnection):
 				self.irc_connection.close('')
@@ -165,7 +167,7 @@ class Participant:
 	
 	
 	def set_both_sides(self):
-		self.bridge.say('[Warning] The nickname "'+self.nickname+'" is used on both sides of the bridge', log=True)
+		self.bridge.say(say_levels.warning, 'The nickname "'+self.nickname+'" is used on both sides of the bridge', log=True)
 		if isinstance(self.irc_connection, ServerConnection):
 			self.irc_connection.close('')
 		if self.irc_connection != 'both':
@@ -296,7 +298,7 @@ class Participant:
 				else:
 					self.bridge.irc_connection.privmsg(self.bridge.irc_room, '<'+self.nickname+'> '+message)
 		except EncodingException:
-			self.bridge.say('[Warning] "'+self.nickname+'" is sending messages using an unknown encoding', log=True)
+			self.bridge.say(say_levels.warning, '"'+self.nickname+'" is sending messages using an unknown encoding', log=True)
 	
 	
 	def sayOnIRCTo(self, to, message):
@@ -304,7 +306,7 @@ class Participant:
 			try:
 				self.irc_connection.privmsg(to, message)
 			except EncodingException:
-				self.bridge.say('[Warning] "'+self.nickname+'" is sending messages using an unknown encoding', log=True)
+				self.bridge.say(say_levels.warning, '"'+self.nickname+'" is sending messages using an unknown encoding', log=True)
 		elif not isinstance(self.xmpp_c, xmpp.client.Client):
 			if self.bridge.mode != 'normal':
 				self.bridge.getParticipant(to).sayOnXMPPTo(self.nickname, 'Sorry but cross-protocol private messages are disabled in '+self.bridge.mode+' mode.')
@@ -322,7 +324,7 @@ class Participant:
 				else:
 					self.bridge.xmpp_room.say('<'+self.nickname+'> '+message)
 		except EncodingException:
-			self.bridge.say('[Warning] "'+self.nickname+'" is sending messages using an unknown encoding', log=True)
+			self.bridge.say(say_levels.warning, '"'+self.nickname+'" is sending messages using an unknown encoding', log=True)
 	
 	
 	def sayOnXMPPTo(self, to, message):
@@ -335,7 +337,7 @@ class Participant:
 				else:
 					self.bridge.getParticipant(to).sayOnXMPPTo(self.nickname, 'Sorry but you cannot send cross-protocol private messages because I don\'t have an XMPP duplicate with your nickname.')
 		except EncodingException:
-			self.bridge.say('[Warning] "'+self.nickname+'" is sending messages using an unknown encoding', log=True)
+			self.bridge.say(say_levels.warning, '"'+self.nickname+'" is sending messages using an unknown encoding', log=True)
 	
 	
 	def leave(self, message):
new file mode 100644
--- /dev/null
+++ b/say_levels.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+from weighted_string import Weighted_string
+
+debug = Weighted_string(0, 'debug')
+info = Weighted_string(1, 'info')
+notice = Weighted_string(2, 'notice')
+warning = Weighted_string(3, 'warning')
+error = Weighted_string(4, 'error')
+nothing = Weighted_string(5, 'nothing')
+levels = ['debug', 'info', 'notice', 'warning', 'error', 'nothing']
+
+def get(level):
+	if isinstance(level, int):
+		return globals()[levels[level]]
+	elif isinstance(level, basestring):
+		try:
+			return globals()[str(level)]
+		except KeyError:
+			raise ValueError, 'say_level must be one of these values: '+', '.join(levels)
+	else:
+		raise TypeError, 'say_level must be either an int or a string'
--- a/start_bots_from_xml_config.py
+++ b/start_bots_from_xml_config.py
@@ -22,7 +22,9 @@ from time import sleep
 import sys
 import traceback
 
+from admin import Admin
 from bot import Bot
+import say_levels
 
 
 try:
@@ -49,11 +51,19 @@ for bot_el in config.getElementsByTagNam
 	if bot_el.hasAttribute('debug'):
 		if bot_el.getAttribute('debug') == 'true':
 			debug = True
-	admins_jid = []
+	
+	admins = []
 	for admin_el in bot_el.getElementsByTagName('admin'):
 		if admin_el.hasAttribute('jid'):
-			admins_jid.append(admin_el.getAttribute('jid'))
-	bot = Bot(bot_el.getAttribute('jid'), bot_el.getAttribute('password'), bot_el.getAttribute('nickname'), admins_jid=admins_jid, debug=debug)
+			admin = Admin()
+			admin.jid = admin_el.getAttribute('jid')
+			if admin_el.hasAttribute('say_level'):
+				admin.say_level = say_levels.get(admin_el.getAttribute('say_level'))
+			else:
+				admin.say_level = say_levels.warning
+			admins.append(admin)
+	
+	bot = Bot(bot_el.getAttribute('jid'), bot_el.getAttribute('password'), bot_el.getAttribute('nickname'), admins=admins, debug=debug)
 	bots.append(bot)
 	for bridge_el in bot_el.getElementsByTagName('bridge'):
 		xmpp_room = bridge_el.getElementsByTagName('xmpp-room')[0]
@@ -71,14 +81,14 @@ for bot_el in config.getElementsByTagNam
 			irc_charsets = None
 		
 		if bridge_el.hasAttribute('say_level'):
-			say_level = bridge_el.getAttribute('say_level')
+			say_level = say_levels.get(bridge_el.getAttribute('say_level'))
 		else:
-			say_level = 'all'
+			say_level = say_levels.nothing
 		
 		if bridge_el.hasAttribute('mode'):
 			mode = bridge_el.getAttribute('mode')
 		else:
-			mode = 'normal'
+			mode = 'bypass'
 		
 		bot.new_bridge(xmpp_room.getAttribute('jid'), irc.getAttribute('chan'), irc.getAttribute('server'), mode, say_level, irc_connection_interval=irc_connection_interval, irc_charsets=irc_charsets)
 
new file mode 100644
--- /dev/null
+++ b/weighted_string.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+class Weighted_string(unicode):
+	
+	def __new__(cls, weight, string):
+		return super(Weighted_string, cls).__new__(cls, unicode(string))
+	
+	def __init__(self, weight, string):
+		self.weight = weight
+	
+	def __lt__(self, other):
+		return self.weight < other
+	
+	def __le__(self, other):
+		return self.weight <= other
+	
+	def __eq__(self, other):
+		return self.weight == other
+	
+	def __ne__(self, other):
+		return self.weight != other
+	
+	def __gt__(self, other):
+		return self.weight > other
+	
+	def __ge__(self, other):
+		return self.weight >= other
+	
+	def __repr__(self):
+		return '<'+unicode(self.weight)+', '+unicode.__repr__(self)+'>'