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 -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 +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 + +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()