diff --git a/python/mybot/BotInstance.py b/python/mybot/BotInstance.py new file mode 100644 index 0000000..b58df60 --- /dev/null +++ b/python/mybot/BotInstance.py @@ -0,0 +1,153 @@ +#!/bin/python + +# BotInstance +# +# (c) 2006 Andreas Jaggi + +import re, os, copy, random +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): + + print "Spawning bot for: %s:%s %s on %s" % (server, port, nick, channel) + + if manager == None: + manager = BotManager.BotManager() + self.manager = manager + self.channel = channel + self.observedChannels = [] + + 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 = [] + + def requestChannelList(self): + self.send("LIST") + + def join(self, channel, key = None): + if channel not in self.observedChannels: + IRCClient.join(self, channel, key) + + self.observedChannels.append(channel) + self.manager.observedChannelsUpdated(self) + + def handle_say(self, source, to, message): + #print "----(%s->%s): %s" % (source, to, message) + + sourcenick = source.split("!")[0] + + 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 self.nickre.match(message): + self.readFuzzyText(message) + if to != self.nick: + self.say(to, self.answer) + else: + self.say(sourcenick, self.answer) + else: + self.readFuzzyText(message) + + def readFuzzyText(self, message): + if len(message) > 0: + newtokens = set(message.split(" ")) + oldtokens = set(self.answer.split(" ")) + nexttokens = list(newtokens.union(oldtokens)) + random.shuffle(nexttokens) + a = random.randint(0, (len(nexttokens)-1)/2) + 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: + # reply to a LIST command + newchannel = params.split(' ')[1] + if newchannel not in self.availableChannels: + self.availableChannels.append(newchannel) + self.manager.availableChannelsUpdated(self) + + self.addChannel(newchannel) + if code == 405: + self.spanNext() + self.finished = 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() + + def handle_notice(self, source, to, message): + print "notice: (%s->%s): %s" % (source, to, message) + + def handle_command(self, prefix, command, params): + print "command: %s" % command + +if __name__ == "__main__": + bot = BotInstance() + bot.start() + bot.tjoin() diff --git a/python/mybot/BotManager.py b/python/mybot/BotManager.py new file mode 100644 index 0000000..b408341 --- /dev/null +++ b/python/mybot/BotManager.py @@ -0,0 +1,33 @@ +#!/bin/python + +# BotManager +# +# (c) 2006 Andreas Jaggi + +class BotManager: + def __init__(self): + self.botInstances = [] + self.availableChannels = {} + self.observedChannels = {} + + def updateAvailableChannels(self): + for b in self.botInstances: + b.requestChannelList() + + def availableChannelsUpdated(self, botInstance): + serverChannels = self.availableChannels[botInstance.server] + newChannels = set(serverChannels).union(set(botInstance.availableChannels)) + self.availableChannels[botInstance.server] = list(newChannels) + + def observedChannelsUpdated(self, botInstance): + serverChannels = self.observedChannels[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 + +if __name__ == "__main__": + botm = BotManager() diff --git a/python/mybot/IRCClient.py b/python/mybot/IRCClient.py new file mode 100644 index 0000000..bd26db3 --- /dev/null +++ b/python/mybot/IRCClient.py @@ -0,0 +1,185 @@ +#!/bin/python +# IRC client class. vim:et:ts=4 +# Adam Sampson + +import sys, socket +from threading import Thread + +def getnick(s): + n = s.find("!") + if n > -1: + return s[:n] + else: + return s + +def ircsplit(s, num): + """Split a string of the form word1 word2 .. wordN :string.""" + l = s.split(" ", num) + if len(l) < (num + 1): + l.append(None) + elif l[num][0] == ":": + l[num] = l[num][1:] + return l + +class IRCClient(Thread): + """A simple IRC client class. Subclass this to implement your own + IRC applications.""" + + def __init__(self, server, port): + Thread.__init__(self) + """Open a connection to an IRC server.""" + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.server = server + self.socket.connect((self.server, port)) + self.fd = self.socket.makefile("r") + self.open = 1 + self.my_nick = None + + def __del__(self): + """Close the connection.""" + if self.open: self.quit() + + def send(self, string): + """Send a string to the server.""" + self.handle_outgoing(string) + self.socket.send(string + "\n") + + def recv(self): + """Read a line from the server. Return None on EOF.""" + line = self.fd.readline() + if line == "": + return None + else: + s = line.strip() + self.handle_incoming(s) + return s + + def connect(self, nick, realname = "IRCClient", password = None): + """Log in to the server.""" + if password: self.send("PASS :" + password) + self.nick(nick) + self.send("USER " + nick + " 0 * :" + realname) + + def nick(self, nick): + self.send("NICK " + nick) + self.my_nick = nick + + def kick(self, channel, nick, reason): + if reason is None: + self.send("KICK " + channel + " " + nick) + else: + self.send("KICK " + channel + " " + nick + " :" + reason) + + def topic(self, channel, topic): + self.send("TOPIC " + channel + " :" + topic) + + def quit(self, reason = "Quit"): + """Quit from the server.""" + self.send("QUIT :" + reason) + self.socket.close() + self.open = 0 + + def tjoin(self): + Thread.join(self) + + def join(self, channel, key = None): + """Join a channel.""" + if key is None: + self.send("JOIN " + channel) + else: + self.send("JOIN " + channel + " " + key) + + def usermode(self, channel, user, mode): + """Change a user's modes on a channel.""" + self.send("MODE " + channel + " " + mode + " " + user) + + def op(self, channel, user): self.usermode(channel, user, "+o") + def deop(self, channel, user): self.usermode(channel, user, "-o") + def voice(self, channel, user): self.usermode(channel, user, "+v") + def devoice(self, channel, user): self.usermode(channel, user, "-v") + + def say(self, to, text): + """Send a message to a user or channel.""" + self.send("PRIVMSG " + to + " :" + text) + + def notice(self, to, text): + self.send("NOTICE " + to + " :" + text) + + def mainloop(self): + self.start() + sefl.join() + + def run(self): + while 1: + line = self.recv() + if not line: break + + prefix = None + if line[0] == ":" and len(line) > 1: (prefix, line) = line[1:].split(" ", 1) + (command, params) = line.split(" ", 1) + + if command[0] >= '0' and command[0] <= '9': + # Server reply + code = int(command) + self.handle_reply(prefix, code, params) + elif command == "PING": + self.send("PONG " + params) + elif command == "NICK": + l = ircsplit(params, 0) + self.handle_nick(prefix, l[0]) + elif command == "MODE": + self.handle_mode(prefix, params) + elif command == "QUIT": + l = ircsplit(params, 0) + self.handle_quit(prefix, l[0]) + elif command == "JOIN": + l = ircsplit(params, 0) + self.handle_join(prefix, l[0]) + elif command == "PART": + l = ircsplit(params, 1) + self.handle_part(prefix, l[0], l[1]) + elif command == "TOPIC": + l = ircsplit(params, 1) + self.handle_topic(prefix, l[0], l[1]) + elif command == "INVITE": + l = ircsplit(params, 1) + self.handle_invite(prefix, l[0], l[1]) + elif command == "KICK": + l = ircsplit(params, 2) + self.handle_kick(prefix, l[0], l[1], l[2]) + elif command == "PRIVMSG": + l = ircsplit(params, 1) + self.handle_say(prefix, l[0], l[1]) + elif command == "NOTICE": + l = ircsplit(params, 1) + self.handle_notice(prefix, l[0], l[1]) + else: + self.handle_command(prefix, command, params) + + def getnick(self, s): + return split(s, "!", 1)[0] + + def get_nick(self): return self.my_nick + + def handle_incoming(self, line): pass + def handle_outgoing(self, line): pass + def handle_nick(self, old, new): pass + def handle_mode(self, nick, modes): pass + def handle_quit(self, nick, reason): pass + def handle_join(self, nick, channel): pass + def handle_part(self, nick, channel, reason): pass + def handle_topic(self, nick, channel, topic): pass + def handle_invite(self, source, to, channel): pass + def handle_kick(self, source, channel, to, reason): pass + def handle_say(self, source, to, message): pass + def handle_notice(self, source, to, message): pass + def handle_unknown(self, prefix, command, params): pass + def handle_reply(self, prefix, code, params): pass + +class LogAllMixin: + def handle_incoming(self, line): + print >>sys.stderr, "< " + line + + def handle_outgoing(self, line): + print >>sys.stderr, "> " + line +