diff --git a/decorators.py b/decorators.py index 1c4c40f..e9032db 100644 --- a/decorators.py +++ b/decorators.py @@ -7,9 +7,9 @@ def generate(fdict): def dec(f): def innerf(*args): largs = list(args) - if not raw_nick: largs[1] = parse_nick(largs[1])[0] + if not raw_nick and largs[1]: largs[1] = parse_nick(largs[1])[0] if admin_only: - if largs[1] in botconfig.ADMINS: + if largs[1] and largs[1] in botconfig.ADMINS: return f(*largs) else: largs[0].notice(largs[1], "You are not an admin.") diff --git a/oyoyo/client.py b/oyoyo/client.py index 631430f..486e978 100644 --- a/oyoyo/client.py +++ b/oyoyo/client.py @@ -60,25 +60,8 @@ class IRCClient(object): Warning: By default this class will not block on socket operations, this means if you use a plain while loop your app will consume 100% cpu. To enable blocking pass blocking=True. - - >>> class My_Handler(DefaultCommandHandler): - ... def privmsg(self, prefix, command, args): - ... print "%s said %s" % (prefix, args[1]) - ... - >>> def connect_callback(c): - ... c.join('#myroom') - ... - >>> cli = IRCClient(My_Handler, - ... host="irc.freenode.net", - ... port=6667, - ... nick="myname", - ... connect_cb=connect_callback) - ... - >>> cli_con = cli.connect() - >>> while 1: - ... cli_con.next() - ... """ + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.nickname = "" self.real_name = "" @@ -96,7 +79,7 @@ class IRCClient(object): """ send a message to the connected server. all arguments are joined with a space for convenience, for example the following are identical - >>> cli.send("JOIN %s" % some_room) + >>> cli.send("JOIN " + some_room) >>> cli.send("JOIN", some_room) In python 2, all args must be of type str or unicode, *BUT* if they are @@ -109,7 +92,7 @@ class IRCClient(object): # Convert all args to bytes if not already encoding = kwargs.get('encoding') or 'utf_8' bargs = [] - for arg in args: + for i,arg in enumerate(args): if isinstance(arg, str): bargs.append(bytes(arg, encoding)) elif isinstance(arg, bytes): @@ -117,11 +100,12 @@ class IRCClient(object): elif arg is None: continue else: - raise IRCClientError('Refusing to send one of the args from provided: %s' - % repr([(type(arg), arg) for arg in args])) + raise Exception(('Refusing to send arg at index {1} of the args from '+ + 'provided: {0}').format(repr([(type(arg), arg) + for arg in args]), i)) msg = bytes(" ", "utf_8").join(bargs) - logging.info('---> send "%s"' % msg) + logging.info('---> send "{0}"'.format(msg)) self.socket.send(msg + bytes("\r\n", "utf_8")) @@ -132,12 +116,12 @@ class IRCClient(object): >>> cli = IRCClient(my_handler, host="irc.freenode.net", port=6667) >>> g = cli.connect() >>> while 1: - ... g.next() + ... next(g) """ try: - logging.info('connecting to %s:%s' % (self.host, self.port)) - self.socket.connect(("%s" % self.host, self.port)) + logging.info('connecting to {0}:{1}'.format(self.host, self.port)) + self.socket.connect(("{0}".format(self.host), self.port)) if not self.blocking: self.socket.setblocking(0) @@ -151,17 +135,13 @@ class IRCClient(object): while not self._end: try: buffer += self.socket.recv(1024) - except socket.error as e: - try: # a little dance of compatibility to get the errno - errno = e.errno - except AttributeError: - errno = e[0] - if not self.blocking and errno == 11: + except socket.error as e: + if not self.blocking and e.errno == 11: pass else: raise e else: - data = buffer.split(bytes("\n", "ascii")) + data = buffer.split(bytes("\n", "utf_8")) buffer = data.pop() for el in data: @@ -181,6 +161,7 @@ class IRCClient(object): def msg(self, user, msg): for line in msg.split('\n'): self.send("PRIVMSG", user, ":{0}".format(line)) + privmsg = msg # Same thing def notice(self, user, msg): for line in msg.split('\n'): self.send("NOTICE", user, ":{0}".format(line)) @@ -191,3 +172,7 @@ class IRCClient(object): def user(self, uname, rname): self.send("USER", uname, self.host, self.host, rname or uname) + def mainLoop(self): + conn = self.connect() + while True: + next(conn) diff --git a/oyoyo/cmdhandler.py b/oyoyo/cmdhandler.py index ad96281..2f500e7 100644 --- a/oyoyo/cmdhandler.py +++ b/oyoyo/cmdhandler.py @@ -33,11 +33,11 @@ class CommandError(Exception): class NoSuchCommandError(CommandError): def __str__(self): - return 'No such command "%s"' % ".".join(self.cmd) + return 'No such command "{0}"'.format(".".join(self.cmd)) class ProtectedCommandError(CommandError): def __str__(self): - return 'Command "%s" is protected' % ".".join(self.cmd) + return 'Command "{0}" is protected'.format(".".join(self.cmd)) class CommandHandler(object): @@ -65,7 +65,7 @@ class CommandHandler(object): command_parts = [] for cmdpart in in_command_parts: if isinstance(cmdpart, bytes): - cmdpart = cmdpart.decode('ascii') + cmdpart = cmdpart.decode('utf_8') command_parts.append(cmdpart) p = self @@ -91,7 +91,10 @@ class CommandHandler(object): @protected def run(self, command, *args): """ finds and runs a command """ - logging.debug("processCommand %s(%s)" % (command, args)) + logging.debug("processCommand {0}({1})".format(command, + [arg.decode('utf_8') + for arg in args + if isinstance(arg, bytes)])) try: f = self.get(command) @@ -99,15 +102,15 @@ class CommandHandler(object): self.__unhandled__(command, *args) return - logging.debug('f %s' % f) - + logging.debug('f {0}'.format(f)) try: largs = list(args) for i,arg in enumerate(largs): - if arg: largs[i] = arg.decode('ascii') + if arg: largs[i] = arg.decode('utf_8') f(*largs) + self.__unhandled__(command, *args) except Exception as e: - logging.error('command raised %s' % e) + logging.error('command raised {0}'.format(e)) logging.error(traceback.format_exc()) raise CommandError(command) @@ -116,13 +119,6 @@ class CommandHandler(object): """The default handler for commands. Override this method to apply custom behavior (example, printing) unhandled commands. """ - logging.debug('unhandled command %s(%s)' % (cmd, args)) - - -class DefaultCommandHandler(CommandHandler): - """ CommandHandler that provides methods for the normal operation of IRC. - If you want your bot to properly respond to pings, etc, you should subclass this. - """ - - def ping(self, prefix, server): - self.client.send('PONG', server) + logging.debug('Unhandled command {0}({1})'.format(cmd, [arg.decode('utf_8') + for arg in args + if isinstance(arg, bytes)])) diff --git a/oyoyo/parse.py b/oyoyo/parse.py index 05f27d5..2121e19 100644 --- a/oyoyo/parse.py +++ b/oyoyo/parse.py @@ -40,8 +40,8 @@ def parse_raw_irc_command(element): ::= CR LF """ - parts = element.strip().split(bytes(" ", "ascii")) - if parts[0].startswith(bytes(':', 'ascii')): + parts = element.strip().split(bytes(" ", "utf_8")) + if parts[0].startswith(bytes(':', 'utf_8')): prefix = parts[0][1:] command = parts[1] args = parts[2:] @@ -54,16 +54,16 @@ def parse_raw_irc_command(element): try: command = numeric_events[command] except KeyError: - logging.warn('unknown numeric event %s' % command) + logging.warn('unknown numeric event {0}'.format(command)) command = command.lower() - if isinstance(command, bytes): command = command.decode("ascii") + if isinstance(command, bytes): command = command.decode("utf_8") - if args[0].startswith(bytes(':', 'ascii')): - args = [bytes(" ", "ascii").join(args)[1:]] + if args[0].startswith(bytes(':', 'utf_8')): + args = [bytes(" ", "utf_8").join(args)[1:]] else: for idx, arg in enumerate(args): - if arg.startswith(bytes(':', 'ascii')): - args = args[:idx] + [bytes(" ", 'ascii').join(args[idx:])[1:]] + if arg.startswith(bytes(':', 'utf_8')): + args = args[:idx] + [bytes(" ", 'utf_8').join(args[idx:])[1:]] break return (prefix, command, args) diff --git a/wolfbot.py b/wolfbot.py index c38de5c..d4ee833 100644 --- a/wolfbot.py +++ b/wolfbot.py @@ -1,11 +1,11 @@ from oyoyo.client import IRCClient -from oyoyo.cmdhandler import DefaultCommandHandler, protected +from oyoyo.cmdhandler import CommandHandler, protected from oyoyo.parse import parse_nick import logging import botconfig import wolfgame -class WolfBotHandler(DefaultCommandHandler): +class WolfBotHandler(CommandHandler): def __init__(self, client): super().__init__(client) @@ -28,19 +28,19 @@ class WolfBotHandler(DefaultCommandHandler): if cmd in wolfgame.HOOKS.keys(): largs = list(args) for i,arg in enumerate(largs): - if arg: largs[i] = arg.decode('ascii') + if isinstance(arg, bytes): largs[i] = arg.decode('ascii') wolfgame.HOOKS[cmd](self.client, *largs) else: - logging.debug('unhandled command %s(%s)' % (cmd, args)) + logging.debug('Unhandled command {0}({1})'.format(cmd, [arg.decode('utf_8') + for arg in args + if isinstance(arg, bytes)])) def main(): - logging.basicConfig(level=logging.INFO) + logging.basicConfig(level=logging.DEBUG) cli = IRCClient(WolfBotHandler, host=botconfig.HOST, port=botconfig.PORT, nickname=botconfig.NICK, connect_cb=wolfgame.connect_callback) - conn = cli.connect() - while True: - next(conn) + cli.mainLoop() if __name__ == "__main__": diff --git a/wolfgame.py b/wolfgame.py index d8587c5..c719c04 100644 --- a/wolfgame.py +++ b/wolfgame.py @@ -259,7 +259,7 @@ def chk_decision(cli): cli.msg(botconfig.CHANNEL, random.choice(vars.LYNCH_MESSAGES).format( votee, vars.get_role(votee))) - if del_player(cli, votee, True): + if del_player_lynch(cli, vote): transition_night(cli) @@ -368,10 +368,10 @@ def chk_win(cli): -def del_player(cli, nick, forced_death): +def del_player(cli, nick): """ - forced_death = True if death is from a natural game process - Returns False if one side won. forced_death = True when lynched or killed + Returns: False if one side won. + arg: forced_death = True when lynched. """ cmode = [] @@ -379,14 +379,17 @@ def del_player(cli, nick, forced_death): vars.del_player(nick) ret = True if vars.PHASE == "join": + # Died during the joining process as a person mass_mode(cli, cmode) return not chk_win(cli) - if vars.PHASE != "join" and ret: # Died during the game + if vars.PHASE != "join" and ret: + # Died during the game, so quiet! cmode.append(("+q", nick)) mass_mode(cli, cmode) vars.DEAD.append(nick) ret = not chk_win(cli) if vars.PHASE in ("night", "day") and ret: + # remove him from variables if he is in there if vars.VICTIM == nick: vars.VICTIM = "" for x in (vars.OBSERVED, vars.HVISITED): @@ -396,17 +399,28 @@ def del_player(cli, nick, forced_death): del x[k] elif x[k] == nick: del x[k] - if vars.PHASE == "day" and not forced_death and ret: # didn't die from lynching + return ret + + +def del_player_lynch(cli, nick): + if not del_player(cli, nick): + return + if vars.PHASE == "day" and not lynched_death and ret: + # didn't die from lynching, therefore a vote is still going on if nick in vars.VOTES.keys(): del vars.VOTES[nick] # Delete his votes for k in vars.VOTES.keys(): if nick in vars.VOTES[k]: vars.VOTES[k].remove(nick) chk_decision(cli) - return ret - + return not chk_win(cli) + + +@hook("ping") +def on_ping(cli, prefix, server): + cli.send('PONG', server) + - def leave(cli, what, nick): if vars.PHASE == "none": cli.notice(nick, "No game is currently running.") @@ -429,7 +443,7 @@ def leave(cli, what, nick): "Appears (s)he was a \u0002{1}\u0002.") msg = msg.format(nick, vars.get_role(nick)) cli.msg(botconfig.CHANNEL, msg) - del_player(cli, nick, False) + del_player(cli, nick) cmd("!leave")(lambda cli, nick, *rest: leave(cli, "!leave", nick)) cmd("!quit")(lambda cli, nick, *rest: leave(cli, "!quit", nick)) @@ -502,7 +516,7 @@ def transition_day(cli): "The drunk's pet tiger probably ate him.").format(crow)) dead.append(crow) for deadperson in dead: - if not del_player(cli, deadperson, True): + if not del_player(cli, deadperson): return cli.msg(chan, "\n".join(message)) cli.msg(chan, ("The villagers must now vote for whom to lynch. "+ @@ -540,6 +554,9 @@ def vote(cli, nick, chan, rest): pl_l = [x.strip().lower() for x in pl] rest = re.split("\s+",rest)[0].strip().lower() if rest in pl_l: + if nick in vars.WOUNDED: + cli.msg(chan, ("{0}: You are wounded and resting, "+ + "thus you are unable to vote for the day.")) voted = pl[pl_l.index(rest)] lcandidates = list(vars.VOTES.keys()) for voters in lcandidates: # remove previous vote @@ -618,13 +635,13 @@ def shoot(cli, nick, chan, rest): if victimrole in ("wolf", "werecrow"): cli.msg(chan, ("\u0002{0}\u0002 is a wolf, and is dying from "+ "the silver bullet.").format(victim)) - if not del_player(cli, victim, True): + if not del_player(cli, victim): return elif random.random() <= vars.MANSLAUGHTER_CHANCE: cli.msg(chan, ("\u0002{0}\u0002 is a not a wolf "+ "but was accidentally fatally injured.").format(victim)) cli.msg(chan, "Appears (s)he was a \u0002{0}\u0002.".format(victimrole)) - if not del_player(cli, victim, True): + if not del_player(cli, victim): return else: cli.msg(chan, ("\u0002{0}\u0002 is a villager and is injured but "+ @@ -639,7 +656,7 @@ def shoot(cli, nick, chan, rest): cli.msg(chan, ("\u0002{0}\u0002 should clean his/her weapons more often. "+ "The gun exploded and killed him/her!").format(nick)) cli.msg(chan, "Appears that (s)he was a \u0002{0}\u0002.".format(vars.get_role(nick))) - if not del_player(cli, nick, True): + if not del_player(cli, nick): return # Someone won. @checks