Public feed
#599001
#! /usr/bin/python2.5# Kilgame server as an IRC client.# Based on ircbot.py by bsmntbombdood. http://abacus.kwzs.be/~bsmnt_bot/import socketimport reimport sysimport tracebackimport threadimport pprintimport inspectimport timeimport osimport threadingimport copyimport pickleimport marshalimport typesimport mathfrom cStringIO import StringIOclass StopHandlingCallbacks: passclass QuitIRC: passclass RemoveCallback: passclass IRCbot: def __init__(self, host, nick, ident, owner, realname="", port=6667, chan=None, verbose=True, exec_chans=[], command_char="#", threading=True, error_in_chan=True, ban_file="/bans"): self.host = host self.port = port self.nick = nick self.ident= ident self.realname = realname self.chan = chan self.verbose = verbose self.owner = owner self.error_in_chan = error_in_chan self.errorchan = "#%s_errors" % self.nick self.connected = False; self.raw_regex_queue = []; self.ban_file = ban_file self.banlist = self.read_bans() self.COMMAND_CHAR = command_char self.THREADING = threading self.commands_running = [] self.commands_running_lock = thread.allocate_lock() self.gameInited = False self.gameStarted = False self.gamePlayerDict = {} self.locals = {"self": self} self.message_re = re.compile(r"^:(?P<nick>\S+)!(?P<ident>\S+)@(?P<host>\S+) (?P<command>\S+) (?P<channel>\S+) :(?P<message>.*)") #default callbacks: self.register_raw("^PING (.*)$", self.pong) self.register_raw( r"^:%s PRIVMSG \S* :\+%squit ?(.*)" % (self.owner, self.COMMAND_CHAR), self.do_quit) self.register_raw( r"^:%s PRIVMSG \S* :\+%sraw (.*)" % (self.owner, self.COMMAND_CHAR), self.do_raw) self.register_raw( r"^\S+ PRIVMSG \S+ :.%sctcp (\S+) (.+)" % self.COMMAND_CHAR, self.do_ctcp) self.register_raw( r"^:%s PRIVMSG (\S*) :\+%spexec (.*)" % (self.owner, self.COMMAND_CHAR), self.do_exec) self.register_raw( r"\S+ PRIVMSG (%s) :.%sexec (.*)" % ("|".join(exec_chans+[self.errorchan]), self.COMMAND_CHAR), self.do_exec) self.register_raw( r"\S+ PRIVMSG \S+ :.%sps" % self.COMMAND_CHAR, self.do_ps) self.register_raw( r"^\S+ PRIVMSG (%s) :.%skill (.*)" % ("|".join(exec_chans+[self.errorchan]), self.COMMAND_CHAR), self.do_kill) self.register_raw( r"^\S+ PRIVMSG %s :.%sinit" % (self.chan, self.COMMAND_CHAR), self.do_init) self.register_raw( r"^(\S+)!\S*@\S* PRIVMSG %s :(.)%sjoin" % (self.chan, self.COMMAND_CHAR), self.do_join) self.register_raw( r"^\S+ PRIVMSG %s :.%sstart" % (self.chan, self.COMMAND_CHAR), self.do_start) self.register_raw( r"^(\S+)!\S*@\S* PRIVMSG %s :(.)%sstatus" % (self.chan, self.COMMAND_CHAR), self.do_status) self.register_raw( r"^(\S+)!\S*@\S* PRIVMSG %s :(.)%sshoot (\S{1,16})" % (self.chan, self.COMMAND_CHAR), self.do_shoot) self.register_raw( r"^(\S+)!\S*@\S* PRIVMSG %s :(.)%sfriend (\S{1,16})" % (self.chan, self.COMMAND_CHAR), self.do_friend) self.register_raw( r"^\S+ PRIVMSG %s :.%send" % (self.chan, self.COMMAND_CHAR), self.do_end) self.register_raw(r"^ERROR :Closing Link:.*", lambda x : True) def raw(self, message): message += "\r\n" x = self.socket.send(message) while x < len(message): message = message[x:] x = self.socket.send(message) def connect(self): self.socket = socket.socket(); self.socket.connect((self.host, self.port)) self.raw("NICK %s" % self.nick) self.raw("USER %s %s bla :%s" % (self.ident, self.host, self.realname)) self.raw("CAPAB :IDENTIFY-MSG") if isinstance(self.chan, list): for i in self.chan: self.raw("JOIN :%s" % i) else: self.raw("JOIN :%s" % self.chan) self.raw("JOIN :%s" % self.errorchan) self.sockfile = self.socket.makefile("rw") self.connected = True; def listen(self): if not self.connected: self.connect() while self.connected: line = self.sockfile.readline() line = line.strip() if not line: continue if self.verbose: sys.__stdout__.write(line + "\n") if hasattr(self, "onmsg"): if self.onmsg(message): self.connected = False break; self.do_callbacks(line) def disconnect(self, message): self.raw("QUIT :%s" % message) self.socket.close() self.sockfile.close() self.connected = False return True def register_raw(self, regex, callback): if isinstance(regex, str): self.raw_regex_queue.append((re.compile(regex), callback)) else: self.raw_regex_queue.append((regex, callback)) def do_callbacks(self, message): # each entry in self.raw_regex_queue is (regex, callback) for ban in self.banlist: if ban.match(message): print "%s is banned by %r" % (message, ban.pattern) return for i in self.raw_regex_queue: if not self.connected: break m = i[0].match(message) if m: try: self.exec_execer("self.handle_callback(message, m, i)", (globals(), locals())) except StopHandlingCallbacks: break except RemoveCallback: self.raw_regex_queue.remove(i) except QuitIRC: self.disconnect("Disconnected.") return def handle_callback(self, message, m, i): try: retval = i[1](message, m) except TypeError: try: retval = i[1](message) except TypeError: self.raw("PRIVMSG %s :TypeError for callback %s, %r. Removing." % (self.errorchan, i[1].__name__, i[0].pattern)) self.raw_regex_queue.remove(i) return if retval: self.disconnect("Disconnected.") return def pong(self, message, r): self.raw("PONG %s" % r.group(1)) def do_quit(self, message, r): self.disconnect(r.group(1)) return True def do_raw(self, message, r): self.raw(r.group(1)) def do_ctcp(self, message, r): self.raw("PRIVMSG %s :\x01%s\x01" % (r.group(1), r.group(2))) def exec_execer(self, command, env): info = (command, time.time(), env, threading.Event()) self.commands_running_lock.acquire() self.commands_running.append(info) self.commands_running_lock.release() thread_info.info = info sys.settrace(exec_global_tracer) try: exec command in env[0], env[1] except SystemExit: pass except Exception: stuff = traceback.format_exc() if self.error_in_chan: env[0]["sys"].stdout.write(stuff.rstrip().split("\n")[-1]) env[0]["sys"].stderr.write(stuff) sys.settrace(None) self.commands_running_lock.acquire() self.commands_running.remove(info) self.commands_running_lock.release() def do_exec(self, message, r): command = r.group(2) # what to expose in exec env = (globals(), locals()) env[0]["sys"] = SysWrapper( stdout = IRCFileWrapper(self.raw, r.group(1)), stderr = IRCFileWrapper(self.raw, self.errorchan)) if self.THREADING: thread.start_new_thread(self.exec_execer, (command, env)) else: self.exec_execer(command, env) def get_message(self, line): return self.message_re.match(line).group("message") def print_callbacks(self, fd = sys.stdout): pprint.pprint( [(i[0].pattern, i[1].__name__) for i in self.raw_regex_queue], fd) def do_ps(self, message, r): channel = self.message_re.match(message).group("channel") for i in range(len(self.commands_running)): info = self.commands_running[i] if info[3].isSet(): killed = ", killed" else: killed = "" self.raw("PRIVMSG %s :%s: %r, %.2f seconds%s" % (channel, i, info[0], time.time() - info[1], killed)) if not self.commands_running and self.verbose: self.raw("PRIVMSG %s :None" % channel) def do_kill(self, message, r): channel = r.group(1) try: pid = int(r.group(2)) except ValueError: self.raw("PRIVMSG %s :Argument to kill must be a number" % channel) return self.commands_running_lock.acquire() try: self.commands_running[pid][3].set() except IndexError: self.commands_running_lock.release() self.raw("PRIVMSG %s :Proccess %s does not exist" % (channel, r.group(1))) return self.commands_running_lock.release() def do_init(self, message, r): if self.gameInited: sys.stdout("The game has already been initialized. Say #join to join; #start to start; #end to abort the game.") else: self.gameInited = True sys.stdout("Game initialized. Say #join to join; #start to start; #end to abort the game.") def do_join(self, message, r): if r.group(2) == "-": sys.stdout("%s, please identify to NickServ first." % r.group(1)) elif r.group(1) in self.gamePlayerDict: sys.stdout("%s, you are already in the game. Say #end to abort the game." % r.group(1)) elif not self.gameInited: sys.stdout("%s, the game has not yet been initialized. Say #init to initialize the game." % r.group(1)) elif self.gameStarted: sys.stdout("%s, the game has already started. Say #status to view status; #end to abort the game." % r.group(1)) else: self.gamePlayerDict[r.group(1)] = 2, datetime.min, [] sys.stdout("%s, you have joined the game. Say #start to start; #end to abort the game." % r.group(1)) def do_start(self, message, r): if not self.gameInited: sys.stdout("The game has not yet been initialized. Say #init to initialize the game.") elif self.gameStarted: sys.stdout("The game has already started. Say #status to view status; #end to abort the game.") elif len(self.gamePlayerDict) < 2: sys.stdout("The game doesn't yet have enough players. Say #join to join; #end to abort the game.") else: self.gameStarted = True sys.stdout("Game started. Say #status to view status; #end to abort the game.") def listInit(self, l): return reduce(lambda x, y: x + ", " + y, l[:-1]) def do_status(self, message, r): if not self.gameInited: sys.stdout("%s, the game has not yet been initialized. Say #init to initialize the game." % r.group(1)) if not self.gameStarted: if self.gamePlayerDict == []: sys.stdout("%s, currently there are no players. Say #join to join." % r.group(1)) elif len(self.gamePlayerDict) == 1: sys.stdout("%s, currently the only player is %s. Say #join to join." % (r.group(1), self.gamePlayerDict.keys()[0])) else: sys.stdout("%s, the current players are %s and %s. Say #join to join; #start to start." % (r.group(1), listInit(playerList), playerList[-1])) else: playerList = [] for name, (lives, nextShootTime, noShootList) in self.gamePlayerDict.iteritems(): timeToShoot = 0 if nextShootTime < datetime.now() else (nextShootTime - datetime.now()).seconds playerList.append("%s (%s lives, must wait %s seconds before shooting)" % (name, lives, timeToShoot)) sys.stdout("%s, the current players are %s and %s." % (r.group(1), self.listInit(playerList), playerList[-1])) noShootList = self.gamePlayerDict[r.group(1)][2] if noShootList == []: sys.stdout("%s, you are currently able to shoot anybody." % r.group(1)) elif len(noShootList) == 1: sys.stdout("%s, you are unable to shoot %s." % (r.group(1), noShootList[0])) sys.stdout("%s, you are unable to shoot %s and %s." % (r.group(1) + self.listInit(noShootList), noShootList[-1])) sys.stdout("%s, say #shoot to shoot a player; #friend to promise to not shoot a player; #end to abort the game." % r.group(1)) def do_shoot(self, message, r): sys.stdout("Not yet implemented.") def do_friend(self, message, r): sys.stdout("Not yet implemented.") def do_end(self, message, r): sys.stdout("Not yet implemented.") def save_callbacks(self, f): if not hasattr(f, "write"): f = open(f, "wb") data = [(i[0], marshal.dumps(i[1].func_code), inspect.ismethod(i[1])) for i in self.raw_regex_queue] pickle.dump(data, f) def load_callbacks(self, f): if not hasattr(f, "read"): f = open(f, "rb") callbacks = pickle.load(f) self.raw_regex_queue = [] for i in callbacks: fn = types.FunctionType(marshal.loads(i[1]), globals()) if i[2]: # method, this only works for methods on IRCbot callback = types.MethodType(fn, self, self.__class__) else: callback = fn self.raw_regex_queue.append((i[0], callback)) def read_bans(self): try: fd = open(self.ban_file, "r") except IOError: fd = open(self.ban_file, "w+") bans = fd.read() fd.close() self.banlist = [re.compile(i) for i in bans.split("\n") if i] return self.banlist def write_bans(self): fd = open(self.ban_file, "w+") fd.write("\n".join([i.pattern for i in self.banlist])) fd.close() def ban(self, pattern): self.banlist.append(re.compile(pattern)) self.write_bans() def unban(self, pattern): for i in self.banlist: if i.pattern == pattern: self.banlist.remove(i) self.write_bans() # to kill threadsdef exec_global_tracer(frame, event, arg): if event == "call": return exec_local_tracer else: return exec_global_tracerdef exec_local_tracer(frame, event, arg): try: info=frame.f_globals["thread_info"].info except KeyError: return exec_local_tracer if info[3].isSet(): thread.exit() return exec_local_tracer class IRCFileWrapper: def __init__(self, raw, channel, rate_limit = None): """ rate_limit is function of one argument (the number of lines sent so far), and must return a number (the number of seconds to wait before sending the next line)""" self.raw = raw self.channel = channel self.rate_limit = rate_limit or (lambda x: (x > 2 and min([x/5., 2])) or 0) def write(self, s): if not self.channel: return s = str(s) s = s.split("\n") lines_sent = 0 for i in s: if i.isspace() or not i: continue sent = 0 while sent < len(i): self.raw("PRIVMSG %s :%s" % (self.channel, i[sent:sent+375])) sent += 375 lines_sent += 1 time.sleep(self.rate_limit(lines_sent)) __call__ = writeclass SysWrapper: """wrap the sys module with our own stdout and stderr""" def __init__(self, stdout, stderr): self.__sys = __import__("sys") self.stdout = stdout self.stderr = stderr def __getattr__(self, what): if what == "stdout": return self.stdout elif what == "stderr": return self.stderr else: # this is the only branch ever called return getattr(self.__sys, what)args = { "host": "85.188.1.26", "nick": "EagleBot", "ident": "bsmnt", "realname": "bsmntbombdood bot", "chan": "#kilbot", "exec_chans": ["#kilbot"], "owner": r"ihope!\S*@\S*" }if os.getuid() == 0: os.setgid(1005) os.setuid(1005)thread_info = threading.local()bot = IRCbot(**args)bot.listen()def hack_closure(func, locals, globals): code = func.func_code env = globals.copy() needed = [i for i in code.co_names if i not in code.co_varnames] print needed for var in needed: env[var] = locals[var] for k in locals: print env[k] return types.FunctionType(code, env)