diff --git a/python/mybot/BotInstance.py b/python/mybot/BotInstance.py
index b58df60..77503cf 100644
--- a/python/mybot/BotInstance.py
+++ b/python/mybot/BotInstance.py
@@ -1,51 +1,55 @@
 #!/bin/python
 
-# BotInstance
+# MyBot: BotInstance
 #
 # (c) 2006 Andreas Jaggi <andreas.jaggi@waterwave.ch>
 
-import re, os, copy, random
+import re, os, copy, random, datetime
 from IRCClient import IRCClient, LogAllMixin
 import BotManager
 
 class BotInstance(LogAllMixin, IRCClient):
-	def __init__(self, manager = None, server = "irc.freenode.net", port = 6667, nick = "saf02oitgawsg", channel = "#asdf", id = 0):
+	def __init__(self, manager = None, server = "irc.freenode.net", port = 6667, nick = "saf02oitgawsg", channel = "#asdf"):
 
 		print "Spawning bot for: %s:%s %s on %s" % (server, port, nick, channel)
 
 		if manager == None:
 				manager = BotManager.BotManager()
 		self.manager = manager
+		self.server = server
 		self.channel = channel
+		self.nickname = nick
+		self.port = port
 		self.observedChannels = []
+		self.availableChannels = []
+		self.nickre = re.compile(nick)
+
+		self.answer = "Um, Richard, I didn't see the question."
+		self.queries = []
+		self.results = {}
+		self.queriesre = {}
+
+		self.channelLimitReached = 0
+		self.nextInstance = None
 
 		IRCClient.__init__(self, server, port)
 		self.connect(nick, nick)
 		self.join(channel)
 
-		self.answer = "Um, Richard, I didn't see the question."
-		self.autojoin = 0
-		self.tojoin = []
-		self.next = None
-		self.id = id
-		self.nick = nick
-		self.port = port
-		self.joinct = 0
-		self.server = server
-		self.finished = 0
-		self.nickre = re.compile(nick)
-
-		self.availableChannels = []
+		self.start();
 
 	def requestChannelList(self):
 		self.send("LIST")
 
 	def join(self, channel, key = None):
 		if channel not in self.observedChannels:
-			IRCClient.join(self, channel, key)
+			if self.channelLimitReached:
+				self.nextInstance.join(channel, key)
+			else:
+				IRCClient.join(self, channel, key)
 
-			self.observedChannels.append(channel)
-			self.manager.observedChannelsUpdated(self)
+				self.observedChannels.append(channel)
+				self.manager.observedChannelsUpdated(self)
 
 	def handle_say(self, source, to, message):
 		#print "----(%s->%s): %s" % (source, to, message)
@@ -54,38 +58,31 @@
 
 		if message == "Shane":
 			self.say(self.channel, self.answer)
-		elif message == "AutoJoinEnable":
-			self.autojoin = 1
-			#self.say(self.channel, "Enabled AutoJoin")
-		elif message == "AutoJoinDisable":
-			self.autojoin = 0
-			#self.say(self.channel, "Disabled AutoJoin")
 		elif message == "PrintChannels":
 			for x in self.observedChannels:
 				self.say(self.channel, x)
 		elif message == "DoList":
 			self.send("LIST")
-			#self.say(self.channel, "Doing list")
-		elif message == "WorkOnJoin":
-			#self.say(self.channel, "Working on join")
-			self.WorkOnJoin()
-		elif message.split(' ')[0] == 'Inject':
-			newchannel = message.split(' ')[1]
-			#self.say(self.channel, "Injecting %s" % newchannel)
-			self.addChannel(newchannel)
-		elif message.split(' ')[0] == 'NNS':
-			self.server = message.split(' ')[1]
-			#self.say(self.channel, "NNS %s" % self.server)
-			self.spanNext()
+		elif message.split(' ')[0] == 'JoinChannel':
+			self.join(message.split(' ')[1])
 		elif self.nickre.match(message):
 			self.readFuzzyText(message)
-			if to != self.nick:
+			if to != self.nickname:
 				self.say(to, self.answer)
 			else:
 				self.say(sourcenick, self.answer)
 		else:
 			self.readFuzzyText(message)
 
+		for query in self.queries:
+			queryre = self.queriesre.setdefault(query, re.compile(query))
+
+			if queryre.match(message) or queryre.match(source) or queryre.match(to):
+				if not self.results.has_key(query):
+					self.results[query] = []
+				self.results[query].append((datetime.date.today().ctime(), self.server, self.channel, source, to, message))
+				self.manager.resultsUpdated(self)
+
 	def readFuzzyText(self, message):
 		if len(message) > 0:
 			newtokens = set(message.split(" "))
@@ -96,28 +93,6 @@
 			b = random.randint(a, len(nexttokens)-1)
 			self.answer = ' '.join(nexttokens[a:b])
 
-	def addChannel(self, channel):
-		if channel not in self.tojoin:
-			if channel not in self.observedChannels:
-				self.tojoin.append(channel)
-				random.shuffle(self.tojoin)
-
-	def WorkOnJoin(self):
-		if self.tojoin and self.autojoin and not self.finished:
-			for x in range(0, min(5, len(self.tojoin))):
-				channel = self.tojoin.pop()
-				#self.say(self.channel, "Joining %s" % channel)
-				self.join(channel)
-			#self.say(self.channel, "%s remaining" % len(self.tojoin))
-
-	def handle_incoming(self, line):
-		LogAllMixin.handle_incoming(self, line)
-
-		self.joinct = self.joinct+1
-		if self.joinct > 200:
-			self.joinct = 0
-			self.WorkOnJoin()
-
 	def handle_reply(self, prefix, code, params):
 		print "reply: %s" % code
 		if code == 322:
@@ -125,21 +100,26 @@
 			newchannel = params.split(' ')[1]
 			if newchannel not in self.availableChannels:
 				self.availableChannels.append(newchannel)
-				self.manager.availableChannelsUpdated(self)
 
-			self.addChannel(newchannel)
+		if code == 323:
+			# end of LIST command
+			self.availableChannels.sort()
+			self.observedChannels.sort()
+			self.manager.availableChannelsUpdated(self)
+
 		if code == 405:
-			self.spanNext()
-			self.finished = 1
+			# "You can't join that many channels"
+			channelname = params.split(' ')[1]
 
-	def spanNext(self):
-		newid = random.randint(int(self.id), int(self.id)**2+10)
-		self.next = BotInstance(self.manager, self.server, self.port, "%s%s%s" % (self.nick, self.id, newid), self.channel, "%s%s" % (self.id, newid))
-		self.autojoin = 0
-		self.next.autojoin = 1
-		self.next.tojoin = copy.deepcopy(self.tojoin)
-		self.tojoin = []
-		self.next.start()
+			print "recieved channel: %s" % channelname
+
+			if not self.channelLimitReached:
+				self.channelLimitReached = 1
+
+				newnick = self.manager.genNick()
+				self.nextInstance = BotInstance(self.manager, self.server, self.port, newnick, self.channel)
+
+			self.nextInstance.join(channelname)
 
 	def handle_notice(self, source, to, message):
 		print "notice: (%s->%s): %s" % (source, to, message)
@@ -149,5 +129,4 @@
 
 if __name__ == "__main__":
 	bot = BotInstance()
-	bot.start()
 	bot.tjoin()
diff --git a/python/mybot/BotManager.py b/python/mybot/BotManager.py
index b408341..6cdd868 100644
--- a/python/mybot/BotManager.py
+++ b/python/mybot/BotManager.py
@@ -1,33 +1,138 @@
 #!/bin/python
 
-# BotManager
+# MyBot: BotManager
 #
 # (c) 2006 Andreas Jaggi <andreas.jaggi@waterwave.ch>
 
+import BotInstance
+
 class BotManager:
 	def __init__(self):
-		self.botInstances = []
+		self.botInstances = {}
 		self.availableChannels = {}
 		self.observedChannels = {}
+		self.comChannels = {}
+		self.results = {}
+
+		self.nickCounter = 0
+		self.channelCounter = 0
+
+		self.availableChannelsListener = []
+		self.observedChannelsListener = []
+		self.resultsListener = []
 
 	def updateAvailableChannels(self):
-		for b in self.botInstances:
+		"""Request update of available channels list"""
+		for b in self.botInstances.itervalues():
 			b.requestChannelList()
 	
+	def addServer(self, server, port = 6667):
+		"""Add a new server (e.g. create a new bot for the server)"""
+		if server not in self.observedChannels.keys():
+			channel = self.genComChannelName()
+			nick = self.genNick()
+			botInstance = BotInstance.BotInstance(self, server, port, nick, channel)
+			self.botInstances[server] = botInstance
+			self.comChannels[server] = channel
+
+			botInstance.requestChannelList()
+
+	def observeChannel(self, server, channel):
+		botInstance = self.botInstances.get(server, None)
+		if botInstance:
+			botInstance.join(channel)
+
+	def addSearchQuery(self, query, server=None):
+		self.results[query] = []
+
+		if not server:
+			for b in self.botInstances.itervalues():
+				b.queries.append(query)
+		else:
+			botInstance = self.botInstances.get(server, None)
+			if botInstance:
+				botInstance.append(query)
+
+	def getSearchQueries(self, server=None):
+		queries = set([])
+		if not server:
+			for b in self.botInstances.itervalues():
+				queries.union(set(b.queries))
+		else:
+			botInstance = self.botInstances.get(server, None)
+			if botInstance:
+				queries = set(botInstance.queries)
+		return list(queries)
+
+	def getSearchResults(self, server=None):
+		results = set([])
+		if not server:
+			for b in self.botInstances.itervalues():
+				results.union(set(b.results))
+		else:
+			botInstance = self.botInstances.get(server, None)
+			if botInstance:
+				results = set(botInstance.results)
+		return list(results)
+
+	def genComChannelName(self):
+		"""Generate a random and unique channel name"""
+		self.channelCounter += 1
+		return '#mbcc-%s' % self.channelCounter
+
+	def genNick(self):
+		"""Generate a random and unique nick"""
+		self.nickCounter += 1
+		return 'mybot-%s' % self.nickCounter
+
+	# API for listener
+
+	def addAvailableChannelsListener(self, listener):
+		self.availableChannelsListener.append(listener)
+
+	def removeAvailalbeChannelsListener(self, listener):
+		self.availableChannelsListener.remove(listener)
+	
+	def addObservedChannelsListener(self, listener):
+		self.observedChannelsListener.append(listener)
+
+	def removeObservedChannelsListener(self, listener):
+		self.observedChannelsListener.remove(listener)
+	
+	def addResultsListener(self, listener):
+		self.resultsListener.append(listener)
+
+	def removeResultsListener(self, listener):
+		self.resultsListener.remove(listener)
+	
+	# API for BotInstances
+
 	def availableChannelsUpdated(self, botInstance):
-		serverChannels = self.availableChannels[botInstance.server]
+		serverChannels = self.availableChannels.get(botInstance.server, [])
 		newChannels = set(serverChannels).union(set(botInstance.availableChannels))
 		self.availableChannels[botInstance.server] = list(newChannels)
 
+		for listener in self.availableChannelsListener:
+			listener()
+
 	def observedChannelsUpdated(self, botInstance):
-		serverChannels = self.observedChannels[botInstance.server]
+		serverChannels = self.observedChannels.get(botInstance.server, [])
 		newChannels = set(serverChannels).union(set(botInstance.observedChannels))
 		self.observedChannels[botInstance.server] = list(newChannels)
 
-	def addServer(self, server, port = 6667):
-		if server not in self.observedChannels.keys():
-			#b = BotInstance(self, server, port, # TODO)
-			pass
-	
+		print "BotManager:observedChannelsUpdated"
+		for listener in self.observedChannelsListener:
+			listener()
+
+	def resultsUpdated(self, botInstance):
+		for query in botInstance.queries:
+			for result in botInstance.results.get(query, []):
+				if not self.results.has_key(query):
+					self.results[query] = []
+				self.results[query].append(result)
+
+		for listener in self.resultsListener:
+			listener()
+
 if __name__ == "__main__":
 	botm = BotManager()
diff --git a/python/mybot/IRCClient.py b/python/mybot/IRCClient.py
index bd26db3..665e068 100644
--- a/python/mybot/IRCClient.py
+++ b/python/mybot/IRCClient.py
@@ -107,7 +107,7 @@
 
     def mainloop(self):
         self.start()
-        sefl.join()
+        self.tjoin()
 
     def run(self):
         while 1:
diff --git a/python/mybot/MyBot.py b/python/mybot/MyBot.py
new file mode 100644
index 0000000..9700c4e
--- /dev/null
+++ b/python/mybot/MyBot.py
@@ -0,0 +1,245 @@
+#!/bin/python
+
+# MyBot
+#
+# (c) 2006 Andreas Jaggi <andreas.jaggi@waterwave.ch>
+
+import wx, sys
+
+import BotManager
+
+class ServerList(wx.Panel):
+	def __init__(self, parent, id, botManager):
+		wx.Panel.__init__(self, parent, id)
+
+		self.botManager = botManager
+
+		sizer = wx.BoxSizer(wx.VERTICAL)
+
+		self.tree = wx.TreeCtrl(self, style=wx.TR_HIDE_ROOT)
+		sizer.Add(self.tree, 1, flag=wx.EXPAND)
+		self.rootId = self.tree.AddRoot("Servers")
+
+		myId = wx.NewId()
+		self.serverInput = wx.TextCtrl(self, myId, "irc.freenode.net", style=wx.TE_PROCESS_ENTER)
+		sizer.Add(self.serverInput, flag=wx.EXPAND)
+		#self.Bind(wx.EVT_TEXT_ENTER, self.onAddServer, self.serverInput)
+		wx.EVT_TEXT_ENTER(self.serverInput, myId, self.onAddServer)
+
+		myId = wx.NewId()
+		self.addServerButton = wx.Button(self, myId, "Add Server")
+		sizer.Add(self.addServerButton, flag=wx.EXPAND)
+		#self.Bind(wx.EVT_BUTTON, self.onAddServer, self.addServerButton)
+		wx.EVT_BUTTON(self.addServerButton, myId, self.onAddServer)
+
+		myId = wx.NewId()
+		self.observeChannelButton = wx.Button(self, myId, "Observe Channel")
+		sizer.Add(self.observeChannelButton, flag=wx.EXPAND)
+		#self.Bind(wx.EVT_BUTTON, self.onObserveChannel, self.observeChannelButton)
+		wx.EVT_BUTTON(self.observeChannelButton, myId, self.onObserveChannel)
+
+		myId = wx.NewId()
+		self.refreshButton = wx.Button(self, myId, "Refresh Channel List")
+		sizer.Add(self.refreshButton, flag=wx.EXPAND)
+		#self.Bind(wx.EVT_BUTTON, self.onObserveChannel, self.observeChannelButton)
+		wx.EVT_BUTTON(self.refreshButton, myId, self.onRefreshChannelList)
+
+		self.SetSizer(sizer)
+		self.Fit()
+
+	def onRefreshChannelList(self, event):
+		print "ServerList:RefreshChannelList"
+		self.botManager.updateAvailableChannels()
+
+	def onObserveChannel(self, event):
+		print "ServerList:onObserveChannel"
+		channelId = self.tree.GetSelection()
+		channel = self.tree.GetItemText(channelId)
+
+		found = False
+		for serverId in self.getChildrenIds(self.tree, self.rootId):
+			if found:
+				break
+			if self.tree.ItemHasChildren(serverId):
+				for childId in self.getChildrenIds(self.tree, serverId):
+					if childId == channelId:
+						server = self.tree.GetItemText(serverId)
+						found = True
+						break
+
+		self.botManager.observeChannel(server, channel)
+
+	def onAddServer(self, event):
+		print "ServerList:onAddServer"
+		server = self.serverInput.GetValue()
+		self.serverInput.Clear()
+
+		self.botManager.addServer(server)
+
+	def onUpdateAvailableChannels(self):
+		print "AvailableChannels updated"
+		expanded = self.tree.IsExpanded(self.rootId)
+		self.tree.CollapseAndReset(self.rootId)
+		for server in self.botManager.availableChannels.iterkeys():
+			nserver = self.tree.AppendItem(self.rootId, server)
+		if expanded:
+			self.tree.Expand(self.rootId)
+		else:
+			self.tree.Collapse(self.rootId)
+
+		self.tree.SortChildren(self.rootId)
+
+		for serverId in self.getChildrenIds(self.tree, self.rootId):
+			server = self.tree.GetItemText(serverId)
+			if self.botManager.availableChannels.has_key(server):
+				expanded = self.tree.IsExpanded(serverId)
+				self.tree.CollapseAndReset(serverId)
+
+				hasObservedChannels = False
+
+				for channel in self.botManager.availableChannels.get(server, []):
+					nitem = self.tree.AppendItem(serverId, channel)
+					if channel in self.botManager.observedChannels.get(server, []):
+						self.tree.SetItemBold(nitem, 1)
+						hasObservedChannels = True
+				if expanded:
+					self.tree.Expand(serverId)
+				else:
+					self.tree.Collapse(serverId)
+
+				if hasObservedChannels:
+					self.tree.SetItemBold(serverId, 1)
+				else:
+					self.tree.SetItemBold(serverId, 0)
+
+				self.tree.SortChildren(serverId)
+			else:
+				self.tree.Delete(serverId)
+
+	def onUpdateObservedChannels(self):
+		print "ObservedChannels updated"
+		for serverId in self.getChildrenIds(self.tree, self.tree.GetRootItem()):
+			server = self.tree.GetItemText(serverId)
+
+			if not self.botManager.observedChannels.has_key(server):
+				noserver = True
+				self.tree.SetItemBold(serverId, 0)
+			else:
+				noserver = False
+				self.tree.SetItemBold(serverId, 1)
+
+			for channelId in self.getChildrenIds(self.tree, serverId):
+				if noserver:
+					self.tree.SetItemBold(channelId, 0)
+				else:
+					channel = self.tree.GetItemText(channelId)
+					if channel in self.botManager.observedChannels.get(server, []):
+						self.tree.SetItemBold(channelId, 1)
+					else:
+						self.tree.SetItemBold(channelId, 0)
+
+	def getChildrenIds(self, tree, parent):
+		result = []
+		item = tree.GetLastChild(parent)
+		while item:
+			result.append(item)
+			item = tree.GetPrevSibling(item)
+		result.reverse()
+		return result
+
+
+class QueryPanel(wx.SplitterWindow):
+	def __init__(self, parent, id, botManager):
+		wx.SplitterWindow.__init__(self, parent, id)
+
+		self.botManager = botManager
+
+		sizer = wx.BoxSizer(wx.VERTICAL)
+		self.topPanel = wx.Panel(self, -1)
+		self.textLog = wx.TextCtrl(self, -1, style=wx.TE_MULTILINE|wx.MODERN|wx.TE_READONLY)
+
+		myId = wx.NewId()
+		self.queryList = wx.ListCtrl(self.topPanel, myId, style=wx.LC_REPORT)
+		wx.EVT_LIST_ITEM_ACTIVATED(self.queryList, myId, self.onSelectQuery)
+		sizer.Add(self.queryList, 1, flag=wx.EXPAND)
+		
+		self.queryList.InsertColumn(0, "Query")
+		self.queryList.InsertColumn(1, "Results")
+
+		self.queryInput = wx.TextCtrl(self.topPanel, -1, "x-way")
+		sizer.Add(self.queryInput, flag=wx.EXPAND)
+
+		myId = wx.NewId()
+		self.queryButton = wx.Button(self.topPanel, myId, "Add Query")
+		sizer.Add(self.queryButton, flag=wx.EXPAND)
+		#self.Bind(wx.EVT_BUTTON, self.onAddServer, self.addServerButton)
+		wx.EVT_BUTTON(self.queryButton, myId, self.onAddQuery)
+
+		self.topPanel.SetSizer(sizer)
+		self.topPanel.Fit()
+		self.Fit()
+
+		self.SplitHorizontally(self.topPanel, self.textLog)
+
+		self.itemDataMap = {}
+
+		self.selectedQuery = None
+
+	def onSelectQuery(self, event):
+		row = event.GetIndex()
+		for (query, index) in self.itemDataMap.iteritems():
+			if index == row:
+				self.selectedQuery = query
+				break
+		self.onUpdateResults()
+
+	def onAddQuery(self, event):
+		print "Add query"
+		self.botManager.addSearchQuery(self.queryInput.GetValue())
+		self.queryInput.Clear()
+		self.onUpdateResults()
+
+	def onUpdateResults(self):
+		print "Results updated"
+
+		if len(self.itemDataMap):
+			for query in self.itemDataMap.iterkeys():
+				self.queryList.SetStringItem(self.itemDataMap[query], 1, "%s" % len(self.botManager.results.get(query, [])))
+
+		for query in self.botManager.results.iterkeys():
+			if query not in self.itemDataMap.iterkeys():
+				row = self.queryList.GetItemCount()
+				self.itemDataMap[query] = row
+				self.queryList.InsertStringItem(row, query)
+				self.queryList.SetStringItem(row, 1, "%s" % len(self.botManager.results.get(query, [])))
+
+		if self.selectedQuery:
+			self.textLog.Clear()
+			results = self.botManager.results.get(self.selectedQuery, [])
+			for line in results:
+				self.textLog.AppendText("%s\n" % " ".join(line))
+
+
+
+
+class MyBot(wx.Frame):
+	def __init__(self, parent, id):
+		wx.Frame.__init__(self, parent, id, 'MyBot')
+
+		self.botManager = BotManager.BotManager()
+
+		self.verticalSplitter = wx.SplitterWindow(self, -1)
+		self.serverList = ServerList(self.verticalSplitter, -1, self.botManager)
+		self.queryPanel = QueryPanel(self.verticalSplitter, -1, self.botManager)
+
+		self.verticalSplitter.SplitVertically(self.serverList, self.queryPanel, 150)
+
+		self.botManager.addAvailableChannelsListener(self.serverList.onUpdateAvailableChannels)
+		self.botManager.addObservedChannelsListener(self.serverList.onUpdateObservedChannels)
+		self.botManager.addResultsListener(self.queryPanel.onUpdateResults)
+
+if __name__ == "__main__":
+	app = wx.PySimpleApp()
+	mb = MyBot(parent=None, id=-1)
+	mb.Show(True)
+	app.MainLoop()