From 4189d5c874ea7b0cc66d1935b6fa57485b0d4958 Mon Sep 17 00:00:00 2001 From: nyuszika7h Date: Sat, 19 Jul 2014 19:05:49 +0200 Subject: [PATCH] autopep8 --- modules/common.py | 128 +- modules/wolfgame.py | 1873 +++++++++++++++++------------ oyoyo/client.py | 150 ++- oyoyo/ircevents.py | 5 +- oyoyo/parse.py | 10 +- settings/villagergame.wolfgame.py | 223 ++-- settings/wolfgame.py | 252 ++-- tools/decorators.py | 56 +- tools/moduleloader.py | 14 +- tools/wolfgamelogger.py | 28 +- wolfbot.py | 22 +- 11 files changed, 1644 insertions(+), 1117 deletions(-) diff --git a/modules/common.py b/modules/common.py index fdc825f..41b6f7a 100644 --- a/modules/common.py +++ b/modules/common.py @@ -1,4 +1,5 @@ -# The bot commands implemented in here are present no matter which module is loaded +# The bot commands implemented in here are present no matter which module +# is loaded import botconfig from tools import decorators @@ -9,17 +10,19 @@ from settings import common as var from base64 import b64encode import imp -def on_privmsg(cli, rawnick, chan, msg, notice = False): + +def on_privmsg(cli, rawnick, chan, msg, notice=False): currmod = ld.MODULES[ld.CURRENT_MODULE] - - if botconfig.IGNORE_HIDDEN_COMMANDS and (chan.startswith("@#") or chan.startswith("+#")): + + if botconfig.IGNORE_HIDDEN_COMMANDS and ( + chan.startswith("@#") or chan.startswith("+#")): return - - if (notice and ((chan != botconfig.NICK and not botconfig.ALLOW_NOTICE_COMMANDS) or - (chan == botconfig.NICK and not botconfig.ALLOW_PRIVATE_NOTICE_COMMANDS))): + + if (notice and ((chan != botconfig.NICK and not botconfig.ALLOW_NOTICE_COMMANDS) or ( + chan == botconfig.NICK and not botconfig.ALLOW_PRIVATE_NOTICE_COMMANDS))): return # not allowed in settings - - if chan != botconfig.NICK: #not a PM + + if chan != botconfig.NICK: # not a PM if currmod and "" in currmod.COMMANDS.keys(): for fn in currmod.COMMANDS[""]: try: @@ -29,13 +32,15 @@ def on_privmsg(cli, rawnick, chan, msg, notice = False): raise e else: logging.error(traceback.format_exc()) - cli.msg(chan, "An error has occurred and has been logged.") + cli.msg( + chan, + "An error has occurred and has been logged.") # Now that is always called first. for x in set(list(COMMANDS.keys()) + (list(currmod.COMMANDS.keys()) if currmod else list())): - if x and msg.lower().startswith(botconfig.CMD_CHAR+x): - h = msg[len(x)+1:] + if x and msg.lower().startswith(botconfig.CMD_CHAR + x): + h = msg[len(x) + 1:] if not h or h[0] == " " or not x: - for fn in COMMANDS.get(x,[])+(currmod.COMMANDS.get(x,[]) if currmod else []): + for fn in COMMANDS.get(x, []) + (currmod.COMMANDS.get(x, []) if currmod else []): try: fn(cli, rawnick, chan, h.lstrip()) except Exception as e: @@ -43,18 +48,20 @@ def on_privmsg(cli, rawnick, chan, msg, notice = False): raise e else: logging.error(traceback.format_exc()) - cli.msg(chan, "An error has occurred and has been logged.") - + cli.msg( + chan, + "An error has occurred and has been logged.") + else: for x in set(list(PM_COMMANDS.keys()) + (list(currmod.PM_COMMANDS.keys()) if currmod else list())): - if msg.lower().startswith(botconfig.CMD_CHAR+x): - h = msg[len(x)+1:] + if msg.lower().startswith(botconfig.CMD_CHAR + x): + h = msg[len(x) + 1:] elif not x or msg.lower().startswith(x): h = msg[len(x):] else: continue if not h or h[0] == " " or not x: - for fn in PM_COMMANDS.get(x, [])+(currmod.PM_COMMANDS.get(x,[]) if currmod else []): + for fn in PM_COMMANDS.get(x, []) + (currmod.PM_COMMANDS.get(x, []) if currmod else []): try: fn(cli, rawnick, h.lstrip()) except Exception as e: @@ -62,16 +69,22 @@ def on_privmsg(cli, rawnick, chan, msg, notice = False): raise e else: logging.error(traceback.format_exc()) - cli.msg(chan, "An error has occurred and has been logged.") - + cli.msg( + chan, + "An error has occurred and has been logged.") + + def __unhandled__(cli, prefix, cmd, *args): currmod = ld.MODULES[ld.CURRENT_MODULE] - if cmd in set(list(HOOKS.keys())+(list(currmod.HOOKS.keys()) if currmod else list())): + if cmd in set( + list(HOOKS.keys()) + + (list(currmod.HOOKS.keys()) if currmod else list())): largs = list(args) - for i,arg in enumerate(largs): - if isinstance(arg, bytes): largs[i] = arg.decode('ascii') - for fn in HOOKS.get(cmd, [])+(currmod.HOOKS.get(cmd, []) if currmod else []): + for i, arg in enumerate(largs): + if isinstance(arg, bytes): + largs[i] = arg.decode('ascii') + for fn in HOOKS.get(cmd, []) + (currmod.HOOKS.get(cmd, []) if currmod else []): try: fn(cli, prefix, *largs) except Exception as e: @@ -79,13 +92,17 @@ def __unhandled__(cli, prefix, cmd, *args): raise e else: logging.error(traceback.format_exc()) - cli.msg(botconfig.CHANNEL, "An error has occurred and has been logged.") + cli.msg( + botconfig.CHANNEL, + "An error has occurred and has been logged.") else: - logging.debug('Unhandled command {0}({1})'.format(cmd, [arg.decode('utf_8') - for arg in args - if isinstance(arg, bytes)])) + logging.debug( + 'Unhandled command {0}({1})'.format( + cmd, [ + arg.decode('utf_8') for arg in args if isinstance( + arg, bytes)])) + - COMMANDS = {} PM_COMMANDS = {} HOOKS = {} @@ -94,27 +111,28 @@ cmd = decorators.generate(COMMANDS) pmcmd = decorators.generate(PM_COMMANDS) hook = decorators.generate(HOOKS, raw_nick=True, permissions=False) + def connect_callback(cli): - def prepare_stuff(*args): + def prepare_stuff(*args): cli.join(botconfig.CHANNEL) - cli.msg("ChanServ", "op "+botconfig.CHANNEL) - + cli.msg("ChanServ", "op " + botconfig.CHANNEL) + cli.cap("REQ", "extended-join") cli.cap("REQ", "account-notify") - + try: ld.MODULES[ld.CURRENT_MODULE].connect_callback(cli) except AttributeError: - pass # no connect_callback for this one - + pass # no connect_callback for this one + cli.nick(botconfig.NICK) # very important (for regain/release) - + prepare_stuff = hook("endofmotd", hookid=294)(prepare_stuff) def mustregain(cli, *blah): - cli.ns_regain() - + cli.ns_regain() + def mustrelease(cli, *rest): cli.ns_release() cli.nick(botconfig.NICK) @@ -122,51 +140,53 @@ def connect_callback(cli): @hook("unavailresource", hookid=239) @hook("nicknameinuse", hookid=239) def must_use_temp_nick(cli, *etc): - cli.nick(botconfig.NICK+"_") + cli.nick(botconfig.NICK + "_") cli.user(botconfig.NICK, "") - + decorators.unhook(HOOKS, 239) hook("unavailresource")(mustrelease) hook("nicknameinuse")(mustregain) - + if botconfig.SASL_AUTHENTICATION: - + @hook("authenticate") def auth_plus(cli, something, plus): if plus == "+": - nick_b = bytes(botconfig.USERNAME if botconfig.USERNAME else botconfig.NICK, "utf-8") + nick_b = bytes( + botconfig.USERNAME if botconfig.USERNAME else botconfig.NICK, + "utf-8") pass_b = bytes(botconfig.PASS, "utf-8") secrt_msg = b'\0'.join((nick_b, nick_b, pass_b)) - cli.send("AUTHENTICATE " + b64encode(secrt_msg).decode("utf-8")) - + cli.send( + "AUTHENTICATE " + + b64encode(secrt_msg).decode("utf-8")) + @hook("cap") def on_cap(cli, svr, mynick, ack, cap): if ack.upper() == "ACK" and "sasl" in cap: cli.send("AUTHENTICATE PLAIN") - + @hook("903") def on_successful_auth(cli, blah, blahh, blahhh): cli.cap("END") - + @hook("904") @hook("905") @hook("906") @hook("907") def on_failure_auth(cli, *etc): cli.quit() - print("Authentication failed. Did you fill the account name "+ + print("Authentication failed. Did you fill the account name " + "in botconfig.USERNAME if it's different from the bot nick?") - - - + + @hook("ping") def on_ping(cli, prefix, server): cli.send('PONG', server) - - + if botconfig.DEBUG_MODE: - @cmd("module", admin_only = True) + @cmd("module", admin_only=True) def ch_module(cli, nick, chan, rest): rest = rest.strip() if rest in ld.MODULES.keys(): diff --git a/modules/wolfgame.py b/modules/wolfgame.py index 3b1996d..802f4e1 100644 --- a/modules/wolfgame.py +++ b/modules/wolfgame.py @@ -63,7 +63,7 @@ var.USERS = {} var.PINGING = False var.ADMIN_PINGING = False -var.ROLES = {"person" : []} +var.ROLES = {"person": []} var.SPECIAL_ROLES = {} var.ORIGINAL_ROLES = {} var.PLAYERS = {} @@ -90,7 +90,8 @@ var.STASISED = defaultdict(int) var.LOGGER = WolfgameLogger(var.LOG_FILENAME, var.BARE_LOG_FILENAME) -var.JOINED_THIS_GAME = [] # keeps track of who already joined this game at least once (cloaks) +# keeps track of who already joined this game at least once (cloaks) +var.JOINED_THIS_GAME = [] var.OPPED = False # Keeps track of whether the bot is opped @@ -101,23 +102,26 @@ if botconfig.DEBUG_MODE: var.DAY_TIME_LIMIT_CHANGE = 0 var.SHORT_DAY_LIMIT_WARN = 0 var.SHORT_DAY_LIMIT_CHANGE = 0 - var.KILL_IDLE_TIME = 0 #300 - var.WARN_IDLE_TIME = 0 #180 + var.KILL_IDLE_TIME = 0 # 300 + var.WARN_IDLE_TIME = 0 # 180 var.JOIN_TIME_LIMIT = 0 - + def connect_callback(cli): to_be_devoiced = [] cmodes = [] - + @hook("quietlist", hookid=294) def on_quietlist(cli, server, botnick, channel, q, quieted, by, something): - if re.match(".+\!\*@\*", quieted): # only unquiet people quieted by bot + if re.match( + ".+\!\*@\*", + quieted): # only unquiet people quieted by bot cmodes.append(("-q", quieted)) @hook("whospcrpl", hookid=294) def on_whoreply(cli, server, nick, ident, cloak, user, status, acc): - if user in var.USERS: return # Don't add someone who is already there + if user in var.USERS: + return # Don't add someone who is already there if user == botconfig.NICK: cli.nickname = user cli.ident = ident @@ -126,14 +130,14 @@ def connect_callback(cli): acc = "*" if "+" in status: to_be_devoiced.append(user) - var.USERS[user] = dict(cloak=cloak,account=acc) - + var.USERS[user] = dict(cloak=cloak, account=acc) + @hook("endofwho", hookid=294) def afterwho(*args): for nick in to_be_devoiced: cmodes.append(("-v", nick)) # devoice all on connect - + @hook("mode", hookid=294) def on_give_me_ops(cli, blah, blahh, modeaction, target="", *other): if modeaction == "+o" and target == botconfig.NICK: @@ -147,12 +151,13 @@ def connect_callback(cli): cli.mode(botconfig.CHANNEL, "q") # unquiet all - cli.mode(botconfig.CHANNEL, "-m") # remove -m mode from channel + cli.mode( + botconfig.CHANNEL, + "-m") # remove -m mode from channel elif modeaction == "-o" and target == botconfig.NICK: var.OPPED = False cli.msg("ChanServ", "op " + botconfig.CHANNEL) - cli.who(botconfig.CHANNEL, "%nuhaf") @@ -164,24 +169,28 @@ def mass_mode(cli, md): z = list(zip(*md[start_i:])) # zip this remainder ei = lmd % 4 # len(z) else: - z = list(zip(*md[start_i:start_i+4])) # zip four - ei = 4 # len(z) + z = list(zip(*md[start_i:start_i + 4])) # zip four + ei = 4 # len(z) # Now z equal something like [('+v', '-v'), ('asdf', 'wobosd')] arg1 = "".join(z[0]) arg2 = " ".join(z[1]) # + " " + " ".join([x+"!*@*" for x in z[1]]) cli.mode(botconfig.CHANNEL, arg1, arg2) - -def pm(cli, target, message): # message either privmsg or notice, depending on user settings + + +# message either privmsg or notice, depending on user settings +def pm(cli, target, message): if target in var.USERS and var.USERS[target]["cloak"] in var.SIMPLE_NOTIFY: cli.notice(target, message) else: cli.msg(target, message) + def reset_settings(): for attr in list(var.ORIGINAL_SETTINGS.keys()): setattr(var, attr, var.ORIGINAL_SETTINGS[attr]) dict.clear(var.ORIGINAL_SETTINGS) + def reset_modes_timers(cli): # Reset game timers for x, timr in var.TIMERS.items(): @@ -194,17 +203,18 @@ def reset_modes_timers(cli): for plr in var.list_players(): cmodes.append(("-v", plr)) for deadguy in var.DEAD: - cmodes.append(("-q", deadguy+"!*@*")) + cmodes.append(("-q", deadguy + "!*@*")) mass_mode(cli, cmodes) + def reset(cli): var.PHASE = "none" - + var.GAME_ID = 0 var.DEAD = [] - var.ROLES = {"person" : []} + var.ROLES = {"person": []} var.JOINED_THIS_GAME = [] @@ -215,6 +225,7 @@ def reset(cli): dict.clear(var.DCED_PLAYERS) dict.clear(var.DISCONNECTED) + def make_stasis(nick, penalty): try: cloak = var.USERS[nick]['cloak'] @@ -223,19 +234,19 @@ def make_stasis(nick, penalty): except KeyError: pass + @pmcmd("fdie", "fbye", admin_only=True) @cmd("fdie", "fbye", admin_only=True) def forced_exit(cli, nick, *rest): # Admin Only """Forces the bot to close""" - + if var.PHASE in ("day", "night"): stop_game(cli) else: reset_modes_timers(cli) reset(cli) - cli.quit("Forced quit from "+nick) - + cli.quit("Forced quit from " + nick) @pmcmd("frestart", admin_only=True) @@ -249,7 +260,7 @@ def restart_program(cli, nick, *rest): reset_modes_timers(cli) reset(cli) - cli.quit("Forced restart from "+nick) + cli.quit("Forced restart from " + nick) raise SystemExit finally: print("RESTARTING") @@ -262,8 +273,7 @@ def restart_program(cli, nick, *rest): os.execl(python, python, sys.argv[0], "--verbose") else: os.execl(python, python, *sys.argv) - - + @pmcmd("ping") def pm_ping(cli, nick, rest): @@ -274,77 +284,77 @@ def pm_ping(cli, nick, rest): def pinger(cli, nick, chan, rest): """Pings the channel to get people's attention. Rate-Limited.""" - if var.PHASE in ('night','day'): + if var.PHASE in ('night', 'day'): #cli.notice(nick, "You cannot use this command while a game is running.") cli.notice(nick, 'Pong!') return if (var.LAST_PING and - var.LAST_PING + timedelta(seconds=var.PING_WAIT) > datetime.now()): + var.LAST_PING + timedelta(seconds=var.PING_WAIT) > datetime.now()): cli.notice(nick, ("This command is rate-limited. " + "Please wait a while before using it again.")) return - + var.LAST_PING = datetime.now() if var.PINGING: return var.PINGING = True TO_PING = [] - - @hook("whoreply", hookid=800) def on_whoreply(cli, server, dunno, chan, dunno1, cloak, dunno3, user, status, dunno4): - if not var.PINGING: return - if user in (botconfig.NICK, nick): return # Don't ping self. + if not var.PINGING: + return + if user in (botconfig.NICK, nick): + return # Don't ping self. if (all((not var.OPT_IN_PING, 'G' not in status, # not /away '+' not in status, # not already joined (voiced) - cloak not in var.STASISED, # not in stasis + cloak not in var.STASISED, # not in stasis cloak not in var.AWAY)) or all((var.OPT_IN_PING, '+' not in status, cloak in var.PING_IN))): TO_PING.append(user) - @hook("endofwho", hookid=800) def do_ping(*args): - if not var.PINGING: return + if not var.PINGING: + return TO_PING.sort(key=lambda x: x.lower()) - - cli.msg(botconfig.CHANNEL, "PING! "+" ".join(TO_PING)) + + cli.msg(botconfig.CHANNEL, "PING! " + " ".join(TO_PING)) var.PINGING = False - + minimum = datetime.now() + timedelta(seconds=var.PING_MIN_WAIT) if not var.CAN_START_TIME or var.CAN_START_TIME < minimum: - var.CAN_START_TIME = minimum + var.CAN_START_TIME = minimum decorators.unhook(HOOKS, 800) cli.who(botconfig.CHANNEL) -@cmd("simple", raw_nick = True) -@pmcmd("simple", raw_nick = True) +@cmd("simple", raw_nick=True) +@pmcmd("simple", raw_nick=True) def mark_simple_notify(cli, nick, *rest): """If you want the bot to NOTICE you for every interaction""" - + nick, _, __, cloak = parse_nick(nick) - + if cloak in var.SIMPLE_NOTIFY: var.SIMPLE_NOTIFY.remove(cloak) var.remove_simple_rolemsg(cloak) - + cli.notice(nick, "You now no longer receive simple role instructions.") return - + var.SIMPLE_NOTIFY.append(cloak) var.add_simple_rolemsg(cloak) - + cli.notice(nick, "You now receive simple role instructions.") if not var.OPT_IN_PING: @@ -416,9 +426,9 @@ def fpinger(cli, nick, chan, rest): def join(cli, nick, chann_, rest): """Either starts a new game of Werewolf or joins an existing game that has not started yet.""" pl = var.list_players() - + chan = botconfig.CHANNEL - + nick, _, __, cloak = parse_nick(nick) if not var.OPPED: @@ -428,43 +438,57 @@ def join(cli, nick, chann_, rest): try: cloak = var.USERS[nick]['cloak'] if cloak is not None and cloak in var.STASISED: - cli.notice(nick, "Sorry, but you are in stasis for {0} games.".format(var.STASISED[cloak])) + cli.notice( + nick, + "Sorry, but you are in stasis for {0} games.".format( + var.STASISED[cloak])) return except KeyError: cloak = None - if var.PHASE == "none": - + cli.mode(chan, "+v", nick) var.ROLES["person"].append(nick) var.PHASE = "join" var.WAITED = 0 var.GAME_ID = time.time() var.JOINED_THIS_GAME.append(cloak) - var.CAN_START_TIME = datetime.now() + timedelta(seconds=var.MINIMUM_WAIT) - cli.msg(chan, ('\u0002{0}\u0002 has started a game of Werewolf. '+ - 'Type "{1}join" to join. Type "{1}start" to start the game. '+ - 'Type "{1}wait" to increase start wait time.').format(nick, botconfig.CMD_CHAR)) - + var.CAN_START_TIME = datetime.now( + ) + timedelta(seconds=var.MINIMUM_WAIT) + cli.msg( + chan, + ('\u0002{0}\u0002 has started a game of Werewolf. ' + + 'Type "{1}join" to join. Type "{1}start" to start the game. ' + + 'Type "{1}wait" to increase start wait time.').format( + nick, + botconfig.CMD_CHAR)) + # Set join timer if var.JOIN_TIME_LIMIT: t = threading.Timer(var.JOIN_TIME_LIMIT, kill_join, [cli, chan]) var.TIMERS['join'] = t t.daemon = True t.start() - + elif nick in pl: cli.notice(nick, "You're already playing!") elif len(pl) >= var.MAX_PLAYERS: cli.notice(nick, "Too many players! Try again next time.") elif var.PHASE != "join": - cli.notice(nick, "Sorry but the game is already running. Try again next time.") + cli.notice( + nick, + "Sorry but the game is already running. Try again next time.") else: - + cli.mode(chan, "+v", nick) var.ROLES["person"].append(nick) - cli.msg(chan, '\u0002{0}\u0002 has joined the game and raised the number of players to \u0002{1}\u0002.'.format(nick, len(pl) + 1)) + cli.msg( + chan, + '\u0002{0}\u0002 has joined the game and raised the number of players to \u0002{1}\u0002.'.format( + nick, + len(pl) + + 1)) if not cloak in var.JOINED_THIS_GAME: # make sure this only happens once var.JOINED_THIS_GAME.append(cloak) @@ -472,20 +496,24 @@ def join(cli, nick, chann_, rest): # add var.EXTRA_WAIT_JOIN to wait time if now > var.CAN_START_TIME: - var.CAN_START_TIME = now + timedelta(seconds=var.EXTRA_WAIT_JOIN) + var.CAN_START_TIME = now + timedelta( + seconds=var.EXTRA_WAIT_JOIN) else: var.CAN_START_TIME += timedelta(seconds=var.EXTRA_WAIT_JOIN) - # make sure there's at least var.WAIT_AFTER_JOIN seconds of wait time left, if not add them - if now + timedelta(seconds=var.WAIT_AFTER_JOIN) > var.CAN_START_TIME: - var.CAN_START_TIME = now + timedelta(seconds=var.WAIT_AFTER_JOIN) + # make sure there's at least var.WAIT_AFTER_JOIN seconds of wait time + # left, if not add them + if now + timedelta( + seconds=var.WAIT_AFTER_JOIN) > var.CAN_START_TIME: + var.CAN_START_TIME = now + timedelta( + seconds=var.WAIT_AFTER_JOIN) - var.LAST_STATS = None # reset + var.LAST_STATS = None # reset var.LAST_GSTATS = None var.LAST_PSTATS = None var.LAST_TIME = None - + def kill_join(cli, chan): pl = var.list_players() pl.sort(key=lambda x: x.lower()) @@ -497,7 +525,7 @@ def kill_join(cli, chan): 'has been canceled. If you are still active, ' + 'please join again to start a new game.') var.LOGGER.logMessage('Game canceled.') - + @cmd("fjoin", admin_only=True) def fjoin(cli, nick, chann_, rest): @@ -506,7 +534,7 @@ def fjoin(cli, nick, chann_, rest): if not rest.strip(): join(cli, nick, chan, "") - for a in re.split(" +",rest): + for a in re.split(" +", rest): a = a.strip() if not a: continue @@ -515,8 +543,8 @@ def fjoin(cli, nick, chann_, rest): if a.lower() not in ull: if not is_fake_nick(a) or not botconfig.DEBUG_MODE: if not noticed: # important - cli.msg(chan, nick+(": You may only fjoin "+ - "people who are in this channel.")) + cli.msg(chan, nick + (": You may only fjoin " + + "people who are in this channel.")) noticed = True continue if not is_fake_nick(a): @@ -526,13 +554,14 @@ def fjoin(cli, nick, chann_, rest): else: cli.notice(nick, "No, that won't be allowed.") + @cmd("fleave", "fquit", admin_only=True) def fleave(cli, nick, chann_, rest): chan = botconfig.CHANNEL - + if var.PHASE == "none": cli.notice(nick, "No game is running.") - for a in re.split(" +",rest): + for a in re.split(" +", rest): a = a.strip() if not a: continue @@ -541,32 +570,40 @@ def fleave(cli, nick, chann_, rest): if a.lower() in pll: a = pl[pll.index(a.lower())] else: - cli.msg(chan, nick+": That person is not playing.") + cli.msg(chan, nick + ": That person is not playing.") return - cli.msg(chan, ("\u0002{0}\u0002 is forcing"+ + cli.msg(chan, ("\u0002{0}\u0002 is forcing" + " \u0002{1}\u0002 to leave.").format(nick, a)) cli.msg(chan, "Say goodbye to the \02{0}\02.".format(var.get_role(a))) if var.PHASE == "join": - cli.msg(chan, ("New player count: \u0002{0}\u0002").format(len(var.list_players()) - 1)) + cli.msg( + chan, ("New player count: \u0002{0}\u0002").format( + len(var.list_players()) - 1)) if var.PHASE in ("day", "night"): - var.LOGGER.logMessage("{0} is forcing {1} to leave.".format(nick, a)) - var.LOGGER.logMessage("Say goodbye to the {0}".format(var.get_role(a))) + var.LOGGER.logMessage( + "{0} is forcing {1} to leave.".format( + nick, + a)) + var.LOGGER.logMessage( + "Say goodbye to the {0}".format( + var.get_role(a))) del_player(cli, a) @cmd("fstart", admin_only=True) def fstart(cli, nick, chan, rest): var.CAN_START_TIME = datetime.now() - cli.msg(botconfig.CHANNEL, "\u0002{0}\u0002 has forced the game to start.".format(nick)) + cli.msg( + botconfig.CHANNEL, + "\u0002{0}\u0002 has forced the game to start.".format(nick)) start(cli, nick, chan, rest) - @hook("kick") def on_kicked(cli, nick, chan, victim, reason): if victim == botconfig.NICK: cli.join(botconfig.CHANNEL) - cli.msg("ChanServ", "op "+botconfig.CHANNEL) + cli.msg("ChanServ", "op " + botconfig.CHANNEL) @hook("account") @@ -576,32 +613,34 @@ def on_account(cli, nick, acc): var.USERS[nick]["cloak"] = cloak var.USERS[nick]["account"] = acc + @cmd("stats") def stats(cli, nick, chan, rest): """Display the player statistics""" if var.PHASE == "none": cli.notice(nick, "No game is currently running.") return - + pl = var.list_players() - + if nick != chan and (nick in pl or var.PHASE == "join"): # only do this rate-limiting stuff if the person is in game - if (var.LAST_STATS and - var.LAST_STATS + timedelta(seconds=var.STATS_RATE_LIMIT) > datetime.now()): + if (var.LAST_STATS and var.LAST_STATS + + timedelta(seconds=var.STATS_RATE_LIMIT) > datetime.now()): cli.notice(nick, ("This command is rate-limited. " + "Please wait a while before using it again.")) return - + var.LAST_STATS = datetime.now() - + pl.sort(key=lambda x: x.lower()) if len(pl) > 1: - msg = '{0}: \u0002{1}\u0002 players: {2}'.format(nick, - len(pl), ", ".join(pl)) + msg = '{0}: \u0002{1}\u0002 players: {2}'.format( + nick, len(pl), + ", ".join(pl)) else: msg = '{0}: \u00021\u0002 player: {1}'.format(nick, pl[0]) - + if nick == chan: pm(cli, nick, msg) else: @@ -610,7 +649,7 @@ def stats(cli, nick, chan, rest): var.LOGGER.logMessage(msg.replace("\02", "")) else: cli.notice(nick, msg) - + if var.PHASE == "join": return @@ -620,8 +659,8 @@ def stats(cli, nick, chan, rest): if var.ROLES[k]] l2 = [k for k in var.ORIGINAL_ROLES.keys() if var.ORIGINAL_ROLES[k]] - rs = list(set(l1+l2)) - + rs = list(set(l1 + l2)) + # Due to popular demand, picky ordering if "wolf" in rs: rs.remove("wolf") @@ -632,8 +671,7 @@ def stats(cli, nick, chan, rest): if "villager" in rs: rs.remove("villager") rs.append("villager") - - + firstcount = len(var.ROLES[rs[0]]) if firstcount > 1 or not firstcount: vb = "are" @@ -645,16 +683,18 @@ def stats(cli, nick, chan, rest): continue elif role == "villager" and var.HIDDEN_TRAITOR: count += len(var.ROLES["traitor"]) - + if count > 1 or count == 0: - message.append("\u0002{0}\u0002 {1}".format(count if count else "\u0002no\u0002", var.plural(role))) + message.append( + "\u0002{0}\u0002 {1}".format( + count if count else "\u0002no\u0002", + var.plural(role))) else: message.append("\u0002{0}\u0002 {1}".format(count, role)) - stats_mssg = "{0}: It is currently {4}. There {3} {1}, and {2}.".format(nick, - ", ".join(message[0:-1]), - message[-1], - vb, - var.PHASE) + stats_mssg = "{0}: It is currently {4}. There {3} {1}, and {2}.".format( + nick, ", ".join(message[0: -1]), + message[-1], + vb, var.PHASE) if nick == chan: pm(cli, nick, stats_mssg) else: @@ -664,40 +704,46 @@ def stats(cli, nick, chan, rest): else: cli.notice(nick, stats_mssg) + @pmcmd("stats") def stats_pm(cli, nick, rest): stats(cli, nick, nick, rest) - def hurry_up(cli, gameid, change): - if var.PHASE != "day": return + if var.PHASE != "day": + return if gameid: if gameid != var.DAY_ID: return chan = botconfig.CHANNEL - + if not change: - cli.msg(chan, ("\02As the sun sinks inexorably toward the horizon, turning the lanky pine " + - "trees into fire-edged silhouettes, the villagers are reminded that very little " + - "time remains for them to reach a decision; if darkness falls before they have done " + - "so, the majority will win the vote. No one will be lynched if there " + - "are no votes or an even split.\02")) + cli.msg( + chan, + ("\02As the sun sinks inexorably toward the horizon, turning the lanky pine " + + "trees into fire-edged silhouettes, the villagers are reminded that very little " + + "time remains for them to reach a decision; if darkness falls before they have done " + + "so, the majority will win the vote. No one will be lynched if there " + + "are no votes or an even split.\02")) if not var.DAY_TIME_LIMIT_CHANGE: return if (len(var.list_players()) <= var.SHORT_DAY_PLAYERS): - tmr = threading.Timer(var.SHORT_DAY_LIMIT_CHANGE, hurry_up, [cli, var.DAY_ID, True]) + tmr = threading.Timer( + var.SHORT_DAY_LIMIT_CHANGE, hurry_up, [ + cli, var.DAY_ID, True]) else: - tmr = threading.Timer(var.DAY_TIME_LIMIT_CHANGE, hurry_up, [cli, var.DAY_ID, True]) + tmr = threading.Timer( + var.DAY_TIME_LIMIT_CHANGE, hurry_up, [ + cli, var.DAY_ID, True]) tmr.daemon = True var.TIMERS["day"] = tmr tmr.start() return - - + var.DAY_ID = 0 - + pl = var.list_players() avail = len(pl) - len(var.WOUNDED) votesneeded = avail // 2 + 1 @@ -716,13 +762,11 @@ def hurry_up(cli, gameid, change): var.VOTES[maxfound[1]] = [None] * votesneeded chk_decision(cli) # Induce a lynch else: - cli.msg(chan, ("As the sun sets, the villagers agree to "+ - "retire to their beds and wait for morning.")) - var.LOGGER.logMessage(("As the sun sets, the villagers agree to "+ + cli.msg(chan, ("As the sun sets, the villagers agree to " + + "retire to their beds and wait for morning.")) + var.LOGGER.logMessage(("As the sun sets, the villagers agree to " + "retire to their beds and wait for morning.")) transition_night(cli) - - @cmd("fnight", admin_only=True) @@ -741,7 +785,6 @@ def fday(cli, nick, chan, rest): transition_day(cli) - def chk_decision(cli): chan = botconfig.CHANNEL pl = var.list_players() @@ -749,7 +792,10 @@ def chk_decision(cli): votesneeded = avail // 2 + 1 for votee, voters in iter(var.VOTES.items()): if len(voters) >= votesneeded: - lmsg = random.choice(var.LYNCH_MESSAGES).format(votee, var.get_reveal_role(votee)) + lmsg = random.choice( + var.LYNCH_MESSAGES).format( + votee, + var.get_reveal_role(votee)) cli.msg(botconfig.CHANNEL, lmsg) var.LOGGER.logMessage(lmsg.replace("\02", "")) var.LOGGER.logBare(votee, "LYNCHED") @@ -757,40 +803,39 @@ def chk_decision(cli): transition_night(cli) - @cmd("votes") def show_votes(cli, nick, chan, rest): """Displays the voting statistics.""" - + if var.PHASE in ("none", "join"): cli.notice(nick, "No game is currently running.") return if var.PHASE != "day": cli.notice(nick, "Voting is only during the day.") return - - if (var.LAST_VOTES and - var.LAST_VOTES + timedelta(seconds=var.VOTES_RATE_LIMIT) > datetime.now()): + + if (var.LAST_VOTES and var.LAST_VOTES + + timedelta(seconds=var.VOTES_RATE_LIMIT) > datetime.now()): cli.notice(nick, ("This command is rate-limited." + "Please wait a while before using it again.")) - return - + return + pl = var.list_players() - + if nick in pl: - var.LAST_VOTES = datetime.now() - + var.LAST_VOTES = datetime.now() + if not var.VOTES.values(): - msg = nick+": No votes yet." + msg = nick + ": No votes yet." if nick in pl: - var.LAST_VOTES = None # reset + var.LAST_VOTES = None # reset else: votelist = ["{0}: {1} ({2})".format(votee, len(var.VOTES[votee]), " ".join(var.VOTES[votee])) for votee in var.VOTES.keys()] msg = "{0}: {1}".format(nick, ", ".join(votelist)) - + if nick in pl: cli.msg(chan, msg) else: @@ -799,7 +844,7 @@ def show_votes(cli, nick, chan, rest): pl = var.list_players() avail = len(pl) - len(var.WOUNDED) votesneeded = avail // 2 + 1 - the_message = ("{0}: \u0002{1}\u0002 players, \u0002{2}\u0002 votes "+ + the_message = ("{0}: \u0002{1}\u0002 players, \u0002{2}\u0002 votes " + "required to lynch, \u0002{3}\u0002 players available " + "to vote.").format(nick, len(pl), votesneeded, avail) if nick in pl: @@ -808,17 +853,15 @@ def show_votes(cli, nick, chan, rest): cli.notice(nick, the_message) - def chk_traitor(cli): for tt in var.ROLES["traitor"]: var.ROLES["wolf"].append(tt) var.ROLES["traitor"].remove(tt) - pm(cli, tt, ('HOOOOOOOOOWL. You have become... a wolf!\n'+ + pm(cli, tt, ('HOOOOOOOOOWL. You have become... a wolf!\n' + 'It is up to you to avenge your fallen leaders!')) - -def stop_game(cli, winner = ""): +def stop_game(cli, winner=""): chan = botconfig.CHANNEL if var.DAY_START_TIME: now = datetime.now() @@ -833,78 +876,87 @@ def stop_game(cli, winner = ""): nitemin, nitesec = var.NIGHT_TIMEDELTA.seconds // 60, var.NIGHT_TIMEDELTA.seconds % 60 total = var.DAY_TIMEDELTA + var.NIGHT_TIMEDELTA tmin, tsec = total.seconds // 60, total.seconds % 60 - gameend_msg = ("Game lasted \u0002{0:0>2}:{1:0>2}\u0002. " + - "\u0002{2:0>2}:{3:0>2}\u0002 was day. " + - "\u0002{4:0>2}:{5:0>2}\u0002 was night. ").format(tmin, tsec, - daymin, daysec, - nitemin, nitesec) + gameend_msg = ( + "Game lasted \u0002{0:0>2}:{1:0>2}\u0002. " + + "\u0002{2:0>2}:{3:0>2}\u0002 was day. " + + "\u0002{4:0>2}:{5:0>2}\u0002 was night. ").format( + tmin, + tsec, + daymin, + daysec, + nitemin, + nitesec) cli.msg(chan, gameend_msg) - var.LOGGER.logMessage(gameend_msg.replace("\02", "")+"\n") + var.LOGGER.logMessage(gameend_msg.replace("\02", "") + "\n") var.LOGGER.logBare("DAY", "TIME", str(var.DAY_TIMEDELTA.seconds)) var.LOGGER.logBare("NIGHT", "TIME", str(var.NIGHT_TIMEDELTA.seconds)) var.LOGGER.logBare("GAME", "TIME", str(total.seconds)) roles_msg = [] - + var.ORIGINAL_ROLES["cursed villager"] = var.CURSED # A hack var.ORIGINAL_ROLES["gunner"] = list(var.GUNNERS.keys()) lroles = list(var.ORIGINAL_ROLES.keys()) lroles.remove("wolf") lroles.insert(0, "wolf") # picky, howl consistency - + for role in lroles: if len(var.ORIGINAL_ROLES[role]) == 0 or role == "villager": continue playersinrole = list(var.ORIGINAL_ROLES[role]) - for i,plr in enumerate(playersinrole): + for i, plr in enumerate(playersinrole): if plr.startswith("(dced)"): # don't care about it here playersinrole[i] = plr[6:] if len(playersinrole) == 2: msg = "The {1} were \u0002{0[0]}\u0002 and \u0002{0[1]}\u0002." roles_msg.append(msg.format(playersinrole, var.plural(role))) elif len(playersinrole) == 1: - roles_msg.append("The {1} was \u0002{0[0]}\u0002.".format(playersinrole, - role)) + roles_msg.append( + "The {1} was \u0002{0[0]}\u0002.".format( + playersinrole, + role)) else: msg = "The {2} were {0}, and \u0002{1}\u0002." - nickslist = ["\u0002"+x+"\u0002" for x in playersinrole[0:-1]] + nickslist = ["\u0002" + x + "\u0002" for x in playersinrole[0:-1]] roles_msg.append(msg.format(", ".join(nickslist), - playersinrole[-1], - var.plural(role))) + playersinrole[-1], + var.plural(role))) cli.msg(chan, " ".join(roles_msg)) reset_modes_timers(cli) - + # Set temporary phase to deal with disk lag var.PHASE = "writing files" - + plrl = [] - for role,ppl in var.ORIGINAL_ROLES.items(): + for role, ppl in var.ORIGINAL_ROLES.items(): for x in ppl: plrl.append((x, role)) - + var.LOGGER.saveToFile() - + for plr, rol in plrl: - #if plr not in var.USERS.keys(): # they died TODO: when a player leaves, count the game as lost for them + # if plr not in var.USERS.keys(): # they died TODO: when a player leaves, count the game as lost for them # if plr in var.DEAD_USERS.keys(): # acc = var.DEAD_USERS[plr]["account"] # else: - # continue # something wrong happened - #else: + # continue # something wrong happened + # else: if plr.startswith("(dced)") and plr[6:] in var.DCED_PLAYERS.keys(): acc = var.DCED_PLAYERS[plr[6:]]["account"] elif plr in var.PLAYERS.keys(): acc = var.PLAYERS[plr]["account"] else: - continue #probably fjoin'd fake + continue # probably fjoin'd fake if acc == "*": continue # not logged in during game start # determine if this player's team won - if plr in (var.ORIGINAL_ROLES["wolf"] + var.ORIGINAL_ROLES["traitor"] + - var.ORIGINAL_ROLES["werecrow"]): # the player was wolf-aligned + if plr in ( + var.ORIGINAL_ROLES["wolf"] + + var.ORIGINAL_ROLES["traitor"] + + var.ORIGINAL_ROLES["werecrow"]): # the player was wolf-aligned if winner == "wolves": won = True elif winner == "villagers": @@ -918,17 +970,18 @@ def stop_game(cli, winner = ""): won = True else: break - - iwon = won and plr in var.list_players() # survived, team won = individual win - + + # survived, team won = individual win + iwon = won and plr in var.list_players() + var.update_role_stats(acc, rol, won, iwon) - + size = len(var.list_players()) + len(var.DEAD) - if winner != "": # Only update if not an abnormal game stop + if winner != "": # Only update if not an abnormal game stop var.update_game_stats(size, winner) - + reset(cli) - + # This must be after reset(cli) if var.AFTER_FLASTGAME: var.AFTER_FLASTGAME() @@ -936,57 +989,60 @@ def stop_game(cli, winner = ""): if var.ADMIN_TO_PING: # It was an flastgame cli.msg(chan, "PING! " + var.ADMIN_TO_PING) var.ADMIN_TO_PING = None - + return True -def chk_win(cli, end_game = True): + +def chk_win(cli, end_game=True): """ Returns True if someone won """ - + chan = botconfig.CHANNEL lpl = len(var.list_players()) - + if lpl == 0: #cli.msg(chan, "No more players remaining. Game ended.") reset_modes_timers(cli) reset(cli) return True - + if var.PHASE == "join": return False - - - lwolves = (len(var.ROLES["wolf"])+ - len(var.ROLES["traitor"])+ + + lwolves = (len(var.ROLES["wolf"]) + + len(var.ROLES["traitor"]) + len(var.ROLES["werecrow"])) if var.PHASE == "day": lpl -= len([x for x in var.WOUNDED if x not in var.ROLES["traitor"]]) lwolves -= len([x for x in var.WOUNDED if x in var.ROLES["traitor"]]) - + if lwolves == lpl / 2: - message = ("Game over! There are the same number of wolves as " + - "uninjured villagers. The wolves overpower the villagers and win.") + message = ( + "Game over! There are the same number of wolves as " + + "uninjured villagers. The wolves overpower the villagers and win.") village_win = False elif lwolves > lpl / 2: - message = ("Game over! There are more wolves than "+ - "uninjured villagers. The wolves overpower the villagers and win.") + message = ( + "Game over! There are more wolves than " + + "uninjured villagers. The wolves overpower the villagers and win.") village_win = False elif (not var.ROLES["wolf"] and not var.ROLES["traitor"] and not var.ROLES["werecrow"]): message = ("Game over! All the wolves are dead! The villagers " + - "chop them up, BBQ them, and have a hearty meal.") + "chop them up, BBQ them, and have a hearty meal.") village_win = True - elif (not var.ROLES["wolf"] and not + elif (not var.ROLES["wolf"] and not var.ROLES["werecrow"] and var.ROLES["traitor"]): for t in var.ROLES["traitor"]: var.LOGGER.logBare(t, "TRANSFORM") chk_traitor(cli) - cli.msg(chan, ('\u0002The villagers, during their celebrations, are '+ - 'frightened as they hear a loud howl. The wolves are '+ + cli.msg(chan, ('\u0002The villagers, during their celebrations, are ' + + 'frightened as they hear a loud howl. The wolves are ' + 'not gone!\u0002')) - var.LOGGER.logMessage(('The villagers, during their celebrations, are '+ - 'frightened as they hear a loud howl. The wolves are '+ - 'not gone!')) + var.LOGGER.logMessage( + ('The villagers, during their celebrations, are ' + + 'frightened as they hear a loud howl. The wolves are ' + + 'not gone!')) return chk_win(cli, end_game) else: return False @@ -998,19 +1054,16 @@ def chk_win(cli, end_game = True): return True - - - -def del_player(cli, nick, forced_death = False, devoice = True, end_game = True): +def del_player(cli, nick, forced_death=False, devoice=True, end_game=True): """ Returns: False if one side won. arg: forced_death = True when lynched or when the seer/wolf both don't act """ - t = time.time() # time - - var.LAST_STATS = None # reset + t = time.time() # time + + var.LAST_STATS = None # reset var.LAST_VOTES = None - + with var.GRAVEYARD_LOCK: if not var.GAME_ID or var.GAME_ID > t: # either game ended, or a new game has started. @@ -1027,14 +1080,14 @@ def del_player(cli, nick, forced_death = False, devoice = True, end_game = True) if var.PHASE != "join": # Died during the game, so quiet! if not is_fake_nick(nick): - cmode.append(("+q", nick+"!*@*")) + cmode.append(("+q", nick + "!*@*")) mass_mode(cli, cmode) if nick not in var.DEAD: var.DEAD.append(nick) ret = not chk_win(cli, end_game) if var.PHASE in ("night", "day") and ret: # remove the player from variables if they're in there - for a,b in list(var.KILLS.items()): + for a, b in list(var.KILLS.items()): if b == nick: del var.KILLS[a] elif a == nick: @@ -1048,29 +1101,31 @@ def del_player(cli, nick, forced_death = False, devoice = True, end_game = True) del x[k] if nick in var.DISCONNECTED: del var.DISCONNECTED[nick] - if var.PHASE == "day" and not forced_death and ret: # didn't die from lynching + # didn't die from lynching + if var.PHASE == "day" and not forced_death and ret: if nick in var.VOTES.keys(): - del var.VOTES[nick] # Delete other people's votes on the player + # Delete other people's votes on the player + del var.VOTES[nick] for k in list(var.VOTES.keys()): if nick in var.VOTES[k]: var.VOTES[k].remove(nick) if not var.VOTES[k]: # no more votes on that person del var.VOTES[k] - break # can only vote once - + break # can only vote once + if nick in var.WOUNDED: var.WOUNDED.remove(nick) chk_decision(cli) elif var.PHASE == "night" and ret: chk_nightdone(cli) - return ret + return ret def reaper(cli, gameid): # check to see if idlers need to be killed. var.IDLE_WARNED = [] chan = botconfig.CHANNEL - + while gameid == var.GAME_ID: with var.GRAVEYARD_LOCK: # Terminate reaper when experiencing disk lag @@ -1083,80 +1138,111 @@ def reaper(cli, gameid): lst = var.LAST_SAID_TIME.get(nick, var.GAME_START_TIME) tdiff = datetime.now() - lst if (tdiff > timedelta(seconds=var.WARN_IDLE_TIME) and - nick not in var.IDLE_WARNED): + nick not in var.IDLE_WARNED): if var.WARN_IDLE_TIME: to_warn.append(nick) var.IDLE_WARNED.append(nick) - var.LAST_SAID_TIME[nick] = (datetime.now() - - timedelta(seconds=var.WARN_IDLE_TIME)) # Give them a chance + var.LAST_SAID_TIME[nick] = ( + datetime.now() - + timedelta( + seconds=var.WARN_IDLE_TIME)) # Give them a chance elif (tdiff > timedelta(seconds=var.KILL_IDLE_TIME) and - nick in var.IDLE_WARNED): + nick in var.IDLE_WARNED): if var.KILL_IDLE_TIME: to_kill.append(nick) elif (tdiff < timedelta(seconds=var.WARN_IDLE_TIME) and - nick in var.IDLE_WARNED): - var.IDLE_WARNED.remove(nick) # player saved himself from death + nick in var.IDLE_WARNED): + # player saved himself from death + var.IDLE_WARNED.remove(nick) for nck in to_kill: if nck not in var.list_players(): continue - cli.msg(chan, ("\u0002{0}\u0002 didn't get out of bed for a very long "+ - "time and has been found dead. The survivors bury "+ - "the \u0002{1}\u0002's body.").format(nck, var.get_reveal_role(nck))) + cli.msg( + chan, + ("\u0002{0}\u0002 didn't get out of bed for a very long " + + "time and has been found dead. The survivors bury " + + "the \u0002{1}\u0002's body.").format( + nck, + var.get_reveal_role(nck))) make_stasis(nck, var.IDLE_STASIS_PENALTY) if not del_player(cli, nck): return pl = var.list_players() x = [a for a in to_warn if a in pl] if x: - cli.msg(chan, ("{0}: \u0002You have been idling for a while. "+ - "Please say something soon or you "+ - "might be declared dead.\u0002").format(", ".join(x))) + cli.msg( + chan, + ("{0}: \u0002You have been idling for a while. " + + "Please say something soon or you " + + "might be declared dead.\u0002").format( + ", ".join(x))) for dcedplayer in list(var.DISCONNECTED.keys()): _, timeofdc, what = var.DISCONNECTED[dcedplayer] - if what == "quit" and (datetime.now() - timeofdc) > timedelta(seconds=var.QUIT_GRACE_TIME): - cli.msg(chan, ("\02{0}\02 was mauled by wild animals and has died. It seems that "+ - "\02{1}\02 meat is tasty.").format(dcedplayer, var.get_reveal_role(dcedplayer))) + if what == "quit" and ( + datetime.now() - + timeofdc) > timedelta( + seconds=var.QUIT_GRACE_TIME): + cli.msg( + chan, + ("\02{0}\02 was mauled by wild animals and has died. It seems that " + + "\02{1}\02 meat is tasty.").format( + dcedplayer, + var.get_reveal_role(dcedplayer))) if var.PHASE != "join": make_stasis(dcedplayer, var.PART_STASIS_PENALTY) - if not del_player(cli, dcedplayer, devoice = False): + if not del_player(cli, dcedplayer, devoice=False): return elif what == "part" and (datetime.now() - timeofdc) > timedelta(seconds=var.PART_GRACE_TIME): - cli.msg(chan, ("\02{0}\02, a \02{1}\02, ate some poisonous berries "+ - "and has died.").format(dcedplayer, var.get_reveal_role(dcedplayer))) + cli.msg( + chan, + ("\02{0}\02, a \02{1}\02, ate some poisonous berries " + + "and has died.").format( + dcedplayer, + var.get_reveal_role(dcedplayer))) if var.PHASE != "join": make_stasis(dcedplayer, var.PART_STASIS_PENALTY) - if not del_player(cli, dcedplayer, devoice = False): + if not del_player(cli, dcedplayer, devoice=False): return time.sleep(10) - @cmd("") # update last said def update_last_said(cli, nick, chan, rest): if var.PHASE not in ("join", "none"): var.LAST_SAID_TIME[nick] = datetime.now() - + if var.PHASE not in ("none", "join"): var.LOGGER.logChannelMessage(nick, rest) fullstring = "".join(rest) if var.CARE_BOLD and BOLD in fullstring: if var.KILL_BOLD: - cli.send("KICK {0} {1} :Using bold is not allowed".format(botconfig.CHANNEL, nick)) + cli.send( + "KICK {0} {1} :Using bold is not allowed".format( + botconfig.CHANNEL, + nick)) else: cli.notice(nick, "Using bold in the channel is not allowed.") - if var.CARE_COLOR and any(code in fullstring for code in ["\x03", "\x16", "\x1f" ]): + if var.CARE_COLOR and any( + code in fullstring for code in [ + "\x03", + "\x16", + "\x1f"]): if var.KILL_COLOR: - cli.send("KICK {0} {1} :Using color is not allowed".format(botconfig.CHANNEL, nick)) + cli.send( + "KICK {0} {1} :Using color is not allowed".format( + botconfig.CHANNEL, + nick)) else: cli.notice(nick, "Using color in the channel is not allowed.") + @hook("join") def on_join(cli, raw_nick, chan, acc="*", rname=""): - nick,m,u,cloak = parse_nick(raw_nick) + nick, m, u, cloak = parse_nick(raw_nick) if nick != botconfig.NICK: if nick not in var.USERS.keys(): - var.USERS[nick] = dict(cloak=cloak,account=acc) + var.USERS[nick] = dict(cloak=cloak, account=acc) else: var.USERS[nick]["cloak"] = cloak var.USERS[nick]["account"] = acc @@ -1164,13 +1250,15 @@ def on_join(cli, raw_nick, chan, acc="*", rname=""): if nick in var.DISCONNECTED.keys(): clk = var.DISCONNECTED[nick][0] if cloak == clk: - cli.mode(chan, "+v", nick, nick+"!*@*") + cli.mode(chan, "+v", nick, nick + "!*@*") del var.DISCONNECTED[nick] var.LAST_SAID_TIME[nick] = datetime.now() - cli.msg(chan, "\02{0}\02 has returned to the village.".format(nick)) - for r,rlist in var.ORIGINAL_ROLES.items(): - if "(dced)"+nick in rlist: - rlist.remove("(dced)"+nick) + cli.msg( + chan, + "\02{0}\02 has returned to the village.".format(nick)) + for r, rlist in var.ORIGINAL_ROLES.items(): + if "(dced)" + nick in rlist: + rlist.remove("(dced)" + nick) rlist.append(nick) break if nick in var.DCED_PLAYERS.keys(): @@ -1180,6 +1268,7 @@ def on_join(cli, raw_nick, chan, acc="*", rname=""): if nick == "ChanServ" and not var.OPPED: cli.msg("ChanServ", "op " + chan) + @cmd("goat") def goat(cli, nick, chan, rest): """Use a goat to interact with anyone in the channel during the day""" @@ -1197,7 +1286,7 @@ def goat(cli, nick, chan, rest): return ul = list(var.USERS.keys()) ull = [x.lower() for x in ul] - rest = re.split(" +",rest)[0].strip().lower() + rest = re.split(" +", rest)[0].strip().lower() if not rest: cli.notice(nick, "Not enough parameters.") return @@ -1211,34 +1300,42 @@ def goat(cli, nick, chan, rest): matches += 1 else: if matches != 1: - pm(cli, nick,"\u0002{0}\u0002 is not in this channel.".format(rest)) + pm(cli, + nick, + "\u0002{0}\u0002 is not in this channel.".format(rest)) return victim = ul[ull.index(victim)] goatact = random.choice(["kicks", "headbutts"]) - cli.msg(botconfig.CHANNEL, ("\u0002{0}\u0002's goat walks by "+ - "and {1} \u0002{2}\u0002.").format(nick, - goatact, victim)) - var.LOGGER.logMessage("{0}'s goat walks by and {1} {2}.".format(nick, goatact, - victim)) + cli.msg( + botconfig.CHANNEL, + ("\u0002{0}\u0002's goat walks by " + + "and {1} \u0002{2}\u0002.").format( + nick, + goatact, + victim)) + var.LOGGER.logMessage( + "{0}'s goat walks by and {1} {2}.".format( + nick, + goatact, + victim)) var.GOATED = True - - + @hook("nick") def on_nick(cli, prefix, nick): - prefix,u,m,cloak = parse_nick(prefix) + prefix, u, m, cloak = parse_nick(prefix) chan = botconfig.CHANNEL if prefix in var.USERS: var.USERS[nick] = var.USERS.pop(prefix) - + if prefix == var.ADMIN_TO_PING: var.ADMIN_TO_PING = nick # for k,v in list(var.DEAD_USERS.items()): # if prefix == k: - # var.DEAD_USERS[nick] = var.DEAD_USERS[k] - # del var.DEAD_USERS[k] + # var.DEAD_USERS[nick] = var.DEAD_USERS[k] + # del var.DEAD_USERS[k] if prefix in var.list_players() and prefix not in var.DISCONNECTED.keys(): r = var.ROLES[var.get_role(prefix)] @@ -1246,12 +1343,12 @@ def on_nick(cli, prefix, nick): r.remove(prefix) if var.PHASE in ("night", "day"): - for k,v in var.ORIGINAL_ROLES.items(): + for k, v in var.ORIGINAL_ROLES.items(): if prefix in v: var.ORIGINAL_ROLES[k].remove(prefix) var.ORIGINAL_ROLES[k].append(nick) break - for k,v in list(var.PLAYERS.items()): + for k, v in list(var.PLAYERS.items()): if prefix == k: var.PLAYERS[nick] = var.PLAYERS[k] del var.PLAYERS[k] @@ -1262,12 +1359,12 @@ def on_nick(cli, prefix, nick): var.CURSED.remove(prefix) for dictvar in (var.HVISITED, var.OBSERVED, var.GUARDED, var.KILLS): kvp = [] - for a,b in dictvar.items(): + for a, b in dictvar.items(): if a == prefix: a = nick if b == prefix: b = nick - kvp.append((a,b)) + kvp.append((a, b)) dictvar.update(kvp) if prefix in dictvar.keys(): del dictvar[prefix] @@ -1301,46 +1398,54 @@ def on_nick(cli, prefix, nick): if nick in var.DISCONNECTED.keys(): clk = var.DISCONNECTED[nick][0] if cloak == clk: - cli.mode(chan, "+v", nick, nick+"!*@*") + cli.mode(chan, "+v", nick, nick + "!*@*") del var.DISCONNECTED[nick] - - cli.msg(chan, ("\02{0}\02 has returned to "+ + + cli.msg(chan, ("\02{0}\02 has returned to " + "the village.").format(nick)) + def leave(cli, what, nick, why=""): nick, _, _, cloak = parse_nick(nick) - if what == "part" and why != botconfig.CHANNEL: return - + if what == "part" and why != botconfig.CHANNEL: + return + if why and why == botconfig.CHANGING_HOST_QUIT_MESSAGE: return if var.PHASE == "none": return if nick in var.PLAYERS: # must prevent double entry in var.ORIGINAL_ROLES - for r,rlist in var.ORIGINAL_ROLES.items(): + for r, rlist in var.ORIGINAL_ROLES.items(): if nick in rlist: var.ORIGINAL_ROLES[r].remove(nick) - var.ORIGINAL_ROLES[r].append("(dced)"+nick) + var.ORIGINAL_ROLES[r].append("(dced)" + nick) break var.DCED_PLAYERS[nick] = var.PLAYERS.pop(nick) if nick not in var.list_players() or nick in var.DISCONNECTED.keys(): return - + # the player who just quit was in the game killplayer = True if what == "part" and (not var.PART_GRACE_TIME or var.PHASE == "join"): - msg = ("\02{0}\02, a \02{1}\02, ate some poisonous berries and has "+ + msg = ("\02{0}\02, a \02{1}\02, ate some poisonous berries and has " + "died.").format(nick, var.get_reveal_role(nick)) elif what == "quit" and (not var.QUIT_GRACE_TIME or var.PHASE == "join"): - msg = ("\02{0}\02 was mauled by wild animals and has died. It seems that "+ - "\02{1}\02 meat is tasty.").format(nick, var.get_reveal_role(nick)) + msg = ( + "\02{0}\02 was mauled by wild animals and has died. It seems that " + + "\02{1}\02 meat is tasty.").format( + nick, + var.get_reveal_role(nick)) elif what != "kick": msg = "\u0002{0}\u0002 has gone missing.".format(nick) killplayer = False else: - msg = ("\02{0}\02 died due to falling off a cliff. The "+ - "\02{1}\02 is lost to the ravine forever.").format(nick, var.get_reveal_role(nick)) + msg = ( + "\02{0}\02 died due to falling off a cliff. The " + + "\02{1}\02 is lost to the ravine forever.").format( + nick, + var.get_reveal_role(nick)) make_stasis(nick, var.LEAVE_STASIS_PENALTY) cli.msg(botconfig.CHANNEL, msg) var.LOGGER.logMessage(msg.replace("\02", "")) @@ -1349,7 +1454,7 @@ def leave(cli, what, nick, why=""): else: var.DISCONNECTED[nick] = (cloak, datetime.now(), what) -#Functions decorated with hook do not parse the nick by default +# Functions decorated with hook do not parse the nick by default hook("part")(lambda cli, nick, *rest: leave(cli, "part", nick, rest[0])) hook("quit")(lambda cli, nick, *rest: leave(cli, "quit", nick, rest[0])) hook("kick")(lambda cli, nick, *rest: leave(cli, "kick", rest[1])) @@ -1370,17 +1475,24 @@ def leave_game(cli, nick, chan, rest): population = (" New player count: \u0002{0}\u0002").format(lpl) else: population = "" - if nick not in var.list_players() or nick in var.DISCONNECTED.keys(): # not playing + # not playing + if nick not in var.list_players() or nick in var.DISCONNECTED.keys(): cli.notice(nick, "You're not currently playing.") return - cli.msg(botconfig.CHANNEL, ("\02{0}\02, a \02{1}\02, has died of an unknown disease.{2}").format(nick, var.get_reveal_role(nick), population)) - var.LOGGER.logMessage(("{0}, a {1}, has died of an unknown disease.").format(nick, var.get_reveal_role(nick))) + cli.msg( + botconfig.CHANNEL, + ("\02{0}\02, a \02{1}\02, has died of an unknown disease.{2}").format( + nick, + var.get_reveal_role(nick), + population)) + var.LOGGER.logMessage( + ("{0}, a {1}, has died of an unknown disease.").format( + nick, + var.get_reveal_role(nick))) if var.PHASE != "join": make_stasis(nick, var.LEAVE_STASIS_PENALTY) del_player(cli, nick) - - def begin_day(cli): @@ -1394,10 +1506,12 @@ def begin_day(cli): var.HVISITED = {} var.GUARDED = {} var.STARTED_DAY_PLAYERS = len(var.list_players()) - - msg = ("The villagers must now vote for whom to lynch. "+ - 'Use "{0}lynch " to cast your vote. {1} votes '+ - 'are required to lynch.').format(botconfig.CMD_CHAR, len(var.list_players()) // 2 + 1) + + msg = ( + "The villagers must now vote for whom to lynch. " + + 'Use "{0}lynch " to cast your vote. {1} votes ' + + 'are required to lynch.').format( + botconfig.CMD_CHAR, len(var.list_players()) // 2 + 1) cli.msg(chan, msg) var.LOGGER.logMessage(msg) var.LOGGER.logBare("DAY", "BEGIN") @@ -1405,51 +1519,61 @@ def begin_day(cli): if var.DAY_TIME_LIMIT_WARN > 0: # Time limit enabled var.DAY_ID = time.time() if var.STARTED_DAY_PLAYERS <= var.SHORT_DAY_PLAYERS: - t = threading.Timer(var.SHORT_DAY_LIMIT_WARN, hurry_up, [cli, var.DAY_ID, False]) + t = threading.Timer( + var.SHORT_DAY_LIMIT_WARN, hurry_up, [ + cli, var.DAY_ID, False]) else: - t = threading.Timer(var.DAY_TIME_LIMIT_WARN, hurry_up, [cli, var.DAY_ID, False]) + t = threading.Timer( + var.DAY_TIME_LIMIT_WARN, hurry_up, [ + cli, var.DAY_ID, False]) var.TIMERS["day_warn"] = t t.daemon = True t.start() + def night_warn(cli, gameid): if gameid != var.NIGHT_ID: return - + if var.PHASE == "day": return - - cli.msg(botconfig.CHANNEL, ("\02A few villagers awake early and notice it " + - "is still dark outside. " + - "The night is almost over and there are " + - "still whispers heard in the village.\02")) + + cli.msg( + botconfig.CHANNEL, + ("\02A few villagers awake early and notice it " + + "is still dark outside. " + + "The night is almost over and there are " + + "still whispers heard in the village.\02")) + def transition_day(cli, gameid=0): if gameid: if gameid != var.NIGHT_ID: return var.NIGHT_ID = 0 - + if var.PHASE == "day": return - + var.PHASE = "day" var.GOATED = False chan = botconfig.CHANNEL - + # Reset daytime variables var.VOTES = {} var.INVESTIGATED = [] var.WOUNDED = [] var.DAY_START_TIME = datetime.now() - if (not len(var.SEEN)+len(var.KILLS)+len(var.OBSERVED) # neither seer nor wolf acted + if (not len(var.SEEN) + len(var.KILLS) + len(var.OBSERVED) # neither seer nor wolf acted and not var.START_WITH_DAY and var.FIRST_NIGHT and var.ROLES["seer"] and not botconfig.DEBUG_MODE): - cli.msg(botconfig.CHANNEL, "\02The wolves all die of a mysterious plague.\02") - for x in var.ROLES["wolf"]+var.ROLES["werecrow"]+var.ROLES["traitor"]: + cli.msg( + botconfig.CHANNEL, + "\02The wolves all die of a mysterious plague.\02") + for x in var.ROLES["wolf"] + var.ROLES["werecrow"] + var.ROLES["traitor"]: if not del_player(cli, x, True): return - + var.FIRST_NIGHT = False td = var.DAY_START_TIME - var.NIGHT_START_TIME @@ -1463,7 +1587,7 @@ def transition_day(cli, gameid=0): found[v] += 1 else: found[v] = 1 - + maxc = 0 victim = "" dups = [] @@ -1479,146 +1603,174 @@ def transition_day(cli, gameid=0): if dups: dups.append(victim) victim = random.choice(dups) - - message = [("Night lasted \u0002{0:0>2}:{1:0>2}\u0002. It is now daytime. "+ - "The villagers awake, thankful for surviving the night, "+ - "and search the village... ").format(min, sec)] + + message = [ + ("Night lasted \u0002{0:0>2}:{1:0>2}\u0002. It is now daytime. " + + "The villagers awake, thankful for surviving the night, " + + "and search the village... ").format( + min, + sec)] dead = [] crowonly = var.ROLES["werecrow"] and not var.ROLES["wolf"] if victim: - var.LOGGER.logBare(victim, "WOLVESVICTIM", *[y for x,y in var.KILLS.items() if x == victim]) + var.LOGGER.logBare(victim, "WOLVESVICTIM", * + [y for x, y in var.KILLS.items() if x == victim]) for crow, target in iter(var.OBSERVED.items()): if ((target in list(var.HVISITED.keys()) and var.HVISITED[target]) or # if var.HVISITED[target] is None, harlot visited self - target in var.SEEN or (target in list(var.GUARDED.keys()) and var.GUARDED[target])): - pm(cli, crow, ("As the sun rises, you conclude that \u0002{0}\u0002 was not in "+ - "bed all night, and you fly back to your house.").format(target)) + target in var.SEEN or (target in list(var.GUARDED.keys()) and var.GUARDED[target])): + pm( + cli, + crow, + ("As the sun rises, you conclude that \u0002{0}\u0002 was not in " + + "bed all night, and you fly back to your house.").format(target)) else: - pm(cli, crow, ("As the sun rises, you conclude that \u0002{0}\u0002 was sleeping "+ - "all night long, and you fly back to your house.").format(target)) + pm( + cli, + crow, + ("As the sun rises, you conclude that \u0002{0}\u0002 was sleeping " + + "all night long, and you fly back to your house.").format(target)) if victim in var.GUARDED.values(): for gangel in var.ROLES["guardian angel"]: if var.GUARDED.get(gangel) == victim: dead.append(gangel) - message.append(("\u0002{0}\u0002 sacrificed their life to the wolves to "+ - "guard that of another.").format(gangel)) + message.append( + ("\u0002{0}\u0002 sacrificed their life to the wolves to " + + "guard that of another.").format(gangel)) break victim = "" elif not victim: message.append(random.choice(var.NO_VICTIMS_MESSAGES) + - " All villagers, however, have survived.") + " All villagers, however, have survived.") elif victim in var.ROLES["harlot"]: # Attacked harlot, yay no kill if var.HVISITED.get(victim): - message.append("The wolves' selected victim was a harlot, "+ + message.append("The wolves' selected victim was a harlot, " + "who was not at home last night.") victim = "" if victim and (victim not in var.ROLES["harlot"] or # not a harlot - not var.HVISITED.get(victim)): # harlot stayed home - message.append(("The dead body of \u0002{0}\u0002, a "+ - "\u0002{1}\u0002, is found. Those remaining mourn the "+ - "tragedy.").format(victim, var.get_role(victim))) + not var.HVISITED.get(victim)): # harlot stayed home + message.append( + ("The dead body of \u0002{0}\u0002, a " + + "\u0002{1}\u0002, is found. Those remaining mourn the " + + "tragedy.").format( + victim, + var.get_role(victim))) dead.append(victim) var.LOGGER.logBare(victim, "KILLED") - if random.random() < 1/50: + if random.random() < 1 / 50: message.append(random.choice( ["https://i.imgur.com/nO8rZ.gif", - "https://i.imgur.com/uGVfZ.gif", - "https://i.imgur.com/mUcM09n.gif", - "https://i.imgur.com/P7TEGyQ.gif", - "https://i.imgur.com/b8HAvjL.gif", - "https://i.imgur.com/PIIfL15.gif"] - )) - - if victim in var.GUNNERS.keys() and var.GUNNERS[victim]: # victim had bullets! + "https://i.imgur.com/uGVfZ.gif", + "https://i.imgur.com/mUcM09n.gif", + "https://i.imgur.com/P7TEGyQ.gif", + "https://i.imgur.com/b8HAvjL.gif", + "https://i.imgur.com/PIIfL15.gif"] + )) + + # victim had bullets! + if victim in var.GUNNERS.keys() and var.GUNNERS[victim]: if random.random() < var.GUNNER_KILLS_WOLF_AT_NIGHT_CHANCE: wc = var.ROLES["werecrow"][:] for crow in wc: if crow in var.OBSERVED.keys(): wc.remove(crow) # don't kill off werecrows that observed - deadwolf = random.choice(var.ROLES["wolf"]+wc) - message.append(("Fortunately, the victim, \02{0}\02, had bullets, and "+ - "\02{1}\02, a \02{2}\02, was shot dead.").format(victim, deadwolf, var.get_role(deadwolf))) + deadwolf = random.choice(var.ROLES["wolf"] + wc) + message.append( + ("Fortunately, the victim, \02{0}\02, had bullets, and " + + "\02{1}\02, a \02{2}\02, was shot dead.").format( + victim, + deadwolf, + var.get_role(deadwolf))) var.LOGGER.logBare(deadwolf, "KILLEDBYGUNNER") dead.append(deadwolf) - if victim in var.HVISITED.values(): # victim was visited by some harlot + if victim in var.HVISITED.values(): # victim was visited by some harlot for hlt in var.HVISITED.keys(): if var.HVISITED[hlt] == victim: - message.append(("\02{0}\02, a \02harlot\02, made the unfortunate mistake of "+ - "visiting the victim's house last night and is "+ - "now dead.").format(hlt)) + message.append( + ("\02{0}\02, a \02harlot\02, made the unfortunate mistake of " + + "visiting the victim's house last night and is " + + "now dead.").format(hlt)) dead.append(hlt) for harlot in var.ROLES["harlot"]: - if var.HVISITED.get(harlot) in var.ROLES["wolf"]+var.ROLES["werecrow"]: - message.append(("\02{0}\02, a \02harlot\02, made the unfortunate mistake of "+ - "visiting a wolf's house last night and is "+ - "now dead.").format(harlot)) + if var.HVISITED.get(harlot) in var.ROLES[ + "wolf"] + var.ROLES["werecrow"]: + message.append( + ("\02{0}\02, a \02harlot\02, made the unfortunate mistake of " + + "visiting a wolf's house last night and is " + + "now dead.").format(harlot)) dead.append(harlot) for gangel in var.ROLES["guardian angel"]: - if var.GUARDED.get(gangel) in var.ROLES["wolf"]+var.ROLES["werecrow"]: + if var.GUARDED.get(gangel) in var.ROLES[ + "wolf"] + var.ROLES["werecrow"]: if victim == gangel: - continue # already dead. + continue # already dead. r = random.random() if r < var.GUARDIAN_ANGEL_DIES_CHANCE: - message.append(("\02{0}\02, a \02guardian angel\02, "+ - "made the unfortunate mistake of guarding a wolf "+ - "last night, and is now dead.").format(gangel)) + message.append( + ("\02{0}\02, a \02guardian angel\02, " + + "made the unfortunate mistake of guarding a wolf " + + "last night, and is now dead.").format(gangel)) var.LOGGER.logBare(gangel, "KILLEDWHENGUARDINGWOLF") dead.append(gangel) cli.msg(chan, "\n".join(message)) for msg in message: var.LOGGER.logMessage(msg.replace("\02", "")) - - for deadperson in dead: # kill each player, but don't end the game if one group outnumbers another - del_player(cli, deadperson, end_game = False) - if chk_win(cli): # if after the last person is killed, one side wins, then actually end the game here + + # kill each player, but don't end the game if one group outnumbers another + for deadperson in dead: + del_player(cli, deadperson, end_game=False) + # if after the last person is killed, one side wins, then actually end the + # game here + if chk_win(cli): return - - if (var.WOLF_STEALS_GUN and victim in dead and - victim in var.GUNNERS.keys() and var.GUNNERS[victim] > 0): + + if (var.WOLF_STEALS_GUN and victim in dead and + victim in var.GUNNERS.keys() and var.GUNNERS[victim] > 0): # victim has bullets - guntaker = random.choice(var.ROLES["wolf"] + var.ROLES["werecrow"] + guntaker = random.choice(var.ROLES["wolf"] + var.ROLES["werecrow"] + var.ROLES["traitor"]) # random looter numbullets = var.GUNNERS[victim] var.WOLF_GUNNERS[guntaker] = 1 # transfer bullets a wolf - mmsg = ("While searching {0}'s belongings, You found " + - "a gun loaded with 1 silver bullet! " + - "You may only use it during the day. " + - "If you shoot at a wolf, you will intentionally miss. " + - "If you shoot a villager, it is likely that they will be injured.") + mmsg = ( + "While searching {0}'s belongings, You found " + + "a gun loaded with 1 silver bullet! " + + "You may only use it during the day. " + + "If you shoot at a wolf, you will intentionally miss. " + + "If you shoot a villager, it is likely that they will be injured.") mmsg = mmsg.format(victim) pm(cli, guntaker, mmsg) var.GUNNERS[victim] = 0 # just in case - begin_day(cli) def chk_nightdone(cli): if (len(var.SEEN) >= len(var.ROLES["seer"]) and # Seers have seen. - len(var.HVISITED.keys()) >= len(var.ROLES["harlot"]) and # harlots have visited. - len(var.GUARDED.keys()) >= len(var.ROLES["guardian angel"]) and # guardians have guarded - len(var.KILLS)+len(var.OBSERVED) >= len(var.ROLES["werecrow"]+var.ROLES["wolf"]) and - var.PHASE == "night"): - + # harlots have visited. + len(var.HVISITED.keys()) >= len(var.ROLES["harlot"]) and + # guardians have guarded + len(var.GUARDED.keys()) >= len(var.ROLES["guardian angel"]) and + len(var.KILLS) + len(var.OBSERVED) >= len(var.ROLES["werecrow"] + var.ROLES["wolf"]) and + var.PHASE == "night"): + # check if wolves are actually agreeing if len(set(var.KILLS.values())) > 1: return - + for x, t in var.TIMERS.items(): t.cancel() - + var.TIMERS = {} if var.PHASE == "night": # Double check transition_day(cli) - @cmd("lynch", "vote", "v") def vote(cli, nick, chann_, rest): """Use this to vote for a candidate to be lynched""" chan = botconfig.CHANNEL - - rest = re.split(" +",rest)[0].strip().lower() + + rest = re.split(" +", rest)[0].strip().lower() if not rest: show_votes(cli, nick, chan, rest) @@ -1630,17 +1782,19 @@ def vote(cli, nick, chann_, rest): cli.notice(nick, "You're not currently playing.") return if var.PHASE != "day": - cli.notice(nick, ("Lynching is only allowed during the day. "+ + cli.notice(nick, ("Lynching is only allowed during the day. " + "Please wait patiently for morning.")) return if nick in var.WOUNDED: - cli.msg(chan, ("{0}: You are wounded and resting, "+ - "thus you are unable to vote for the day.").format(nick)) + cli.msg( + chan, + ("{0}: You are wounded and resting, " + + "thus you are unable to vote for the day.").format(nick)) return pl = var.list_players() pl_l = [x.strip().lower() for x in pl] - + matches = 0 for player in pl_l: if rest == player: @@ -1651,9 +1805,11 @@ def vote(cli, nick, chann_, rest): matches += 1 else: if matches != 1: - pm(cli, nick, "\u0002{0}\u0002 is currently not playing.".format(rest)) + pm(cli, + nick, + "\u0002{0}\u0002 is currently not playing.".format(rest)) return - + voted = pl[pl_l.index(target)] if not var.SELF_LYNCH_ALLOWED: @@ -1672,32 +1828,31 @@ def vote(cli, nick, chann_, rest): var.VOTES[voted] = [nick] else: var.VOTES[voted].append(nick) - cli.msg(chan, ("\u0002{0}\u0002 votes for "+ + cli.msg(chan, ("\u0002{0}\u0002 votes for " + "\u0002{1}\u0002.").format(nick, voted)) var.LOGGER.logMessage("{0} votes for {1}.".format(nick, voted)) var.LOGGER.logBare(voted, "VOTED", nick) - - var.LAST_VOTES = None # reset - - chk_decision(cli) + var.LAST_VOTES = None # reset + + chk_decision(cli) @cmd("retract") def retract(cli, nick, chann_, rest): """Takes back your vote during the day (for whom to lynch)""" - + chan = botconfig.CHANNEL - + if var.PHASE in ("none", "join"): cli.notice(nick, "No game is currently running.") return elif nick not in var.list_players() or nick in var.DISCONNECTED.keys(): cli.notice(nick, "You're not currently playing.") return - + if var.PHASE != "day": - cli.notice(nick, ("Lynching is only allowed during the day. "+ + cli.notice(nick, ("Lynching is only allowed during the day. " + "Please wait patiently for morning.")) return @@ -1710,11 +1865,12 @@ def retract(cli, nick, chann_, rest): cli.msg(chan, "\u0002{0}\u0002's vote was retracted.".format(nick)) var.LOGGER.logBare(voter, "RETRACT", nick) var.LOGGER.logMessage("{0}'s vote was retracted.".format(nick)) - var.LAST_VOTES = None # reset + var.LAST_VOTES = None # reset break else: cli.notice(nick, "You haven't voted yet.") + @pmcmd("retract") def wolfretract(cli, nick, rest): if var.PHASE in ("none", "join"): @@ -1723,7 +1879,7 @@ def wolfretract(cli, nick, rest): elif nick not in var.list_players() or nick in var.DISCONNECTED.keys(): cli.notice(nick, "You're not currently playing.") return - + role = var.get_role(nick) if role not in ('wolf', 'werecrow'): return @@ -1732,19 +1888,20 @@ def wolfretract(cli, nick, rest): return if role == "werecrow": # Check if already observed if var.OBSERVED.get(nick): - pm(cli, nick, ("You have already transformed into a crow, and "+ + pm(cli, nick, ("You have already transformed into a crow, and " + "cannot turn back until day.")) return - + if nick in var.KILLS.keys(): del var.KILLS[nick] pm(cli, nick, "You have retracted your vote.") #var.LOGGER.logBare(nick, "RETRACT", nick) + @cmd("shoot") def shoot(cli, nick, chann_, rest): """Use this to fire off a bullet at someone in the day if you have bullets""" - + chan = botconfig.CHANNEL if var.PHASE in ("none", "join"): cli.notice(nick, "No game is currently running.") @@ -1752,9 +1909,9 @@ def shoot(cli, nick, chann_, rest): elif nick not in var.list_players() or nick in var.DISCONNECTED.keys(): cli.notice(nick, "You're not currently playing.") return - + if var.PHASE != "day": - cli.notice(nick, ("Shooting is only allowed during the day. "+ + cli.notice(nick, ("Shooting is only allowed during the day. " + "Please wait patiently for morning.")) return if not (nick in var.GUNNERS.keys() or nick in var.WOLF_GUNNERS.keys()): @@ -1764,7 +1921,7 @@ def shoot(cli, nick, chann_, rest): (nick in var.WOLF_GUNNERS.keys() and not var.WOLF_GUNNERS[nick])): pm(cli, nick, "You don't have any more bullets.") return - victim = re.split(" +",rest)[0].strip().lower() + victim = re.split(" +", rest)[0].strip().lower() if not victim: cli.notice(nick, "Not enough parameters") return @@ -1780,56 +1937,75 @@ def shoot(cli, nick, chann_, rest): matches += 1 else: if matches != 1: - pm(cli, nick, "\u0002{0}\u0002 is currently not playing.".format(victim)) + pm(cli, + nick, + "\u0002{0}\u0002 is currently not playing.".format(victim)) return victim = pl[pll.index(target)] if victim == nick: cli.notice(nick, "You are holding it the wrong way.") return - - wolfshooter = nick in var.ROLES["wolf"]+var.ROLES["werecrow"]+var.ROLES["traitor"] - + + wolfshooter = nick in var.ROLES[ + "wolf"] + var.ROLES["werecrow"] + var.ROLES["traitor"] + if wolfshooter and nick in var.WOLF_GUNNERS: var.WOLF_GUNNERS[nick] -= 1 else: var.GUNNERS[nick] -= 1 - + rand = random.random() if nick in var.ROLES["village drunk"]: chances = var.DRUNK_GUN_CHANCES else: chances = var.GUN_CHANCES - - wolfvictim = victim in var.ROLES["wolf"]+var.ROLES["werecrow"] - if rand <= chances[0] and not (wolfshooter and wolfvictim): # didn't miss or suicide + + wolfvictim = victim in var.ROLES["wolf"] + var.ROLES["werecrow"] + if rand <= chances[0] and not ( + wolfshooter and wolfvictim): # didn't miss or suicide # and it's not a wolf shooting another wolf - - cli.msg(chan, ("\u0002{0}\u0002 shoots \u0002{1}\u0002 with "+ + + cli.msg(chan, ("\u0002{0}\u0002 shoots \u0002{1}\u0002 with " + "a silver bullet!").format(nick, victim)) - var.LOGGER.logMessage("{0} shoots {1} with a silver bullet!".format(nick, victim)) + var.LOGGER.logMessage( + "{0} shoots {1} with a silver bullet!".format( + nick, + victim)) victimrole = var.get_reveal_role(victim) if victimrole in ("wolf", "werecrow"): - cli.msg(chan, ("\u0002{0}\u0002 is a {1}, and is dying from "+ + cli.msg(chan, ("\u0002{0}\u0002 is a {1}, and is dying from " + "the silver bullet.").format(victim, victimrole)) - var.LOGGER.logMessage(("{0} is a {1}, and is dying from the "+ - "silver bullet.").format(victim, victimrole)) + var.LOGGER.logMessage( + ("{0} is a {1}, and is dying from the " + + "silver bullet.").format( + victim, + victimrole)) if not del_player(cli, victim): return elif random.random() <= var.MANSLAUGHTER_CHANCE: - cli.msg(chan, ("\u0002{0}\u0002 is not a wolf "+ - "but was accidentally fatally injured.").format(victim)) - cli.msg(chan, "The village has sacrificed a \u0002{0}\u0002.".format(victimrole)) - var.LOGGER.logMessage("{0} is not a wolf but was accidentally fatally injured.".format(victim)) - var.LOGGER.logMessage("The village has sacrificed a {0}.".format(victimrole)) + cli.msg( + chan, + ("\u0002{0}\u0002 is not a wolf " + + "but was accidentally fatally injured.").format(victim)) + cli.msg( + chan, "The village has sacrificed a \u0002{0}\u0002.".format( + victimrole)) + var.LOGGER.logMessage( + "{0} is not a wolf but was accidentally fatally injured.".format(victim)) + var.LOGGER.logMessage( + "The village has sacrificed a {0}.".format(victimrole)) if not del_player(cli, victim): return else: - cli.msg(chan, ("\u0002{0}\u0002 is a villager and was injured. Luckily "+ - "the injury is minor and will heal after a day of "+ - "rest.").format(victim)) - var.LOGGER.logMessage(("{0} is a villager and was injured. Luckily "+ - "the injury is minor and will heal after a day of "+ - "rest.").format(victim)) + cli.msg( + chan, + ("\u0002{0}\u0002 is a villager and was injured. Luckily " + + "the injury is minor and will heal after a day of " + + "rest.").format(victim)) + var.LOGGER.logMessage( + ("{0} is a villager and was injured. Luckily " + + "the injury is minor and will heal after a day of " + + "rest.").format(victim)) if victim not in var.WOUNDED: var.WOUNDED.append(victim) lcandidates = list(var.VOTES.keys()) @@ -1842,18 +2018,25 @@ def shoot(cli, nick, chann_, rest): chk_decision(cli) chk_win(cli) elif rand <= chances[0] + chances[1]: - cli.msg(chan, "\u0002{0}\u0002 is a lousy shooter and missed!".format(nick)) - var.LOGGER.logMessage("{0} is a lousy shooter and missed!".format(nick)) + cli.msg( + chan, + "\u0002{0}\u0002 is a lousy shooter and missed!".format(nick)) + var.LOGGER.logMessage( + "{0} is a lousy shooter and missed!".format(nick)) else: - cli.msg(chan, ("Oh no! \u0002{0}\u0002's gun was poorly maintained and has exploded! "+ - "The village mourns a gunner-\u0002{1}\u0002.").format(nick, var.get_reveal_role(nick))) - var.LOGGER.logMessage(("Oh no! {0}'s gun was poorly maintained and has exploded! "+ - "The village mourns a gunner-{1}.").format(nick, var.get_reveal_role(nick))) + cli.msg( + chan, + ("Oh no! \u0002{0}\u0002's gun was poorly maintained and has exploded! " + + "The village mourns a gunner-\u0002{1}\u0002.").format( + nick, + var.get_reveal_role(nick))) + var.LOGGER.logMessage( + ("Oh no! {0}'s gun was poorly maintained and has exploded! " + + "The village mourns a gunner-{1}.").format(nick, var.get_reveal_role(nick))) if not del_player(cli, nick): return # Someone won. - @pmcmd("kill") def kill(cli, nick, rest): if var.PHASE in ("none", "join"): @@ -1871,18 +2054,19 @@ def kill(cli, nick, rest): if var.PHASE != "night": pm(cli, nick, "You may only kill people at night.") return - victim = re.split(" +",rest)[0].strip().lower() + victim = re.split(" +", rest)[0].strip().lower() if not victim: pm(cli, nick, "Not enough parameters") return if role == "werecrow": # Check if flying to observe if var.OBSERVED.get(nick): - pm(cli, nick, ("You have already transformed into a crow; therefore, "+ - "you are physically unable to kill a villager.")) + pm(cli, nick, + ("You have already transformed into a crow; therefore, " + + "you are physically unable to kill a villager.")) return pl = var.list_players() pll = [x.lower() for x in pl] - + matches = 0 for player in pll: if victim == player: @@ -1893,18 +2077,23 @@ def kill(cli, nick, rest): matches += 1 else: if matches != 1: - pm(cli, nick, "\u0002{0}\u0002 is currently not playing.".format(victim)) + pm(cli, + nick, + "\u0002{0}\u0002 is currently not playing.".format(victim)) return - + victim = pl[pll.index(target)] if victim == nick: pm(cli, nick, "Suicide is bad. Don't do it.") return - if victim in var.ROLES["wolf"]+var.ROLES["werecrow"]+var.ROLES["traitor"]: + if victim in var.ROLES[ + "wolf"] + var.ROLES["werecrow"] + var.ROLES["traitor"]: pm(cli, nick, "You may only kill villagers, not other wolves.") return var.KILLS[nick] = victim - pm(cli, nick, "You have selected \u0002{0}\u0002 to be killed.".format(victim)) + pm(cli, + nick, + "You have selected \u0002{0}\u0002 to be killed.".format(victim)) var.LOGGER.logBare(nick, "SELECT", victim) chk_nightdone(cli) @@ -1924,13 +2113,13 @@ def guard(cli, nick, rest): if var.PHASE != "night": pm(cli, nick, "You may only protect people at night.") return - victim = re.split(" +",rest)[0].strip().lower() + victim = re.split(" +", rest)[0].strip().lower() if not victim: pm(cli, nick, "Not enough parameters") return if var.GUARDED.get(nick): - pm(cli, nick, ("You are already protecting "+ - "\u0002{0}\u0002.").format(var.GUARDED[nick])) + pm(cli, nick, ("You are already protecting " + + "\u0002{0}\u0002.").format(var.GUARDED[nick])) return pl = var.list_players() pll = [x.lower() for x in pl] @@ -1944,7 +2133,9 @@ def guard(cli, nick, rest): matches += 1 else: if matches != 1: - pm(cli, nick, "\u0002{0}\u0002 is currently not playing.".format(victim)) + pm(cli, + nick, + "\u0002{0}\u0002 is currently not playing.".format(victim)) return victim = pl[pll.index(target)] if victim == nick: @@ -1952,13 +2143,15 @@ def guard(cli, nick, rest): pm(cli, nick, "You have chosen not to guard anyone tonight.") else: var.GUARDED[nick] = victim - pm(cli, nick, "You are protecting \u0002{0}\u0002 tonight. Farewell!".format(var.GUARDED[nick])) - pm(cli, var.GUARDED[nick], "You can sleep well tonight, for a guardian angel is protecting you.") + pm(cli, nick, "You are protecting \u0002{0}\u0002 tonight. Farewell!".format( + var.GUARDED[nick])) + pm(cli, + var.GUARDED[nick], + "You can sleep well tonight, for a guardian angel is protecting you.") var.LOGGER.logBare(var.GUARDED[nick], "GUARDED", nick) chk_nightdone(cli) - @pmcmd("observe") def observe(cli, nick, rest): if var.PHASE in ("none", "join"): @@ -1989,14 +2182,19 @@ def observe(cli, nick, rest): matches += 1 else: if matches != 1: - pm(cli, nick,"\u0002{0}\u0002 is currently not playing.".format(victim)) + pm(cli, + nick, + "\u0002{0}\u0002 is currently not playing.".format(victim)) return victim = pl[pll.index(target)] if victim == nick.lower(): - pm(cli, nick, "Instead of doing that, you should probably go kill someone.") + pm(cli, + nick, + "Instead of doing that, you should probably go kill someone.") return if nick in var.OBSERVED.keys(): - pm(cli, nick, "You are already flying to \02{0}\02's house.".format(var.OBSERVED[nick])) + pm(cli, nick, "You are already flying to \02{0}\02's house.".format( + var.OBSERVED[nick])) return if var.get_role(victim) in ("werecrow", "traitor", "wolf"): pm(cli, nick, "Flying to another wolf's house is a waste of time.") @@ -2004,9 +2202,10 @@ def observe(cli, nick, rest): var.OBSERVED[nick] = victim if nick in var.KILLS.keys(): del var.KILLS[nick] - pm(cli, nick, ("You transform into a large crow and start your flight "+ - "to \u0002{0}'s\u0002 house. You will return after "+ - "collecting your observations when day begins.").format(victim)) + pm(cli, nick, + ("You transform into a large crow and start your flight " + + "to \u0002{0}'s\u0002 house. You will return after " + + "collecting your observations when day begins.").format(victim)) var.LOGGER.logBare(victim, "OBSERVED", nick) chk_nightdone(cli) @@ -2044,7 +2243,9 @@ def investigate(cli, nick, rest): matches += 1 else: if matches != 1: - pm(cli, nick,"\u0002{0}\u0002 is currently not playing.".format(victim)) + pm(cli, + nick, + "\u0002{0}\u0002 is currently not playing.".format(victim)) return victim = pl[pll.index(target)] if victim == nick: @@ -2052,18 +2253,20 @@ def investigate(cli, nick, rest): return var.INVESTIGATED.append(nick) - pm(cli, nick, ("The results of your investigation have returned. \u0002{0}\u0002"+ - " is a... \u0002{1}\u0002!").format(victim, var.get_role(victim))) + pm(cli, nick, + ("The results of your investigation have returned. \u0002{0}\u0002" + + " is a... \u0002{1}\u0002!").format(victim, var.get_role(victim))) var.LOGGER.logBare(victim, "INVESTIGATED", nick) - if random.random() < var.DETECTIVE_REVEALED_CHANCE: # a 2/5 chance (should be changeable in settings) + # a 2/5 chance (should be changeable in settings) + if random.random() < var.DETECTIVE_REVEALED_CHANCE: # The detective's identity is compromised! for badguy in var.ROLES["wolf"] + var.ROLES["werecrow"] + var.ROLES["traitor"]: - pm(cli, badguy, ("Someone accidentally drops a paper. The paper reveals "+ - "that \u0002{0}\u0002 is the detective!").format(nick)) + pm(cli, badguy, + ("Someone accidentally drops a paper. The paper reveals " + + "that \u0002{0}\u0002 is the detective!").format(nick)) var.LOGGER.logBare(nick, "PAPERDROP") - @pmcmd("visit") def hvisit(cli, nick, rest): if var.PHASE in ("none", "join"): @@ -2079,10 +2282,10 @@ def hvisit(cli, nick, rest): pm(cli, nick, "You may only visit someone at night.") return if var.HVISITED.get(nick): - pm(cli, nick, ("You are already spending the night "+ - "with \u0002{0}\u0002.").format(var.HVISITED[nick])) + pm(cli, nick, ("You are already spending the night " + + "with \u0002{0}\u0002.").format(var.HVISITED[nick])) return - victim = re.split(" +",rest)[0].strip().lower() + victim = re.split(" +", rest)[0].strip().lower() if not victim: pm(cli, nick, "Not enough parameters") return @@ -2097,7 +2300,9 @@ def hvisit(cli, nick, rest): matches += 1 else: if matches != 1: - pm(cli, nick,"\u0002{0}\u0002 is currently not playing.".format(victim)) + pm(cli, + nick, + "\u0002{0}\u0002 is currently not playing.".format(victim)) return victim = var.list_players()[pll.index(target)] if nick == victim: # Staying home @@ -2105,17 +2310,20 @@ def hvisit(cli, nick, rest): pm(cli, nick, "You have chosen to stay home for the night.") else: var.HVISITED[nick] = victim - pm(cli, nick, ("You are spending the night with \u0002{0}\u0002. "+ - "Have a good time!").format(var.HVISITED[nick])) - pm(cli, var.HVISITED[nick], ("You are spending the night with \u0002{0}"+ - "\u0002. Have a good time!").format(nick)) + pm(cli, nick, ("You are spending the night with \u0002{0}\u0002. " + + "Have a good time!").format(var.HVISITED[nick])) + pm(cli, var.HVISITED[nick], + ("You are spending the night with \u0002{0}" + + "\u0002. Have a good time!").format(nick)) var.LOGGER.logBare(var.HVISITED[nick], "VISITED", nick) chk_nightdone(cli) def is_fake_nick(who): - return not(re.search("^[a-zA-Z\\\_\]\[`]([a-zA-Z0-9\\\_\]\[`]+)?", who)) or who.lower().endswith("serv") - + return not( + re.search( + "^[a-zA-Z\\\_\]\[`]([a-zA-Z0-9\\\_\]\[`]+)?", + who)) or who.lower().endswith("serv") @pmcmd("see") @@ -2135,7 +2343,7 @@ def see(cli, nick, rest): if nick in var.SEEN: pm(cli, nick, "You may only have one vision per round.") return - victim = re.split(" +",rest)[0].strip().lower() + victim = re.split(" +", rest)[0].strip().lower() pl = var.list_players() pll = [x.lower() for x in pl] if not victim: @@ -2151,7 +2359,9 @@ def see(cli, nick, rest): matches += 1 else: if matches != 1: - pm(cli, nick,"\u0002{0}\u0002 is currently not playing.".format(victim)) + pm(cli, + nick, + "\u0002{0}\u0002 is currently not playing.".format(victim)) return victim = pl[pll.index(target)] if victim == nick: @@ -2163,24 +2373,23 @@ def see(cli, nick, rest): role = "villager" else: role = var.get_role(victim) - pm(cli, nick, ("You have a vision; in this vision, "+ - "you see that \u0002{0}\u0002 is a "+ - "\u0002{1}\u0002!").format(victim, role)) + pm(cli, nick, ("You have a vision; in this vision, " + + "you see that \u0002{0}\u0002 is a " + + "\u0002{1}\u0002!").format(victim, role)) var.SEEN.append(nick) var.LOGGER.logBare(victim, "SEEN", nick) chk_nightdone(cli) - @hook("featurelist") # For multiple targets with PRIVMSG def getfeatures(cli, nick, *rest): for r in rest: if r.startswith("TARGMAX="): x = r[r.index("PRIVMSG:"):] if "," in x: - l = x[x.index(":")+1:x.index(",")] + l = x[x.index(":") + 1:x.index(",")] else: - l = x[x.index(":")+1:] + l = x[x.index(":") + 1:] l = l.strip() if not l or not l.isdigit(): continue @@ -2189,8 +2398,7 @@ def getfeatures(cli, nick, *rest): break - -def mass_privmsg(cli, targets, msg, notice = False): +def mass_privmsg(cli, targets, msg, notice=False): while targets: if len(targets) <= var.MAX_PRIVMSG_TARGETS: bgs = ",".join(targets) @@ -2202,8 +2410,7 @@ def mass_privmsg(cli, targets, msg, notice = False): cli.msg(bgs, msg) else: cli.notice(bgs, msg) - - + @pmcmd("") def relay(cli, nick, rest): @@ -2214,29 +2421,41 @@ def relay(cli, nick, rest): badguys = var.ROLES["wolf"] + var.ROLES["traitor"] + var.ROLES["werecrow"] if len(badguys) > 1: if nick in badguys: - badguys.remove(nick) # remove self from list - + badguys.remove(nick) # remove self from list + if rest.startswith("\01ACTION"): rest = rest[7:-1] - mass_privmsg(cli, [guy for guy in badguys - if (guy in var.PLAYERS and - var.PLAYERS[guy]["cloak"] not in var.SIMPLE_NOTIFY)], "\02{0}\02{1}".format(nick, rest)) - mass_privmsg(cli, [guy for guy in badguys - if (guy in var.PLAYERS and - var.PLAYERS[guy]["cloak"] in var.SIMPLE_NOTIFY)], nick+rest, True) + mass_privmsg( + cli, [ + guy for guy in badguys if ( + guy in var.PLAYERS and var.PLAYERS[guy]["cloak"] not in var.SIMPLE_NOTIFY)], "\02{0}\02{1}".format( + nick, rest)) + mass_privmsg( + cli, [ + guy for guy in badguys if ( + guy in var.PLAYERS and var.PLAYERS[guy]["cloak"] in var.SIMPLE_NOTIFY)], nick + rest, True) else: - mass_privmsg(cli, [guy for guy in badguys - if (guy in var.PLAYERS and - var.PLAYERS[guy]["cloak"] not in var.SIMPLE_NOTIFY)], "\02{0}\02 says: {1}".format(nick, rest)) - mass_privmsg(cli, [guy for guy in badguys - if (guy in var.PLAYERS and - var.PLAYERS[guy]["cloak"] in var.SIMPLE_NOTIFY)], "\02{0}\02 says: {1}".format(nick, rest), True) + mass_privmsg( + cli, + [ + guy for guy in badguys if ( + guy in var.PLAYERS and var.PLAYERS[guy]["cloak"] not in var.SIMPLE_NOTIFY)], + "\02{0}\02 says: {1}".format( + nick, + rest)) + mass_privmsg( + cli, [ + guy for guy in badguys if ( + guy in var.PLAYERS and var.PLAYERS[guy]["cloak"] in var.SIMPLE_NOTIFY)], "\02{0}\02 says: {1}".format( + nick, rest), True) + @pmcmd("") def ctcp_ping(cli, nick, msg): if msg.startswith("\x01PING"): cli.notice(nick, msg) + def transition_night(cli): if var.PHASE == "night": return @@ -2257,55 +2476,74 @@ def transition_night(cli): daydur_msg = "" - if var.NIGHT_TIMEDELTA or var.START_WITH_DAY: # transition from day + if var.NIGHT_TIMEDELTA or var.START_WITH_DAY: # transition from day td = var.NIGHT_START_TIME - var.DAY_START_TIME var.DAY_START_TIME = None var.DAY_TIMEDELTA += td min, sec = td.seconds // 60, td.seconds % 60 - daydur_msg = "Day lasted \u0002{0:0>2}:{1:0>2}\u0002. ".format(min,sec) + daydur_msg = "Day lasted \u0002{0:0>2}:{1:0>2}\u0002. ".format( + min, + sec) chan = botconfig.CHANNEL if var.NIGHT_TIME_LIMIT > 0: var.NIGHT_ID = time.time() - t = threading.Timer(var.NIGHT_TIME_LIMIT, transition_day, [cli, var.NIGHT_ID]) + t = threading.Timer( + var.NIGHT_TIME_LIMIT, transition_day, [ + cli, var.NIGHT_ID]) var.TIMERS["night"] = t var.TIMERS["night"].daemon = True t.start() - + if var.NIGHT_TIME_WARN > 0: - t2 = threading.Timer(var.NIGHT_TIME_WARN, night_warn, [cli, var.NIGHT_ID]) + t2 = threading.Timer( + var.NIGHT_TIME_WARN, night_warn, [ + cli, var.NIGHT_ID]) var.TIMERS["night_warn"] = t2 var.TIMERS["night_warn"].daemon = True t2.start() # send PMs ps = var.list_players() - wolves = var.ROLES["wolf"]+var.ROLES["traitor"]+var.ROLES["werecrow"] + wolves = var.ROLES["wolf"] + var.ROLES["traitor"] + var.ROLES["werecrow"] for wolf in wolves: - normal_notify = wolf in var.PLAYERS and var.PLAYERS[wolf]["cloak"] not in var.SIMPLE_NOTIFY - + normal_notify = wolf in var.PLAYERS and var.PLAYERS[ + wolf]["cloak"] not in var.SIMPLE_NOTIFY + if normal_notify: if wolf in var.ROLES["wolf"]: - pm(cli, wolf, ('You are a \u0002wolf\u0002. It is your job to kill all the '+ - 'villagers. Use "kill " to kill a villager.')) + pm( + cli, + wolf, + ('You are a \u0002wolf\u0002. It is your job to kill all the ' + + 'villagers. Use "kill " to kill a villager.')) elif wolf in var.ROLES["traitor"]: - pm(cli, wolf, (('You are a \u0002{0}\u0002. You are exactly like a '+ - 'villager and not even a seer can see your true identity. '+ - 'Only detectives can. ').format( - "cursed traitor" if wolf in var.CURSED else "traitor"))) + pm( + cli, + wolf, + (('You are a \u0002{0}\u0002. You are exactly like a ' + + 'villager and not even a seer can see your true identity. ' + + 'Only detectives can. ').format( + "cursed traitor" if wolf in var.CURSED else "traitor"))) else: - pm(cli, wolf, ('You are a \u0002werecrow\u0002. You are able to fly at night. '+ - 'Use "kill " to kill a a villager. Alternatively, you can '+ - 'use "observe " to check if someone is in bed or not. '+ - 'Observing will prevent you from participating in a killing.')) + pm( + cli, + wolf, + ('You are a \u0002werecrow\u0002. You are able to fly at night. ' + + 'Use "kill " to kill a a villager. Alternatively, you can ' + + 'use "observe " to check if someone is in bed or not. ' + + 'Observing will prevent you from participating in a killing.')) if len(wolves) > 1: - pm(cli, wolf, 'Also, if you PM me, your message will be relayed to other wolves.') + pm(cli, + wolf, + 'Also, if you PM me, your message will be relayed to other wolves.') else: role = var.get_role(wolf) - pm(cli, wolf, "You are a \02{0}\02.".format("cursed traitor" if role == "traitor" and wolf in var.CURSED else role)) # !simple - - + pm(cli, wolf, "You are a \02{0}\02.".format( + "cursed traitor" + if role == "traitor" and wolf in var.CURSED else role)) # !simple + pl = ps[:] random.shuffle(pl) pl.remove(wolf) # remove self from list @@ -2322,66 +2560,80 @@ def transition_night(cli): elif player in var.CURSED: pl[i] = player + " (cursed)" - pm(cli, wolf, "Players: "+", ".join(pl)) + pm(cli, wolf, "Players: " + ", ".join(pl)) for seer in var.ROLES["seer"]: pl = ps[:] random.shuffle(pl) pl.remove(seer) # remove self from list - - if seer in var.PLAYERS and var.PLAYERS[seer]["cloak"] not in var.SIMPLE_NOTIFY: - pm(cli, seer, ('You are a \u0002seer\u0002. '+ - 'It is your job to detect the wolves, you '+ - 'may have a vision once per night. '+ - 'Use "see " to see the role of a player.')) + + if seer in var.PLAYERS and var.PLAYERS[ + seer]["cloak"] not in var.SIMPLE_NOTIFY: + pm(cli, seer, ('You are a \u0002seer\u0002. ' + + 'It is your job to detect the wolves, you ' + + 'may have a vision once per night. ' + + 'Use "see " to see the role of a player.')) else: pm(cli, seer, "You are a \02seer\02.") # !simple - pm(cli, seer, "Players: "+", ".join(pl)) + pm(cli, seer, "Players: " + ", ".join(pl)) for harlot in var.ROLES["harlot"]: pl = ps[:] random.shuffle(pl) pl.remove(harlot) - if harlot in var.PLAYERS and var.PLAYERS[harlot]["cloak"] not in var.SIMPLE_NOTIFY: - cli.msg(harlot, ('You are a \u0002harlot\u0002. '+ - 'You may spend the night with one person per round. '+ - 'If you visit a victim of a wolf, or visit a wolf, '+ - 'you will die. Use visit to visit a player.')) + if harlot in var.PLAYERS and var.PLAYERS[ + harlot]["cloak"] not in var.SIMPLE_NOTIFY: + cli.msg( + harlot, + ('You are a \u0002harlot\u0002. ' + + 'You may spend the night with one person per round. ' + + 'If you visit a victim of a wolf, or visit a wolf, ' + + 'you will die. Use visit to visit a player.')) else: cli.notice(harlot, "You are a \02harlot\02.") # !simple - pm(cli, harlot, "Players: "+", ".join(pl)) + pm(cli, harlot, "Players: " + ", ".join(pl)) for g_angel in var.ROLES["guardian angel"]: pl = ps[:] random.shuffle(pl) pl.remove(g_angel) - if g_angel in var.PLAYERS and var.PLAYERS[g_angel]["cloak"] not in var.SIMPLE_NOTIFY: - cli.msg(g_angel, ('You are a \u0002guardian angel\u0002. '+ - 'It is your job to protect the villagers. If you guard a'+ - ' wolf, there is a 50/50 chance of you dying, if you guard '+ - 'a victim, they will live. Use guard to guard a player.')) + if g_angel in var.PLAYERS and var.PLAYERS[ + g_angel]["cloak"] not in var.SIMPLE_NOTIFY: + cli.msg( + g_angel, + ('You are a \u0002guardian angel\u0002. ' + + 'It is your job to protect the villagers. If you guard a' + + ' wolf, there is a 50/50 chance of you dying, if you guard ' + + 'a victim, they will live. Use guard to guard a player.')) else: cli.notice(g_angel, "You are a \02guardian angel\02.") # !simple pm(cli, g_angel, "Players: " + ", ".join(pl)) - + for dttv in var.ROLES["detective"]: pl = ps[:] random.shuffle(pl) pl.remove(dttv) - if dttv in var.PLAYERS and var.PLAYERS[dttv]["cloak"] not in var.SIMPLE_NOTIFY: - cli.msg(dttv, ("You are a \u0002detective\u0002.\n"+ - "It is your job to determine all the wolves and traitors. "+ - "Your job is during the day, and you can see the true "+ - "identity of all users, even traitors.\n"+ - "But, each time you use your ability, you risk a 2/5 "+ - "chance of having your identity revealed to the wolves. So be "+ - "careful. Use \"{0}id\" to identify any player during the day.").format(botconfig.CMD_CHAR)) + if dttv in var.PLAYERS and var.PLAYERS[ + dttv]["cloak"] not in var.SIMPLE_NOTIFY: + cli.msg( + dttv, + ("You are a \u0002detective\u0002.\n" + + "It is your job to determine all the wolves and traitors. " + + "Your job is during the day, and you can see the true " + + "identity of all users, even traitors.\n" + + "But, each time you use your ability, you risk a 2/5 " + + "chance of having your identity revealed to the wolves. So be " + + "careful. Use \"{0}id\" to identify any player during the day.").format( + botconfig.CMD_CHAR)) else: cli.notice(dttv, "You are a \02detective\02.") # !simple pm(cli, dttv, "Players: " + ", ".join(pl)) for drunk in var.ROLES["village drunk"]: - if drunk in var.PLAYERS and var.PLAYERS[drunk]["cloak"] not in var.SIMPLE_NOTIFY: - cli.msg(drunk, "You have been drinking too much! You are the \u0002village drunk\u0002.") + if drunk in var.PLAYERS and var.PLAYERS[ + drunk]["cloak"] not in var.SIMPLE_NOTIFY: + cli.msg( + drunk, + "You have been drinking too much! You are the \u0002village drunk\u0002.") else: cli.notice(drunk, "You are the \u0002village drunk\u0002.") @@ -2390,11 +2642,13 @@ def transition_night(cli): continue elif not var.GUNNERS[g]: continue - norm_notify = g in var.PLAYERS and var.PLAYERS[g]["cloak"] not in var.SIMPLE_NOTIFY + norm_notify = g in var.PLAYERS and var.PLAYERS[ + g]["cloak"] not in var.SIMPLE_NOTIFY if norm_notify: - gun_msg = ("You hold a gun that shoots special silver bullets. You may only use it "+ - "during the day. Wolves and the crow will die instantly when shot, but "+ - "a villager or traitor will likely survive. You get {0}.") + gun_msg = ( + "You hold a gun that shoots special silver bullets. You may only use it " + + "during the day. Wolves and the crow will die instantly when shot, but " + + "a villager or traitor will likely survive. You get {0}.") else: gun_msg = ("You have a \02gun\02 with {0}.") if var.GUNNERS[g] == 1: @@ -2403,36 +2657,36 @@ def transition_night(cli): gun_msg = gun_msg.format(str(var.GUNNERS[g]) + " bullets") else: continue - + pm(cli, g, gun_msg) - dmsg = (daydur_msg + "It is now nighttime. All players "+ - "check for PMs from me for instructions. "+ - "If you did not receive one, simply sit back, "+ - "relax, and wait patiently for morning.") + dmsg = (daydur_msg + "It is now nighttime. All players " + + "check for PMs from me for instructions. " + + "If you did not receive one, simply sit back, " + + "relax, and wait patiently for morning.") cli.msg(chan, dmsg) var.LOGGER.logMessage(dmsg.replace("\02", "")) var.LOGGER.logBare("NIGHT", "BEGIN") # cli.msg(chan, "DEBUG: "+str(var.ROLES)) - if not var.ROLES["wolf"] + var.ROLES["werecrow"]: # Probably something interesting going on. + # Probably something interesting going on. + if not var.ROLES["wolf"] + var.ROLES["werecrow"]: chk_nightdone(cli) chk_traitor(cli) - def cgamemode(cli, *args): chan = botconfig.CHANNEL if var.ORIGINAL_SETTINGS: # needs reset reset_settings() - + for arg in args: modeargs = arg.split("=", 1) - + if len(modeargs) < 2: # no equal sign in the middle of the arg cli.msg(botconfig.CHANNEL, "Invalid syntax.") return False - + modeargs[0] = modeargs[0].strip() if modeargs[0] in var.GAME_MODES.keys(): md = modeargs.pop(0) @@ -2442,23 +2696,26 @@ def cgamemode(cli, *args): for attr in dir(gm): val = getattr(gm, attr) if (hasattr(var, attr) and not callable(val) - and not attr.startswith("_")): + and not attr.startswith("_")): var.ORIGINAL_SETTINGS[attr] = getattr(var, attr) setattr(var, attr, val) return True except var.InvalidModeException as e: - cli.msg(botconfig.CHANNEL, "Invalid mode: "+str(e)) + cli.msg(botconfig.CHANNEL, "Invalid mode: " + str(e)) return False else: - cli.msg(chan, "Mode \u0002{0}\u0002 not found.".format(modeargs[0])) + cli.msg( + chan, + "Mode \u0002{0}\u0002 not found.".format( + modeargs[0])) @cmd("start") def start(cli, nick, chann_, rest): """Starts a game of Werewolf""" - + chan = botconfig.CHANNEL - + villagers = var.list_players() pl = villagers[:] @@ -2471,7 +2728,7 @@ def start(cli, nick, chann_, rest): if nick not in villagers and nick != chan: cli.notice(nick, "You're currently not playing.") return - + now = datetime.now() var.GAME_START_TIME = now # Only used for the idler checker dur = int((var.CAN_START_TIME - now).total_seconds()) @@ -2480,7 +2737,11 @@ def start(cli, nick, chann_, rest): return if len(villagers) < var.MIN_PLAYERS: - cli.msg(chan, "{0}: \u0002{1}\u0002 or more players are required to play.".format(nick, var.MIN_PLAYERS)) + cli.msg( + chan, + "{0}: \u0002{1}\u0002 or more players are required to play.".format( + nick, + var.MIN_PLAYERS)) return for pcount in range(len(villagers), var.MIN_PLAYERS - 1, -1): @@ -2488,21 +2749,29 @@ def start(cli, nick, chann_, rest): if addroles: break else: - cli.msg(chan, "{0}: No game settings are defined for \u0002{1}\u0002 player games.".format(nick, len(villagers))) + cli.msg( + chan, + "{0}: No game settings are defined for \u0002{1}\u0002 player games.".format( + nick, + len(villagers))) return # Cancel join timer if 'join' in var.TIMERS: var.TIMERS['join'].cancel() del var.TIMERS['join'] - + if var.ORIGINAL_SETTINGS: # Custom settings while True: wvs = (addroles[var.INDEX_OF_ROLE["wolf"]] + - addroles[var.INDEX_OF_ROLE["traitor"]]) - if len(villagers) < (sum(addroles) - addroles[var.INDEX_OF_ROLE["gunner"]] - - addroles[var.INDEX_OF_ROLE["cursed villager"]]): - cli.msg(chan, "There are too few players in the "+ + addroles[var.INDEX_OF_ROLE["traitor"]]) + if len(villagers) < ( + sum(addroles) - + addroles[ + var.INDEX_OF_ROLE["gunner"]] - + addroles[ + var.INDEX_OF_ROLE["cursed villager"]]): + cli.msg(chan, "There are too few players in the " + "game to use the custom roles.") elif not wvs: cli.msg(chan, "There has to be at least one wolf!") @@ -2511,16 +2780,25 @@ def start(cli, nick, chann_, rest): else: break reset_settings() - cli.msg(chan, "The default settings have been restored. Please !start again.") + cli.msg( + chan, + "The default settings have been restored. Please !start again.") var.PHASE = "join" return - if var.ADMIN_TO_PING: if "join" in COMMANDS.keys(): - COMMANDS["join"] = [lambda *spam: cli.msg(chan, "This command has been disabled by an admin.")] + COMMANDS["join"] = [ + lambda * + spam: cli.msg( + chan, + "This command has been disabled by an admin.")] if "start" in COMMANDS.keys(): - COMMANDS["start"] = [lambda *spam: cli.msg(chan, "This command has been disabled by an admin.")] + COMMANDS["start"] = [ + lambda * + spam: cli.msg( + chan, + "This command has been disabled by an admin.")] var.ROLES = {} var.CURSED = [] @@ -2537,7 +2815,7 @@ def start(cli, nick, chann_, rest): role = var.ROLE_INDICES[i] if role in villager_roles: var.ROLES[role] = [None] * count - continue # We deal with those later, see below + continue # We deal with those later, see below selected = random.sample(villagers, count) var.ROLES[role] = selected for x in selected: @@ -2551,25 +2829,27 @@ def start(cli, nick, chann_, rest): var.ROLES["seer"]): # traitor can be cursed possiblecursed.remove(cannotbe) - - var.CURSED = random.sample(possiblecursed, len(var.ROLES["cursed villager"])) + + var.CURSED = random.sample( + possiblecursed, len( + var.ROLES["cursed villager"])) del var.ROLES["cursed villager"] - + # Select gunner (also a villager) if var.ROLES["gunner"]: - + possible = pl[:] for cannotbe in (var.ROLES["wolf"] + var.ROLES["werecrow"] + var.ROLES["traitor"]): possible.remove(cannotbe) - + for csd in var.CURSED: # cursed cannot be gunner if csd in possible: possible.remove(csd) - + for gnr in random.sample(possible, len(var.ROLES["gunner"])): if gnr in var.ROLES["village drunk"]: - var.GUNNERS[gnr] = (var.DRUNK_SHOTS_MULTIPLIER * + var.GUNNERS[gnr] = (var.DRUNK_SHOTS_MULTIPLIER * math.ceil(var.SHOTS_MULTIPLIER * len(pl))) else: var.GUNNERS[gnr] = math.ceil(var.SHOTS_MULTIPLIER * len(pl)) @@ -2577,27 +2857,30 @@ def start(cli, nick, chann_, rest): var.SPECIAL_ROLES["goat herder"] = [] if var.GOAT_HERDER: - var.SPECIAL_ROLES["goat herder"] = [ nick ] + var.SPECIAL_ROLES["goat herder"] = [nick] var.ROLES["villager"] = villagers - cli.msg(chan, ("{0}: Welcome to Werewolf, the popular detective/social party "+ - "game (a theme of Mafia).").format(", ".join(pl))) + cli.msg( + chan, + ("{0}: Welcome to Werewolf, the popular detective/social party " + + "game (a theme of Mafia).").format( + ", ".join(pl))) cli.mode(chan, "+m") var.ORIGINAL_ROLES = copy.deepcopy(var.ROLES) # Make a copy - + var.DAY_TIMEDELTA = timedelta(0) var.NIGHT_TIMEDELTA = timedelta(0) var.DAY_START_TIME = datetime.now() var.NIGHT_START_TIME = datetime.now() var.LAST_PING = None - + var.LOGGER.log("Game Start") var.LOGGER.logBare("GAME", "BEGIN", nick) var.LOGGER.logBare(str(len(pl)), "PLAYERCOUNT") - + var.LOGGER.log("***") var.LOGGER.log("ROLES: ") for rol in var.ROLES: @@ -2609,23 +2892,27 @@ def start(cli, nick, chann_, rest): r.append(rwu) r = " ".join(r) var.LOGGER.log("{0}: {1}".format(r, ", ".join(var.ROLES[rol]))) - + for plr in var.ROLES[rol]: var.LOGGER.logBare(plr, "ROLE", rol) - + if var.CURSED: - var.LOGGER.log("Cursed Villagers: "+", ".join(var.CURSED)) - + var.LOGGER.log("Cursed Villagers: " + ", ".join(var.CURSED)) + for plr in var.CURSED: - var.LOGGER.logBare(plr+" ROLE cursed villager") + var.LOGGER.logBare(plr + " ROLE cursed villager") if var.GUNNERS: - var.LOGGER.log("Villagers With Bullets: "+", ".join([x+"("+str(y)+")" for x,y in var.GUNNERS.items()])) + var.LOGGER.log("Villagers With Bullets: " + + ", ".join([x + + "(" + + str(y) + + ")" for x, y in var.GUNNERS.items()])) for plr in var.GUNNERS: var.LOGGER.logBare(plr, "ROLE gunner") - - var.LOGGER.log("***") - - var.PLAYERS = {plr:dict(var.USERS[plr]) for plr in pl if plr in var.USERS} + + var.LOGGER.log("***") + + var.PLAYERS = {plr: dict(var.USERS[plr]) for plr in pl if plr in var.USERS} if not var.START_WITH_DAY: var.FIRST_NIGHT = True @@ -2639,12 +2926,11 @@ def start(cli, nick, chann_, rest): del var.STASISED[cloak] # DEATH TO IDLERS! - reapertimer = threading.Thread(None, reaper, args=(cli,var.GAME_ID)) + reapertimer = threading.Thread(None, reaper, args=(cli, var.GAME_ID)) reapertimer.daemon = True reapertimer.start() - - + @hook("error") def on_error(cli, pfx, msg): if msg.endswith("(Excess Flood)"): @@ -2652,6 +2938,7 @@ def on_error(cli, pfx, msg): elif msg.startswith("Closing Link:"): raise SystemExit + @cmd("fstasis", admin_only=True) def fstasis(cli, nick, chan, rest): """Admin command for removing or setting stasis penalties.""" @@ -2660,11 +2947,11 @@ def fstasis(cli, nick, chan, rest): if data: lusers = {k.lower(): v for k, v in var.USERS.items()} user = data[0].lower() - - #if user not in lusers: + + # if user not in lusers: # pm(cli, nick, "Sorry, {0} cannot be found.".format(data[0])) # return - + if user in lusers: cloak = lusers[user]['cloak'] else: @@ -2672,7 +2959,10 @@ def fstasis(cli, nick, chan, rest): if len(data) == 1: if cloak in var.STASISED: - msg = "{0} ({1}) is in stasis for {2} games.".format(data[0], cloak, var.STASISED[cloak]) + msg = "{0} ({1}) is in stasis for {2} games.".format( + data[0], + cloak, + var.STASISED[cloak]) else: msg = "{0} ({1}) is not in stasis.".format(data[0], cloak) else: @@ -2681,14 +2971,18 @@ def fstasis(cli, nick, chan, rest): except ValueError: pm(cli, nick, "Sorry, invalid integer argument.") return - + if amt > 0: var.STASISED[cloak] = amt - msg = "{0} ({1}) is now in stasis for {2} games.".format(data[0], cloak, amt) + msg = "{0} ({1}) is now in stasis for {2} games.".format( + data[0], + cloak, amt) else: if cloak in var.STASISED: del var.STASISED[cloak] - msg = "{0} ({1}) is no longer in stasis.".format(data[0], cloak) + msg = "{0} ({1}) is no longer in stasis.".format( + data[0], + cloak) else: msg = "{0} ({1}) is not in stasis.".format(data[0], cloak) else: @@ -2705,20 +2999,19 @@ def fstasis(cli, nick, chan, rest): else: cli.msg(chan, msg) + @pmcmd("fstasis", admin_only=True) def fstasis_pm(cli, nick, rest): fstasis(cli, nick, nick, rest) - @cmd("wait", "w") def wait(cli, nick, chann_, rest): """Increase the wait time (before !start can be used)""" pl = var.list_players() - + chan = botconfig.CHANNEL - - + if var.PHASE == "none": cli.notice(nick, "No game is currently running.") return @@ -2729,7 +3022,9 @@ def wait(cli, nick, chann_, rest): cli.notice(nick, "You're currently not playing.") return if var.WAITED >= var.MAXIMUM_WAITED: - cli.msg(chan, "Limit has already been reached for extending the wait time.") + cli.msg( + chan, + "Limit has already been reached for extending the wait time.") return now = datetime.now() @@ -2738,19 +3033,17 @@ def wait(cli, nick, chann_, rest): else: var.CAN_START_TIME += timedelta(seconds=var.EXTRA_WAIT) var.WAITED += 1 - cli.msg(chan, ("\u0002{0}\u0002 increased the wait time by "+ - "{1} seconds.").format(nick, var.EXTRA_WAIT)) - + cli.msg(chan, ("\u0002{0}\u0002 increased the wait time by " + + "{1} seconds.").format(nick, var.EXTRA_WAIT)) @cmd("fwait", admin_only=True) def fwait(cli, nick, chann_, rest): pl = var.list_players() - + chan = botconfig.CHANNEL - - + if var.PHASE == "none": cli.notice(nick, "No game is currently running.") return @@ -2767,23 +3060,25 @@ def fwait(cli, nick, chann_, rest): return else: extra = var.EXTRA_WAIT - + now = datetime.now() if now > var.CAN_START_TIME: var.CAN_START_TIME = now + timedelta(seconds=extra) else: var.CAN_START_TIME += timedelta(seconds=extra) var.WAITED += 1 - cli.msg(chan, ("\u0002{0}\u0002 forcibly increased the wait time by "+ - "{1} seconds.").format(nick, extra)) + cli.msg(chan, ("\u0002{0}\u0002 forcibly increased the wait time by " + + "{1} seconds.").format(nick, extra)) -@cmd("fstop",admin_only=True) +@cmd("fstop", admin_only=True) def reset_game(cli, nick, chan, rest): if var.PHASE == "none": cli.notice(nick, "No game is currently running.") return - cli.msg(botconfig.CHANNEL, "\u0002{0}\u0002 has forced the game to stop.".format(nick)) + cli.msg( + botconfig.CHANNEL, + "\u0002{0}\u0002 has forced the game to stop.".format(nick)) var.LOGGER.logMessage("{0} has forced the game to stop.".format(nick)) if var.PHASE != "join": stop_game(cli) @@ -2796,6 +3091,7 @@ def reset_game(cli, nick, chan, rest): def pm_rules(cli, nick, rest): cli.notice(nick, var.RULES) + @cmd("rules") def show_rules(cli, nick, chan, rest): """Displays the rules""" @@ -2806,7 +3102,7 @@ def show_rules(cli, nick, chan, rest): var.LOGGER.logMessage(var.RULES) -@pmcmd("help", raw_nick = True) +@pmcmd("help", raw_nick=True) def get_help(cli, rnick, rest): """Gets help.""" nick, mode, user, cloak = parse_nick(rnick) @@ -2818,19 +3114,32 @@ def get_help(cli, rnick, rest): rest = splitted[0] if splitted else "" found = False if cname: - for c in (COMMANDS,PM_COMMANDS): + for c in (COMMANDS, PM_COMMANDS): if cname in c.keys(): found = True for fn in c[cname]: if fn.__doc__: if callable(fn.__doc__): - pm(cli, nick, botconfig.CMD_CHAR+cname+": "+fn.__doc__(rest)) + pm(cli, nick, botconfig.CMD_CHAR + + cname + + ": " + + fn.__doc__(rest)) if nick == botconfig.CHANNEL: - var.LOGGER.logMessage(botconfig.CMD_CHAR+cname+": "+fn.__doc__(rest)) + var.LOGGER.logMessage( + botconfig.CMD_CHAR + + cname + + ": " + + fn.__doc__(rest)) else: - pm(cli, nick, botconfig.CMD_CHAR+cname+": "+fn.__doc__) + pm(cli, + nick, + botconfig.CMD_CHAR + cname + ": " + fn.__doc__) if nick == botconfig.CHANNEL: - var.LOGGER.logMessage(botconfig.CMD_CHAR+cname+": "+fn.__doc__) + var.LOGGER.logMessage( + botconfig.CMD_CHAR + + cname + + ": " + + fn.__doc__) return else: continue @@ -2840,38 +3149,42 @@ def get_help(cli, rnick, rest): if not found: pm(cli, nick, "Command not found.") else: - pm(cli, nick, "Documentation for this command is not available.") + pm(cli, + nick, + "Documentation for this command is not available.") return # if command was not found, or if no command was given: for name, fn in COMMANDS.items(): - if (name and not fn[0].admin_only and - not fn[0].owner_only and name not in fn[0].aliases): - fns.append("\u0002"+name+"\u0002") + if (name and not fn[0].admin_only and + not fn[0].owner_only and name not in fn[0].aliases): + fns.append("\u0002" + name + "\u0002") afns = [] - if is_admin(cloak) or cloak in botconfig.OWNERS: # todo - is_owner + if is_admin(cloak) or cloak in botconfig.OWNERS: # todo - is_owner for name, fn in COMMANDS.items(): if fn[0].admin_only and name not in fn[0].aliases: - afns.append("\u0002"+name+"\u0002") - cli.notice(nick, "Commands: "+", ".join(fns)) + afns.append("\u0002" + name + "\u0002") + cli.notice(nick, "Commands: " + ", ".join(fns)) if afns: - cli.notice(nick, "Admin Commands: "+", ".join(afns)) + cli.notice(nick, "Admin Commands: " + ", ".join(afns)) - -@cmd("help", raw_nick = True) +@cmd("help", raw_nick=True) def help2(cli, nick, chan, rest): """Gets help""" get_help(cli, nick, rest) -@hook("invite", raw_nick = False, admin_only = True) +@hook("invite", raw_nick=False, admin_only=True) def on_invite(cli, nick, something, chan): if chan == botconfig.CHANNEL: cli.join(chan) - + def is_admin(cloak): - return bool([ptn for ptn in botconfig.OWNERS+botconfig.ADMINS if fnmatch.fnmatch(cloak.lower(), ptn.lower())]) + return bool( + [ptn for ptn in botconfig.OWNERS + botconfig.ADMINS + if fnmatch.fnmatch(cloak.lower(), + ptn.lower())]) @cmd('admins', 'ops') @@ -2880,16 +3193,16 @@ def show_admins(cli, nick, chan, rest): admins = [] pl = var.list_players() - + if (chan != nick and var.LAST_ADMINS and var.LAST_ADMINS + timedelta(seconds=var.ADMINS_RATE_LIMIT) > datetime.now()): cli.notice(nick, ('This command is rate-limited. Please wait a while ' 'before using it again.')) return - + if chan != nick or (var.PHASE in ('day', 'night') or nick in pl): var.LAST_ADMINS = datetime.now() - + if var.ADMIN_PINGING: return @@ -2909,7 +3222,7 @@ def show_admins(cli, nick, chan, rest): return admins.sort(key=lambda x: x.lower()) - + if chan == nick: pm(cli, nick, 'Available admins: {}'.format(', '.join(admins))) elif var.PHASE in ('day', 'night') and nick not in pl: @@ -2928,19 +3241,18 @@ def show_admins_pm(cli, nick, rest): show_admins(cli, nick, nick, rest) - @cmd("coin") def coin(cli, nick, chan, rest): """It's a bad idea to base any decisions on this command.""" - + if var.PHASE in ("day", "night") and nick not in var.list_players(): cli.notice(nick, "You may not use this command right now.") return - + cli.msg(chan, "\2{0}\2 tosses a coin into the air...".format(nick)) var.LOGGER.logMessage("{0} tosses a coin into the air...".format(nick)) coin = random.choice(["heads", "tails"]) - specialty = random.randrange(0,10) + specialty = random.randrange(0, 10) if specialty == 0: coin = "its side" if specialty == 1: @@ -2949,6 +3261,7 @@ def coin(cli, nick, chan, rest): cli.msg(chan, cmsg) var.LOGGER.logMessage(cmsg) + @cmd("pony") def pony(cli, nick, chan, rest): """For entertaining bronies.""" @@ -2964,16 +3277,17 @@ def pony(cli, nick, chan, rest): cli.msg(chan, cmsg) var.LOGGER.logMessage(cmsg) + @cmd("time") def timeleft(cli, nick, chan, rest): """Returns the time left until the next day/night transition.""" - + if var.PHASE not in ("day", "night"): cli.notice(nick, "No game is currently running.") return - if (chan != nick and var.LAST_TIME and - var.LAST_TIME + timedelta(seconds=var.TIME_RATE_LIMIT) > datetime.now()): + if (chan != nick and var.LAST_TIME and var.LAST_TIME + + timedelta(seconds=var.TIME_RATE_LIMIT) > datetime.now()): cli.notice(nick, ("This command is rate-limited. Please wait a while " "before using it again.")) return @@ -2983,37 +3297,42 @@ def timeleft(cli, nick, chan, rest): if var.PHASE == "day": if var.STARTED_DAY_PLAYERS <= var.SHORT_DAY_PLAYERS: - remaining = int((var.SHORT_DAY_LIMIT_WARN + - var.SHORT_DAY_LIMIT_CHANGE) - (datetime.now() - - var.DAY_START_TIME).total_seconds()) + remaining = int( + (var.SHORT_DAY_LIMIT_WARN + var.SHORT_DAY_LIMIT_CHANGE) - + (datetime.now() - var.DAY_START_TIME).total_seconds()) else: - remaining = int((var.DAY_TIME_LIMIT_WARN + - var.DAY_TIME_LIMIT_CHANGE) - (datetime.now() - - var.DAY_START_TIME).total_seconds()) + remaining = int( + (var.DAY_TIME_LIMIT_WARN + var.DAY_TIME_LIMIT_CHANGE) - + (datetime.now() - var.DAY_START_TIME).total_seconds()) else: - remaining = int(var.NIGHT_TIME_LIMIT - (datetime.now() - - var.NIGHT_START_TIME).total_seconds()) - - #Check if timers are actually enabled - if (var.PHASE == "day") and ((var.STARTED_DAY_PLAYERS <= var.SHORT_DAY_PLAYERS and - var.SHORT_DAY_LIMIT_WARN == 0) or (var.DAY_TIME_LIMIT_WARN == 0 and - var.STARTED_DAY_PLAYERS > var.SHORT_DAY_PLAYERS)): + remaining = int( + var.NIGHT_TIME_LIMIT - (datetime.now() - var.NIGHT_START_TIME).total_seconds()) + + # Check if timers are actually enabled + if (var.PHASE == "day") and ((var.STARTED_DAY_PLAYERS <= var.SHORT_DAY_PLAYERS and + var.SHORT_DAY_LIMIT_WARN == 0) or (var.DAY_TIME_LIMIT_WARN == 0 and + var.STARTED_DAY_PLAYERS > var.SHORT_DAY_PLAYERS)): msg = "Day timers are currently disabled." elif var.PHASE == "night" and var.NIGHT_TIME_LIMIT == 0: msg = "Night timers are currently disabled." else: msg = "There is \x02{0[0]:0>2}:{0[1]:0>2}\x02 remaining until {1}.".format( - divmod(remaining, 60), "sunrise" if var.PHASE == "night" else "sunset") + divmod( + remaining, + 60), + "sunrise" if var.PHASE == "night" else "sunset") if nick == chan: pm(cli, nick, msg) else: cli.msg(chan, msg) + @pmcmd("time") def timeleft_pm(cli, nick, rest): timeleft(cli, nick, nick, rest) + @cmd("roles") def listroles(cli, nick, chan, rest): """Display which roles are enabled and when""" @@ -3026,7 +3345,7 @@ def listroles(cli, nick, chan, rest): if pl > 0: txt += '{0}: There are \u0002{1}\u0002 playing. '.format(nick, pl) - for i,v in sorted({i:var.ROLES_GUIDE[i] for i in var.ROLES_GUIDE if i is not None}.items()): + for i, v in sorted({i: var.ROLES_GUIDE[i] for i in var.ROLES_GUIDE if i is not None}.items()): if (i <= pl): txt += BOLD txt += "[" + str(i) + "] " @@ -3045,45 +3364,57 @@ def listroles(cli, nick, chan, rest): else: cli.msg(chan, txt) + @pmcmd("roles") def listroles_pm(cli, nick, rest): listroles(cli, nick, nick, rest) + @cmd("myrole") def myrole(cli, nick, chan, rest): """Reminds you of which role you have.""" if var.PHASE in ("none", "join"): cli.notice(nick, "No game is currently running.") return - + ps = var.list_players() if nick not in ps: cli.notice(nick, "You're currently not playing.") return - + pm(cli, nick, "You are a \02{0}\02.".format(var.get_role(nick))) - + # Check for gun/bullets if nick in var.GUNNERS and var.GUNNERS[nick]: if var.GUNNERS[nick] == 1: - pm(cli, nick, "You have a \02gun\02 with {0} {1}.".format(var.GUNNERS[nick], "bullet")) + pm(cli, nick, "You have a \02gun\02 with {0} {1}.".format( + var.GUNNERS[nick], + "bullet")) else: - pm(cli, nick, "You have a \02gun\02 with {0} {1}.".format(var.GUNNERS[nick], "bullets")) + pm(cli, nick, "You have a \02gun\02 with {0} {1}.".format( + var.GUNNERS[nick], + "bullets")) elif nick in var.WOLF_GUNNERS and var.WOLF_GUNNERS[nick]: if var.WOLF_GUNNERS[nick] == 1: - pm(cli, nick, "You have a \02gun\02 with {0} {1}.".format(var.WOLF_GUNNERS[nick], "bullet")) + pm(cli, nick, "You have a \02gun\02 with {0} {1}.".format( + var.WOLF_GUNNERS[nick], + "bullet")) else: - pm(cli, nick, "You have a \02gun\02 with {0} {1}.".format(var.WOLF_GUNNERS[nick], "bullets")) + pm(cli, nick, "You have a \02gun\02 with {0} {1}.".format( + var.WOLF_GUNNERS[nick], + "bullets")) + @pmcmd("myrole") def myrole_pm(cli, nick, rest): myrole(cli, nick, "", rest) - + + def aftergame(cli, rawnick, rest): """Schedule a command to be run after the game by someone.""" chan = botconfig.CHANNEL nick = parse_nick(rawnick)[0] - + rst = re.split(" +", rest) cmd = rst.pop(0).lower().replace(botconfig.CMD_CHAR, "", 1).strip() @@ -3098,16 +3429,15 @@ def aftergame(cli, rawnick, rest): else: cli.notice(nick, "That command was not found.") return - + if var.PHASE == "none": do_action() return - - cli.msg(chan, ("The command \02{0}\02 has been scheduled to run "+ - "after this game by \02{1}\02.").format(cmd, nick)) + + cli.msg(chan, ("The command \02{0}\02 has been scheduled to run " + + "after this game by \02{1}\02.").format(cmd, nick)) var.AFTER_FLASTGAME = do_action - @cmd("faftergame", admin_only=True, raw_nick=True) def _faftergame(cli, nick, chan, rest): @@ -3115,51 +3445,64 @@ def _faftergame(cli, nick, chan, rest): cli.notice(parse_nick(nick)[0], "Incorrect syntax for this command.") return aftergame(cli, nick, rest) - - - + + @pmcmd("faftergame", admin_only=True, raw_nick=True) def faftergame(cli, nick, rest): _faftergame(cli, nick, botconfig.CHANNEL, rest) + @pmcmd("fghost", owner_only=True) @cmd("fghost", owner_only=True) def fghost(cli, nick, *rest): cli.msg(botconfig.CHANNEL, nick + " is the ghost!") cli.mode(botconfig.CHANNEL, "+v", nick) + @pmcmd("funghost", owner_only=True) @cmd("funghost", owner_only=True) def funghost(cli, nick, *rest): cli.mode(botconfig.CHANNEL, "-v", nick) - + + @pmcmd("flastgame", admin_only=True, raw_nick=True) def flastgame(cli, nick, rest): """This command may be used in the channel or in a PM, and it disables starting or joining a game. !flastgame """ rawnick = nick nick, _, __, cloak = parse_nick(rawnick) - + chan = botconfig.CHANNEL if var.PHASE != "join": if "join" in COMMANDS.keys(): del COMMANDS["join"] - cmd("join")(lambda *spam: cli.msg(chan, "This command has been disabled by an admin.")) + cmd("join")( + lambda * + spam: cli.msg( + chan, + "This command has been disabled by an admin.")) # manually recreate the command by calling the decorator function if "start" in COMMANDS.keys(): del COMMANDS["start"] - cmd("join")(lambda *spam: cli.msg(chan, "This command has been disabled by an admin.")) - - cli.msg(chan, "Starting a new game has now been disabled by \02{0}\02.".format(nick)) + cmd("join")( + lambda * + spam: cli.msg( + chan, + "This command has been disabled by an admin.")) + + cli.msg( + chan, + "Starting a new game has now been disabled by \02{0}\02.".format(nick)) var.ADMIN_TO_PING = nick - + if rest.strip(): aftergame(cli, rawnick, rest) - + + @cmd("flastgame", admin_only=True, raw_nick=True) def _flastgame(cli, nick, chan, rest): flastgame(cli, nick, rest) - - + + @cmd('gamestats', 'gstats') def game_stats(cli, nick, chan, rest): """Gets the game stats for a given game size or lists game totals for all game sizes if no game size is given.""" @@ -3176,7 +3519,7 @@ def game_stats(cli, nick, chan, rest): if var.PHASE not in ('none', 'join'): cli.notice(nick, 'Wait until the game is over to view stats.') return - + # List all games sizes and totals if no size is given if not rest: if chan == nick: @@ -3188,11 +3531,12 @@ def game_stats(cli, nick, chan, rest): # Check for invalid input rest = rest.strip() - if not rest.isdigit() or int(rest) > var.MAX_PLAYERS or int(rest) < var.MIN_PLAYERS: + if not rest.isdigit() or int( + rest) > var.MAX_PLAYERS or int(rest) < var.MIN_PLAYERS: cli.notice(nick, ('Please enter an integer between {} and ' '{}.').format(var.MIN_PLAYERS, var.MAX_PLAYERS)) return - + # Attempt to find game stats for the given game size if chan == nick: pm(cli, nick, var.get_game_stats(int(rest))) @@ -3204,7 +3548,7 @@ def game_stats(cli, nick, chan, rest): def game_stats_pm(cli, nick, rest): game_stats(cli, nick, nick, rest) - + @cmd('playerstats', 'pstats', 'player', 'p') def player_stats(cli, nick, chan, rest): """Gets the stats for the given player and role or a list of role totals if no role is given.""" @@ -3221,7 +3565,7 @@ def player_stats(cli, nick, chan, rest): if var.PHASE not in ('none', 'join'): cli.notice(nick, 'Wait until the game is over to view stats.') return - + params = rest.split() # Check if we have enough parameters @@ -3236,13 +3580,13 @@ def player_stats(cli, nick, chan, rest): if acc == '*': if user == nick: cli.notice(nick, 'You are not identified with NickServ.') - else: + else: cli.notice(nick, user + ' is not identified with NickServ.') return else: acc = user - + # List the player's total games for all roles if no role is given if len(params) < 2: if chan == nick: @@ -3250,14 +3594,14 @@ def player_stats(cli, nick, chan, rest): else: cli.msg(chan, var.get_player_totals(acc)) else: - role = ' '.join(params[1:]) + role = ' '.join(params[1:]) # Attempt to find the player's stats if chan == nick: pm(cli, nick, var.get_player_stats(acc, role)) else: cli.msg(chan, var.get_player_stats(acc, role)) - + @pmcmd('playerstats', 'pstats', 'player', 'p') def player_stats_pm(cli, nick, rest): @@ -3268,10 +3612,12 @@ def player_stats_pm(cli, nick, rest): def fpull(cli, nick, chan, rest): output = None try: - output = subprocess.check_output(('git', 'pull'), stderr=subprocess.STDOUT) + output = subprocess.check_output( + ('git', 'pull'), + stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: pm(cli, nick, '{0}: {1}'.format(type(e), e)) - #raise + # raise if output: for line in output.splitlines(): @@ -3279,23 +3625,26 @@ def fpull(cli, nick, chan, rest): else: pm(cli, nick, '(no output)') + @pmcmd("fpull", admin_only=True) def fpull_pm(cli, nick, rest): fpull(cli, nick, nick, rest) + @pmcmd("fsend", admin_only=True) def fsend(cli, nick, rest): - print('{0} - {1} fsend - {2}'.format(time.strftime('%Y-%m-%dT%H:%M:%S%z'), nick, rest)) + print( + '{0} - {1} fsend - {2}'.format(time.strftime('%Y-%m-%dT%H:%M:%S%z'), nick, rest)) cli.send(rest) - + before_debug_mode_commands = list(COMMANDS.keys()) before_debug_mode_pmcommands = list(PM_COMMANDS.keys()) if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS: - @cmd("eval", owner_only = True) - @pmcmd("eval", owner_only = True) + @cmd("eval", owner_only=True) + @pmcmd("eval", owner_only=True) def pyeval(cli, nick, *rest): rest = list(rest) if len(rest) == 2: @@ -3309,12 +3658,10 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS: else: cli.msg(chan, a[0:500]) except Exception as e: - cli.msg(chan, str(type(e))+":"+str(e)) - - - - @cmd("exec", owner_only = True) - @pmcmd("exec", owner_only = True) + cli.msg(chan, str(type(e)) + ":" + str(e)) + + @cmd("exec", owner_only=True) + @pmcmd("exec", owner_only=True) def py(cli, nick, *rest): rest = list(rest) if len(rest) == 2: @@ -3324,8 +3671,7 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS: try: exec(rest[0]) except Exception as e: - cli.msg(chan, str(type(e))+":"+str(e)) - + cli.msg(chan, str(type(e)) + ":" + str(e)) @cmd('revealroles', admin_only=True) def revealroles(cli, nick, chan, rest): @@ -3333,15 +3679,19 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS: cli.notice(nick, 'No game is currently running.') return - s = ' | '.join('\u0002{}\u0002: {}'.format(role,', '.join(players)) - for (role, players) in sorted(var.ROLES.items()) if players) + s = ' | '.join( + '\u0002{}\u0002: {}'.format( + role, + ', '.join(players)) for ( + role, + players) in sorted( + var.ROLES.items()) if players) if chan == nick: pm(cli, nick, s) else: cli.msg(chan, s) - @cmd("fgame", admin_only=True) def game(cli, nick, chan, rest): pl = var.list_players() @@ -3356,14 +3706,15 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS: return rest = rest.strip().lower() if rest: - if cgamemode(cli, *re.split(" +",rest)): - cli.msg(chan, ("\u0002{0}\u0002 has changed the "+ - "game settings successfully.").format(nick)) - - def fgame_help(args = ""): + if cgamemode(cli, *re.split(" +", rest)): + cli.msg(chan, ("\u0002{0}\u0002 has changed the " + + "game settings successfully.").format(nick)) + + def fgame_help(args=""): args = args.strip() if not args: - return "Available game mode setters: "+ ", ".join(var.GAME_MODES.keys()) + return "Available game mode setters: " + \ + ", ".join(var.GAME_MODES.keys()) elif args in var.GAME_MODES.keys(): return var.GAME_MODES[args].__doc__ else: @@ -3371,11 +3722,10 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS: game.__doc__ = fgame_help - # DO NOT MAKE THIS A PMCOMMAND ALSO @cmd("force", admin_only=True) def forcepm(cli, nick, chan, rest): - rst = re.split(" +",rest) + rst = re.split(" +", rest) if len(rst) < 2: cli.msg(chan, "The syntax is incorrect.") return @@ -3394,12 +3744,14 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS: cmd = rst.pop(0).lower().replace(botconfig.CMD_CHAR, "", 1) did = False if PM_COMMANDS.get(cmd) and not PM_COMMANDS[cmd][0].owner_only: - if (PM_COMMANDS[cmd][0].admin_only and nick in var.USERS and - not is_admin(var.USERS[nick]["cloak"])): + if (PM_COMMANDS[cmd][0].admin_only and nick in var.USERS and + not is_admin(var.USERS[nick]["cloak"])): # Not a full admin - cli.notice(nick, "Only full admins can force an admin-only command.") + cli.notice( + nick, + "Only full admins can force an admin-only command.") return - + for fn in PM_COMMANDS[cmd]: if fn.raw_nick: continue @@ -3409,15 +3761,17 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS: cli.msg(chan, "Operation successful.") else: cli.msg(chan, "Not possible with this command.") - #if var.PHASE == "night": <- Causes problems with night starting twice. + # if var.PHASE == "night": <- Causes problems with night starting twice. # chk_nightdone(cli) elif COMMANDS.get(cmd) and not COMMANDS[cmd][0].owner_only: - if (COMMANDS[cmd][0].admin_only and nick in var.USERS and - not is_admin(var.USERS[nick]["cloak"])): + if (COMMANDS[cmd][0].admin_only and nick in var.USERS and + not is_admin(var.USERS[nick]["cloak"])): # Not a full admin - cli.notice(nick, "Only full admins can force an admin-only command.") + cli.notice( + nick, + "Only full admins can force an admin-only command.") return - + for fn in COMMANDS[cmd]: if fn.raw_nick: continue @@ -3429,20 +3783,19 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS: cli.msg(chan, "Not possible with this command.") else: cli.msg(chan, "That command was not found.") - - + @cmd("rforce", admin_only=True) def rforcepm(cli, nick, chan, rest): - rst = re.split(" +",rest) + rst = re.split(" +", rest) if len(rst) < 2: cli.msg(chan, "The syntax is incorrect.") return who = rst.pop(0).strip().lower() who = who.replace("_", " ") - - if (who not in var.ROLES or not var.ROLES[who]) and (who != "gunner" - or var.PHASE in ("none", "join")): - cli.msg(chan, nick+": invalid role") + + if (who not in var.ROLES or not var.ROLES[who]) and ( + who != "gunner" or var.PHASE in ("none", "join")): + cli.msg(chan, nick + ": invalid role") return elif who == "gunner": tgt = list(var.GUNNERS.keys()) @@ -3451,25 +3804,29 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS: cmd = rst.pop(0).lower().replace(botconfig.CMD_CHAR, "", 1) if PM_COMMANDS.get(cmd) and not PM_COMMANDS[cmd][0].owner_only: - if (PM_COMMANDS[cmd][0].admin_only and nick in var.USERS and - not is_admin(var.USERS[nick]["cloak"])): + if (PM_COMMANDS[cmd][0].admin_only and nick in var.USERS and + not is_admin(var.USERS[nick]["cloak"])): # Not a full admin - cli.notice(nick, "Only full admins can force an admin-only command.") + cli.notice( + nick, + "Only full admins can force an admin-only command.") return - + for fn in PM_COMMANDS[cmd]: for guy in tgt[:]: fn(cli, guy, " ".join(rst)) cli.msg(chan, "Operation successful.") - #if var.PHASE == "night": <- Causes problems with night starting twice. + # if var.PHASE == "night": <- Causes problems with night starting twice. # chk_nightdone(cli) elif cmd.lower() in COMMANDS.keys() and not COMMANDS[cmd][0].owner_only: - if (COMMANDS[cmd][0].admin_only and nick in var.USERS and - not is_admin(var.USERS[nick]["cloak"])): + if (COMMANDS[cmd][0].admin_only and nick in var.USERS and + not is_admin(var.USERS[nick]["cloak"])): # Not a full admin - cli.notice(nick, "Only full admins can force an admin-only command.") + cli.notice( + nick, + "Only full admins can force an admin-only command.") return - + for fn in COMMANDS[cmd]: for guy in tgt[:]: fn(cli, guy, chan, " ".join(rst)) @@ -3477,11 +3834,9 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS: else: cli.msg(chan, "That command was not found.") - - @cmd("frole", admin_only=True) def frole(cli, nick, chan, rest): - rst = re.split(" +",rest) + rst = re.split(" +", rest) if len(rst) < 2: cli.msg(chan, "The syntax is incorrect.") return @@ -3492,7 +3847,9 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS: if who.lower() not in ull: if not is_fake_nick(who): cli.msg(chan, "Could not be done.") - cli.msg(chan, "The target needs to be in this channel or a fake name.") + cli.msg( + chan, + "The target needs to be in this channel or a fake name.") return if not is_fake_nick(who): who = ul[ull.index(who.lower())] @@ -3505,7 +3862,7 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS: cli.msg(chan, "This is only allowed in game.") return if rol.startswith("gunner"): - rolargs = re.split(" +",rol, 1) + rolargs = re.split(" +", rol, 1) if len(rolargs) == 2 and rolargs[1].isdigit(): if len(rolargs[1]) < 7: var.GUNNERS[who] = int(rolargs[1]) @@ -3514,7 +3871,9 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS: var.GUNNERS[who] = 999 var.WOLF_GUNNERS[who] = 999 else: - var.GUNNERS[who] = math.ceil(var.SHOTS_MULTIPLIER * len(pl)) + var.GUNNERS[who] = math.ceil( + var.SHOTS_MULTIPLIER * + len(pl)) if who not in pl: var.ROLES["villager"].append(who) elif rol == "cursed villager": @@ -3531,16 +3890,16 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS: var.del_player(who) var.ROLES[rol].append(who) cli.msg(chan, "Operation successful.") - if var.PHASE not in ('none','join'): + if var.PHASE not in ('none', 'join'): chk_win(cli) - + if botconfig.ALLOWED_NORMAL_MODE_COMMANDS and not botconfig.DEBUG_MODE: for comd in list(COMMANDS.keys()): - if (comd not in before_debug_mode_commands and - comd not in botconfig.ALLOWED_NORMAL_MODE_COMMANDS): + if (comd not in before_debug_mode_commands and + comd not in botconfig.ALLOWED_NORMAL_MODE_COMMANDS): del COMMANDS[comd] for pmcomd in list(PM_COMMANDS.keys()): if (pmcomd not in before_debug_mode_pmcommands and - pmcomd not in botconfig.ALLOWED_NORMAL_MODE_COMMANDS): + pmcomd not in botconfig.ALLOWED_NORMAL_MODE_COMMANDS): del PM_COMMANDS[pmcomd] diff --git a/oyoyo/client.py b/oyoyo/client.py index 66898c4..b571dad 100644 --- a/oyoyo/client.py +++ b/oyoyo/client.py @@ -22,15 +22,20 @@ import traceback import sys import ssl -from oyoyo.parse import parse_raw_irc_command +from oyoyo.parse import parse_raw_irc_command + +# Adapted from +# http://code.activestate.com/recipes/511490-implementation-of-the-token-bucket-algorithm/ + -# Adapted from http://code.activestate.com/recipes/511490-implementation-of-the-token-bucket-algorithm/ class TokenBucket(object): + """An implementation of the token bucket algorithm. - + >>> bucket = TokenBucket(80, 0.5) >>> bucket.consume(1) """ + def __init__(self, tokens, fill_rate): """tokens is the total tokens in the bucket. fill_rate is the rate in tokens/second that the bucket will be refilled.""" @@ -55,9 +60,8 @@ class TokenBucket(object): self._tokens = min(self.capacity, self._tokens + delta) self.timestamp = now return self._tokens - - - + + def add_commands(d): def dec(cls): for c in d: @@ -68,36 +72,39 @@ def add_commands(d): setattr(cls, c, func(c)) return cls return dec + + @add_commands(("join", "mode", "nick", "who", "cap")) class IRCClient(object): + """ IRC Client class. This handles one connection to a server. This can be used either with or without IRCApp ( see connect() docs ) """ def __init__(self, cmd_handler, **kwargs): - """ the first argument should be an object with attributes/methods named - as the irc commands. You may subclass from one of the classes in - oyoyo.cmdhandler for convenience but it is not required. The - methods should have arguments (prefix, args). prefix is + """ the first argument should be an object with attributes/methods named + as the irc commands. You may subclass from one of the classes in + oyoyo.cmdhandler for convenience but it is not required. The + methods should have arguments (prefix, args). prefix is normally the sender of the command. args is a list of arguments. - Its recommened you subclass oyoyo.cmdhandler.DefaultCommandHandler, - this class provides defaults for callbacks that are required for + Its recommened you subclass oyoyo.cmdhandler.DefaultCommandHandler, + this class provides defaults for callbacks that are required for normal IRC operation. all other arguments should be keyword arguments. The most commonly used will be nick, host and port. You can also specify an "on connect" callback. ( check the source for others ) - Warning: By default this class will not block on socket operations, this + 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. + To enable blocking pass blocking=True. """ - - self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.nickname = "" self.hostmask = "" self.ident = "" @@ -111,12 +118,12 @@ class IRCClient(object): self.sasl_auth = False self.use_ssl = False self.lock = threading.RLock() - + self.tokenbucket = TokenBucket(23, 1.73) self.__dict__.update(kwargs) self.command_handler = cmd_handler - + if self.use_ssl: self.socket = ssl.wrap_socket(self.socket) @@ -124,8 +131,8 @@ class IRCClient(object): def send(self, *args, **kwargs): """ send a message to the connected server. all arguments are joined - with a space for convenience, for example the following are identical - + with a space for convenience, for example the following are identical + >>> cli.send("JOIN " + some_room) >>> cli.send("JOIN", some_room) @@ -134,13 +141,13 @@ class IRCClient(object): the 'encoding' keyword argument (default 'utf8'). In python 3, all args must be of type str or bytes, *BUT* if they are str they will be converted to bytes with the encoding specified by the - 'encoding' keyword argument (default 'utf8'). + 'encoding' keyword argument (default 'utf8'). """ with self.lock: # Convert all args to bytes if not already encoding = kwargs.get('encoding') or 'utf_8' bargs = [] - for i,arg in enumerate(args): + for i, arg in enumerate(args): if isinstance(arg, str): bargs.append(bytes(arg, encoding)) elif isinstance(arg, bytes): @@ -148,20 +155,25 @@ class IRCClient(object): elif arg is None: continue else: - 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)) + 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 {0}'.format(str(msg)[1:])) - + while not self.tokenbucket.consume(1): time.sleep(0.3) self.socket.send(msg + bytes("\r\n", "utf_8")) def connect(self): - """ initiates the connection to the server set in self.host:self.port - and returns a generator object. + """ initiates the connection to the server set in self.host:self.port + and returns a generator object. >>> cli = IRCClient(my_handler, host="irc.freenode.net", port=6667) >>> g = cli.connect() @@ -183,32 +195,34 @@ class IRCClient(object): break if not self.blocking: self.socket.setblocking(0) - + if not self.sasl_auth: - self.send("PASS {0}:{1}".format(self.authname if self.authname else self.nickname, - self.password if self.password else "NOPASS")) + self.send( + "PASS {0}:{1}".format( + self.authname if self.authname else self.nickname, + self.password if self.password else "NOPASS")) else: self.cap("LS") - + self.nick(self.nickname) self.user(self.nickname, self.real_name) if self.sasl_auth: self.cap("REQ", "multi-prefix") self.cap("REQ", "sasl") - + if self.connect_cb: try: self.connect_cb(self) except Exception as e: traceback.print_exc() raise e - + buffer = bytes() while not self._end: try: buffer += self.socket.recv(1024) - except socket.error as e: + except socket.error as e: if False and not self.blocking and e.errno == 11: pass else: @@ -219,16 +233,25 @@ class IRCClient(object): for el in data: prefix, command, args = parse_raw_irc_command(el) - + try: enc = "utf8" - fargs = [arg.decode(enc) for arg in args if isinstance(arg,bytes)] + fargs = [ + arg.decode(enc) for arg in args if isinstance( + arg, + bytes)] except UnicodeDecodeError: enc = "latin1" - fargs = [arg.decode(enc) for arg in args if isinstance(arg,bytes)] - - logging.debug("processCommand ({2}){0}({1})".format(command, - fargs, prefix)) + fargs = [ + arg.decode(enc) for arg in args if isinstance( + arg, + bytes)] + + logging.debug( + "processCommand ({2}){0}({1})".format( + command, + fargs, + prefix)) try: largs = list(args) if prefix is not None: @@ -236,21 +259,32 @@ class IRCClient(object): # for i,arg in enumerate(largs): # if arg is not None: largs[i] = arg.decode(enc) if command in self.command_handler: - self.command_handler[command](self, prefix,*fargs) + self.command_handler[command]( + self, + prefix, + * + fargs) elif "" in self.command_handler: - self.command_handler[""](self, prefix, command, *fargs) + self.command_handler[""]( + self, + prefix, + command, + * + fargs) except Exception as e: traceback.print_exc() raise e # ? yield True finally: - if self.socket: + if self.socket: logging.info('closing socket') self.socket.close() yield False + def msg(self, user, msg): for line in msg.split('\n'): - maxchars = 494 - len(self.nickname+self.ident+self.hostmask+user) + maxchars = 494 - len( + self.nickname + self.ident + self.hostmask + user) while line: extra = "" if len(line) > maxchars: @@ -259,9 +293,11 @@ class IRCClient(object): self.send("PRIVMSG", user, ":{0}".format(line)) line = extra privmsg = msg # Same thing + def notice(self, user, msg): for line in msg.split('\n'): - maxchars = 495 - len(self.nickname+self.ident+self.hostmask+user) + maxchars = 495 - len( + self.nickname + self.ident + self.hostmask + user) while line: extra = "" if len(line) > maxchars: @@ -269,27 +305,35 @@ class IRCClient(object): line = line[:maxchars] self.send("NOTICE", user, ":{0}".format(line)) line = extra + def quit(self, msg=""): self.send("QUIT :{0}".format(msg)) + def part(self, chan, msg=""): self.send("PART {0} :{1}".format(chan, msg)) + def kick(self, chan, nick, msg=""): - self.send("KICK", chan, nick, ":"+msg) + self.send("KICK", chan, nick, ":" + msg) + def ns_identify(self, passwd): self.msg("NickServ", "IDENTIFY {0} {1}".format(self.nickname, passwd)) + def ns_ghost(self): - self.msg("NickServ", "GHOST "+self.nickname) + self.msg("NickServ", "GHOST " + self.nickname) + def ns_release(self): - self.msg("NickServ", "RELEASE "+self.nickname) + self.msg("NickServ", "RELEASE " + self.nickname) + def ns_regain(self): - self.msg("NickServ", "REGAIN "+self.nickname) + self.msg("NickServ", "REGAIN " + self.nickname) + def user(self, uname, rname): - self.send("USER", uname, self.host, self.host, - rname or uname) + self.send("USER", uname, self.host, self.host, + rname or uname) + def mainLoop(self): conn = self.connect() while True: if not next(conn): print("Calling sys.exit()...") sys.exit() - diff --git a/oyoyo/ircevents.py b/oyoyo/ircevents.py index d6bcf13..7bada3e 100644 --- a/oyoyo/ircevents.py +++ b/oyoyo/ircevents.py @@ -141,7 +141,7 @@ numeric_events = { b"423": "noadmininfo", b"424": "fileerror", b"431": "nonicknamegiven", - b"432": "erroneusnickname", # Thiss iz how its speld in thee RFC. + b"432": "erroneusnickname", # Thiss iz how its speld in thee RFC. b"433": "nicknameinuse", b"436": "nickcollision", b"437": "unavailresource", # "Nick temporally unavailable" @@ -156,7 +156,7 @@ numeric_events = { b"462": "alreadyregistered", b"463": "nopermforhost", b"464": "passwdmismatch", - b"465": "yourebannedcreep", # I love this one... + b"465": "yourebannedcreep", # I love this one... b"466": "youwillbebanned", b"467": "keyset", b"471": "channelisfull", @@ -208,4 +208,3 @@ protocol_events = [ ] all_events = generated_events + protocol_events + list(numeric_events.values()) - diff --git a/oyoyo/parse.py b/oyoyo/parse.py index f2bc1ec..a9b9e48 100644 --- a/oyoyo/parse.py +++ b/oyoyo/parse.py @@ -18,9 +18,11 @@ import logging from oyoyo.ircevents import generated_events, protocol_events,\ - all_events, numeric_events + all_events, numeric_events # avoiding regex + + def parse_raw_irc_command(element): """ This function parses a raw irc command and returns a tuple @@ -56,12 +58,13 @@ def parse_raw_irc_command(element): except KeyError: logging.debug('unknown numeric event {0}'.format(command)) command = command.lower() - if isinstance(command, bytes): command = command.decode("utf_8") + if isinstance(command, bytes): + command = command.decode("utf_8") if args[0].startswith(bytes(':', 'utf_8')): args = [bytes(" ", "utf_8").join(args)[1:]] else: - for idx, arg in enumerate(args): + for idx, arg in enumerate(args): if arg.startswith(bytes(':', 'utf_8')): args = args[:idx] + [bytes(" ", 'utf_8').join(args[idx:])[1:]] break @@ -89,4 +92,3 @@ def parse_nick(name): return (nick, mode, rest, None) return (nick, mode, user, host) - diff --git a/settings/villagergame.wolfgame.py b/settings/villagergame.wolfgame.py index 6d8e479..af4cd93 100644 --- a/settings/villagergame.wolfgame.py +++ b/settings/villagergame.wolfgame.py @@ -1,6 +1,6 @@ PING_WAIT = 300 # Seconds PING_MIN_WAIT = 30 -MINIMUM_WAIT = 60 +MINIMUM_WAIT = 60 EXTRA_WAIT = 20 MAXIMUM_WAITED = 2 # limit for amount of !wait's STATS_RATE_LIMIT = 15 @@ -22,128 +22,140 @@ MAX_PRIVMSG_TARGETS = 1 LOG_FILENAME = "" BARE_LOG_FILENAME = "" - # HIT MISS SUICIDE -GUN_CHANCES = ( 5/7 , 1/7 , 1/7 ) -DRUNK_GUN_CHANCES = ( 2/7 , 4/7 , 1/7 ) -MANSLAUGHTER_CHANCE = 1/5 # ACCIDENTAL HEADSHOT (FATAL) +# HIT MISS SUICIDE +GUN_CHANCES = (5 / 7, 1 / 7, 1 / 7) +DRUNK_GUN_CHANCES = (2 / 7, 4 / 7, 1 / 7) +MANSLAUGHTER_CHANCE = 1 / 5 # ACCIDENTAL HEADSHOT (FATAL) GUNNER_KILLS_WOLF_AT_NIGHT_CHANCE = 0 -GUARDIAN_ANGEL_DIES_CHANCE = 1/2 -DETECTIVE_REVEALED_CHANCE = 2/5 +GUARDIAN_ANGEL_DIES_CHANCE = 1 / 2 +DETECTIVE_REVEALED_CHANCE = 2 / 5 -################################################################################################################# +########################################################################## # ROLE INDEX: PLAYERS SEER WOLF CURSED DRUNK HARLOT TRAITOR GUNNER CROW ANGEL DETECTIVE ## -################################################################################################################# -ROLES_GUIDE = { 4 : ( 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ), ## - 6 : ( 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 ), ## - 8 : ( 1 , 2 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 ), ## - 10 : ( 1 , 2 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 ), ## - 11 : ( 1 , 2 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 0 ), ## - 15 : ( 1 , 3 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 ), ## - 22 : ( 1 , 4 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 ), ## - 29 : ( 1 , 5 , 1 , 1 , 1 , 1 , 1 , 0 , 1 , 1 ), ## - None : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 )} ## -################################################################################################################# +########################################################################## +ROLES_GUIDE = {4: (1, 1, 0, 0, 0, 0, 0, 0, 0, 0), + 6: (1, 1, 1, 1, 0, 0, 0, 0, 0, 0), + 8: (1, 2, 1, 1, 1, 0, 0, 0, 0, 0), + 10: (1, 2, 1, 1, 1, 1, 1, 0, 0, 0), + 11: (1, 2, 1, 1, 1, 1, 1, 0, 1, 0), + 15: (1, 3, 1, 1, 1, 1, 1, 0, 1, 1), + 22: (1, 4, 1, 1, 1, 1, 1, 0, 1, 1), + 29: (1, 5, 1, 1, 1, 1, 1, 0, 1, 1), + None: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0)} +########################################################################## # Notes: ## -################################################################################################################# +########################################################################## GAME_MODES = {} AWAY = [] # cloaks of people who are away. -SIMPLE_NOTIFY = [] # cloaks of people who !simple, who want everything /notice'd +# cloaks of people who !simple, who want everything /notice'd +SIMPLE_NOTIFY = [] -ROLE_INDICES = {0 : "seer", - 1 : "wolf", - 2 : "cursed villager", - 3 : "village drunk", - 4 : "harlot", - 5 : "traitor", - 6 : "gunner", - 7 : "werecrow", - 8 : "guardian angel", - 9 : "detective"} - -INDEX_OF_ROLE = dict((v,k) for k,v in ROLE_INDICES.items()) +ROLE_INDICES = {0: "seer", + 1: "wolf", + 2: "cursed villager", + 3: "village drunk", + 4: "harlot", + 5: "traitor", + 6: "gunner", + 7: "werecrow", + 8: "guardian angel", + 9: "detective"} + +INDEX_OF_ROLE = dict((v, k) for k, v in ROLE_INDICES.items()) NO_VICTIMS_MESSAGES = ("The body of a young penguin pet is found.", "A pool of blood and wolf paw prints are found.", "Traces of wolf fur are found.") -LYNCH_MESSAGES = ("The villagers, after much debate, finally decide on lynching \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.", - "Under a lot of noise, the pitchfork-bearing villagers lynch \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.", - "The mob drags a protesting \u0002{0}\u0002 to the hanging tree. S/He succumbs to the will of the horde, and is hanged. It is discovered (s)he was a \u0002{1}\u0002.", - "Resigned to his/her fate, \u0002{0}\u0002 is led to the gallows. After death, it is discovered (s)he was a \u0002{1}\u0002.") +LYNCH_MESSAGES = ( + "The villagers, after much debate, finally decide on lynching \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.", + "Under a lot of noise, the pitchfork-bearing villagers lynch \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.", + "The mob drags a protesting \u0002{0}\u0002 to the hanging tree. S/He succumbs to the will of the horde, and is hanged. It is discovered (s)he was a \u0002{1}\u0002.", + "Resigned to his/her fate, \u0002{0}\u0002 is led to the gallows. After death, it is discovered (s)he was a \u0002{1}\u0002.") import botconfig -RULES = (botconfig.CHANNEL + " channel rules: 1) Be nice to others. 2) Do not share information "+ - "after death. 3) No bots allowed. 4) Do not play with clones.\n"+ - "5) Do not quit unless you need to leave. 6) No swearing and keep it "+ - "family-friendly. 7) Do not paste PM's from the bot during the game. "+ - "8) Use common sense. 9) Waiting for timeouts is discouraged.") +RULES = ( + botconfig.CHANNEL + + " channel rules: 1) Be nice to others. 2) Do not share information " + + "after death. 3) No bots allowed. 4) Do not play with clones.\n" + + "5) Do not quit unless you need to leave. 6) No swearing and keep it " + + "family-friendly. 7) Do not paste PM's from the bot during the game. " + + "8) Use common sense. 9) Waiting for timeouts is discouraged.") # Other settings: START_WITH_DAY = False -WOLF_STEALS_GUN = False # at night, the wolf can steal steal the victim's bullets +# at night, the wolf can steal steal the victim's bullets +WOLF_STEALS_GUN = False OPT_IN_PING = False # instead of !away/!back, users can opt-in to be pinged PING_IN = [] # cloaks of users who have opted in for ping is_role = lambda plyr, rol: rol in ROLES and plyr in ROLES[rol] + def plural(role): - if role == "wolf": return "wolves" - elif role == "person": return "people" - else: return role + "s" - + if role == "wolf": + return "wolves" + elif role == "person": + return "people" + else: + return role + "s" + + def list_players(): pl = [] for x in ROLES.values(): pl.extend(x) return pl - + + def list_players_and_roles(): plr = {} for x in ROLES.keys(): for p in ROLES[x]: plr[p] = x return plr - + get_role = lambda plyr: list_players_and_roles()[plyr] def del_player(pname): prole = get_role(pname) ROLES[prole].remove(pname) - - -class InvalidModeException(Exception): pass + +class InvalidModeException(Exception): + pass + + def game_mode(name): def decor(c): GAME_MODES[name] = c return c return decor - -CHANGEABLE_ROLES = { "seers" : INDEX_OF_ROLE["seer"], - "wolves" : INDEX_OF_ROLE["wolf"], - "cursed" : INDEX_OF_ROLE["cursed villager"], - "drunks" : INDEX_OF_ROLE["village drunk"], - "harlots" : INDEX_OF_ROLE["harlot"], - "traitors" : INDEX_OF_ROLE["traitor"], - "gunners" : INDEX_OF_ROLE["gunner"], - "werecrows" : INDEX_OF_ROLE["werecrow"], - "angels" : INDEX_OF_ROLE["guardian angel"], - "detectives" : INDEX_OF_ROLE["detective"]} - +CHANGEABLE_ROLES = {"seers": INDEX_OF_ROLE["seer"], + "wolves": INDEX_OF_ROLE["wolf"], + "cursed": INDEX_OF_ROLE["cursed villager"], + "drunks": INDEX_OF_ROLE["village drunk"], + "harlots": INDEX_OF_ROLE["harlot"], + "traitors": INDEX_OF_ROLE["traitor"], + "gunners": INDEX_OF_ROLE["gunner"], + "werecrows": INDEX_OF_ROLE["werecrow"], + "angels": INDEX_OF_ROLE["guardian angel"], + "detectives": INDEX_OF_ROLE["detective"]} # TODO: implement game modes @game_mode("roles") class ChangedRolesMode(object): + """Example: !fgame roles=wolves:1,seers:0,angels:1""" - + def __init__(self, arg): self.ROLES_GUIDE = ROLES_GUIDE.copy() lx = list(ROLES_GUIDE[None]) @@ -161,102 +173,111 @@ class ChangedRolesMode(object): try: lx[CHANGEABLE_ROLES[role.lower()]] = num except KeyError: - raise InvalidModeException(("The role \u0002{0}\u0002 "+ + raise InvalidModeException(("The role \u0002{0}\u0002 " + "is not valid.").format(role)) except ValueError: - raise InvalidModeException("A bad value was used in mode roles.") + raise InvalidModeException( + "A bad value was used in mode roles.") for k in ROLES_GUIDE.keys(): self.ROLES_GUIDE[k] = tuple(lx) - + # Persistence - - + + # Load saved settings import sqlite3 import os -conn = sqlite3.connect("data.sqlite3", check_same_thread = False) +conn = sqlite3.connect("data.sqlite3", check_same_thread=False) with conn: c = conn.cursor() - c.execute('CREATE TABLE IF NOT EXISTS away (nick TEXT)') # whoops, i mean cloak, not nick - - c.execute('CREATE TABLE IF NOT EXISTS simple_role_notify (cloak TEXT)') # people who understand each role + # whoops, i mean cloak, not nick + c.execute('CREATE TABLE IF NOT EXISTS away (nick TEXT)') + + # people who understand each role + c.execute('CREATE TABLE IF NOT EXISTS simple_role_notify (cloak TEXT)') c.execute('SELECT * FROM away') for row in c: AWAY.append(row[0]) - + c.execute('SELECT * FROM simple_role_notify') for row in c: SIMPLE_NOTIFY.append(row[0]) - + # populate the roles table c.execute('DROP TABLE IF EXISTS roles') - c.execute('CREATE TABLE roles (id INTEGER PRIMARY KEY AUTOINCREMENT, role TEXT)') + c.execute( + 'CREATE TABLE roles (id INTEGER PRIMARY KEY AUTOINCREMENT, role TEXT)') - for x in ["villager"]+list(ROLE_INDICES.values()): + for x in ["villager"] + list(ROLE_INDICES.values()): c.execute("INSERT OR REPLACE INTO roles (role) VALUES (?)", (x,)) - - - c.execute(('CREATE TABLE IF NOT EXISTS rolestats (player TEXT, role TEXT, '+ - 'teamwins SMALLINT, individualwins SMALLINT, totalgames SMALLINT, '+ - 'UNIQUE(player, role))')) - + + c.execute( + ('CREATE TABLE IF NOT EXISTS rolestats (player TEXT, role TEXT, ' + + 'teamwins SMALLINT, individualwins SMALLINT, totalgames SMALLINT, ' + + 'UNIQUE(player, role))')) + if OPT_IN_PING: c.execute('CREATE TABLE IF NOT EXISTS ping (cloak text)') c.execute('SELECT * FROM ping') for row in c: PING_IN.append(row[0]) - - + + def remove_away(clk): with conn: c.execute('DELETE from away where nick=?', (clk,)) - + + def add_away(clk): with conn: c.execute('INSERT into away VALUES (?)', (clk,)) - + + def remove_simple_rolemsg(clk): with conn: c.execute('DELETE from simple_role_notify where cloak=?', (clk,)) - + + def add_simple_rolemsg(clk): with conn: c.execute('INSERT into simple_role_notify VALUES (?)', (clk,)) - + + def remove_ping(clk): with conn: c.execute('DELETE from ping where cloak=?', (clk,)) + + def add_ping(clk): with conn: - c.execute('INSERT into ping VALUES (?)', (clk,)) + c.execute('INSERT into ping VALUES (?)', (clk,)) def update_role_stats(acc, role, won, iwon): - + with conn: wins, iwins, totalgames = 0, 0, 0 - - c.execute(("SELECT teamwins, individualwins, totalgames FROM rolestats "+ - "WHERE player=? AND role=?"), (acc, role)) + + c.execute( + ("SELECT teamwins, individualwins, totalgames FROM rolestats " + + "WHERE player=? AND role=?"), + (acc, role)) row = c.fetchone() if row: wins, iwins, total = row else: - wins, iwins, total = 0,0,0 - + wins, iwins, total = 0, 0, 0 + if won: wins += 1 if iwon: iwins += 1 total += 1 - + c.execute("INSERT OR REPLACE INTO rolestats VALUES (?,?,?,?,?)", (acc, role, wins, iwins, total)) - - - diff --git a/settings/wolfgame.py b/settings/wolfgame.py index f859c1d..56c6b6c 100644 --- a/settings/wolfgame.py +++ b/settings/wolfgame.py @@ -1,9 +1,9 @@ PING_WAIT = 300 # Seconds -PING_MIN_WAIT = 30 # How long !start has to wait after a !ping +PING_MIN_WAIT = 30 # How long !start has to wait after a !ping MINIMUM_WAIT = 60 EXTRA_WAIT = 20 -EXTRA_WAIT_JOIN = 0 # Add this many seconds to the waiting time for each !join -WAIT_AFTER_JOIN = 10 # Wait at least this many seconds after the last join +EXTRA_WAIT_JOIN = 0 # Add this many seconds to the waiting time for each !join +WAIT_AFTER_JOIN = 10 # Wait at least this many seconds after the last join MAXIMUM_WAITED = 3 # limit for amount of !wait's STATS_RATE_LIMIT = 60 VOTES_RATE_LIMIT = 60 @@ -21,14 +21,15 @@ DAY_TIME_LIMIT_WARN = 600 DAY_TIME_LIMIT_CHANGE = 120 # seconds after DAY_TIME_LIMIT_WARN has passed JOIN_TIME_LIMIT = 3600 # May only be set if the above are also set -SHORT_DAY_PLAYERS = 6 # Number of players left to have a short day +SHORT_DAY_PLAYERS = 6 # Number of players left to have a short day SHORT_DAY_LIMIT_WARN = 400 SHORT_DAY_LIMIT_CHANGE = 120 KILL_IDLE_TIME = 300 WARN_IDLE_TIME = 180 PART_GRACE_TIME = 30 QUIT_GRACE_TIME = 30 -# controls how many people it does in one /msg; only works for messages that are the same +# controls how many people it does in one /msg; only works for messages +# that are the same MAX_PRIVMSG_TARGETS = 4 LEAVE_STASIS_PENALTY = 1 IDLE_STASIS_PENALTY = 1 @@ -47,57 +48,59 @@ KILL_BOLD = False LOG_FILENAME = "" BARE_LOG_FILENAME = "" - # HIT MISS SUICIDE -GUN_CHANCES = ( 5/7 , 1/7 , 1/7 ) -DRUNK_GUN_CHANCES = ( 2/7 , 3/7 , 2/7 ) -MANSLAUGHTER_CHANCE = 2/5 # ACCIDENTAL HEADSHOT (FATAL) +# HIT MISS SUICIDE +GUN_CHANCES = (5 / 7, 1 / 7, 1 / 7) +DRUNK_GUN_CHANCES = (2 / 7, 3 / 7, 2 / 7) +MANSLAUGHTER_CHANCE = 2 / 5 # ACCIDENTAL HEADSHOT (FATAL) -GUNNER_KILLS_WOLF_AT_NIGHT_CHANCE = 1/4 -GUARDIAN_ANGEL_DIES_CHANCE = 1/2 -DETECTIVE_REVEALED_CHANCE = 2/5 +GUNNER_KILLS_WOLF_AT_NIGHT_CHANCE = 1 / 4 +GUARDIAN_ANGEL_DIES_CHANCE = 1 / 2 +DETECTIVE_REVEALED_CHANCE = 2 / 5 -################################################################################################################# +########################################################################## # ROLE INDEX: PLAYERS SEER WOLF CURSED DRUNK HARLOT TRAITOR GUNNER CROW ANGEL DETECTIVE ## -################################################################################################################# -ROLES_GUIDE = { 4 : ( 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ), ## - 6 : ( 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ), ## - 8 : ( 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 ), ## - 10 : ( 1 , 2 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 ), ## - 12 : ( 1 , 2 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 ), ## - 15 : ( 1 , 3 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 1 ), ## - 17 : ( 1 , 3 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ), ## - 18 : ( 1 , 3 , 2 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ), ## - 20 : ( 1 , 4 , 2 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ), ## - None : ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 )} ## -################################################################################################################# +########################################################################## +ROLES_GUIDE = {4: (1, 1, 0, 0, 0, 0, 0, 0, 0, 0), + 6: (1, 1, 1, 0, 0, 0, 0, 0, 0, 0), + 8: (1, 1, 1, 1, 1, 1, 0, 0, 0, 0), + 10: (1, 2, 1, 1, 1, 1, 1, 0, 0, 0), + 12: (1, 2, 1, 1, 1, 1, 1, 1, 0, 1), + 15: (1, 3, 1, 1, 1, 1, 1, 1, 0, 1), + 17: (1, 3, 1, 1, 1, 1, 1, 1, 1, 1), + 18: (1, 3, 2, 1, 1, 1, 1, 1, 1, 1), + 20: (1, 4, 2, 1, 1, 1, 1, 1, 1, 1), + None: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0)} +########################################################################## # Notes: ## -################################################################################################################# +########################################################################## GAME_MODES = {} AWAY = ['services.', 'services.int'] # cloaks of people who are away. -SIMPLE_NOTIFY = [] # cloaks of people who !simple, who want everything /notice'd +# cloaks of people who !simple, who want everything /notice'd +SIMPLE_NOTIFY = [] -ROLE_INDICES = {0 : "seer", - 1 : "wolf", - 2 : "cursed villager", - 3 : "village drunk", - 4 : "harlot", - 5 : "traitor", - 6 : "gunner", - 7 : "werecrow", - 8 : "guardian angel", - 9 : "detective"} +ROLE_INDICES = {0: "seer", + 1: "wolf", + 2: "cursed villager", + 3: "village drunk", + 4: "harlot", + 5: "traitor", + 6: "gunner", + 7: "werecrow", + 8: "guardian angel", + 9: "detective"} -INDEX_OF_ROLE = dict((v,k) for k,v in ROLE_INDICES.items()) +INDEX_OF_ROLE = dict((v, k) for k, v in ROLE_INDICES.items()) NO_VICTIMS_MESSAGES = ("The body of a young penguin pet is found.", "A pool of blood and wolf paw prints are found.", "Traces of wolf fur are found.") -LYNCH_MESSAGES = ("The villagers, after much debate, finally decide on lynching \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.", - "Under a lot of noise, the pitchfork-bearing villagers lynch \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.", - "Despite protests, the mob drags their victim to the hanging tree. \u0002{0}\u0002 succumbs to the will of the horde, and is hanged. The villagers have killed a \u0002{1}\u0002.", - "Resigned to the inevitable, \u0002{0}\u0002 is led to the gallows. Once the twitching stops, it is discovered that the village lynched a \u0002{1}\u0002.", - "Before the rope is pulled, \u0002{0}\u0002, the \u0002{1}\u0002, throws a grenade at the mob. The grenade explodes early.") +LYNCH_MESSAGES = ( + "The villagers, after much debate, finally decide on lynching \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.", + "Under a lot of noise, the pitchfork-bearing villagers lynch \u0002{0}\u0002, who turned out to be... a \u0002{1}\u0002.", + "Despite protests, the mob drags their victim to the hanging tree. \u0002{0}\u0002 succumbs to the will of the horde, and is hanged. The villagers have killed a \u0002{1}\u0002.", + "Resigned to the inevitable, \u0002{0}\u0002 is led to the gallows. Once the twitching stops, it is discovered that the village lynched a \u0002{1}\u0002.", + "Before the rope is pulled, \u0002{0}\u0002, the \u0002{1}\u0002, throws a grenade at the mob. The grenade explodes early.") import botconfig @@ -105,17 +108,23 @@ RULES = (botconfig.CHANNEL + " channel rules: http://wolf.xnrand.com/rules") # Other settings: START_WITH_DAY = False -WOLF_STEALS_GUN = True # at night, the wolf can steal steal the victim's bullets +# at night, the wolf can steal steal the victim's bullets +WOLF_STEALS_GUN = True OPT_IN_PING = False # instead of !away/!back, users can opt-in to be pinged PING_IN = [] # cloaks of users who have opted in for ping is_role = lambda plyr, rol: rol in ROLES and plyr in ROLES[rol] + def plural(role): - if role == "wolf": return "wolves" - elif role == "person": return "people" - else: return role + "s" + if role == "wolf": + return "wolves" + elif role == "person": + return "people" + else: + return role + "s" + def list_players(): pl = [] @@ -123,6 +132,7 @@ def list_players(): pl.extend(x) return pl + def list_players_and_roles(): plr = {} for x in ROLES.keys(): @@ -132,19 +142,23 @@ def list_players_and_roles(): get_role = lambda plyr: list_players_and_roles()[plyr] + def get_reveal_role(nick): if HIDDEN_TRAITOR and get_role(nick) == "traitor": return "villager" else: return get_role(nick) + def del_player(pname): prole = get_role(pname) ROLES[prole].remove(pname) +class InvalidModeException(Exception): + pass + -class InvalidModeException(Exception): pass def game_mode(name): def decor(c): GAME_MODES[name] = c @@ -152,23 +166,22 @@ def game_mode(name): return decor -CHANGEABLE_ROLES = { "seers" : INDEX_OF_ROLE["seer"], - "wolves" : INDEX_OF_ROLE["wolf"], - "cursed" : INDEX_OF_ROLE["cursed villager"], - "drunks" : INDEX_OF_ROLE["village drunk"], - "harlots" : INDEX_OF_ROLE["harlot"], - "traitors" : INDEX_OF_ROLE["traitor"], - "gunners" : INDEX_OF_ROLE["gunner"], - "werecrows" : INDEX_OF_ROLE["werecrow"], - "angels" : INDEX_OF_ROLE["guardian angel"], - "detectives" : INDEX_OF_ROLE["detective"]} - - +CHANGEABLE_ROLES = {"seers": INDEX_OF_ROLE["seer"], + "wolves": INDEX_OF_ROLE["wolf"], + "cursed": INDEX_OF_ROLE["cursed villager"], + "drunks": INDEX_OF_ROLE["village drunk"], + "harlots": INDEX_OF_ROLE["harlot"], + "traitors": INDEX_OF_ROLE["traitor"], + "gunners": INDEX_OF_ROLE["gunner"], + "werecrows": INDEX_OF_ROLE["werecrow"], + "angels": INDEX_OF_ROLE["guardian angel"], + "detectives": INDEX_OF_ROLE["detective"]} # TODO: implement game modes @game_mode("roles") class ChangedRolesMode(object): + """Example: !fgame roles=wolves:1,seers:0,angels:1""" def __init__(self, arg): @@ -187,10 +200,11 @@ class ChangedRolesMode(object): try: lx[CHANGEABLE_ROLES[role.lower()]] = num except KeyError: - raise InvalidModeException(("The role \u0002{0}\u0002 "+ + raise InvalidModeException(("The role \u0002{0}\u0002 " + "is not valid.").format(role)) except ValueError: - raise InvalidModeException("A bad value was used in mode roles.") + raise InvalidModeException( + "A bad value was used in mode roles.") for k in ROLES_GUIDE.keys(): self.ROLES_GUIDE[k] = tuple(lx) @@ -201,13 +215,15 @@ class ChangedRolesMode(object): # Load saved settings import sqlite3 -conn = sqlite3.connect("data.sqlite3", check_same_thread = False) +conn = sqlite3.connect("data.sqlite3", check_same_thread=False) with conn: c = conn.cursor() - c.execute('CREATE TABLE IF NOT EXISTS away (nick TEXT)') # whoops, i mean cloak, not nick + # whoops, i mean cloak, not nick + c.execute('CREATE TABLE IF NOT EXISTS away (nick TEXT)') - c.execute('CREATE TABLE IF NOT EXISTS simple_role_notify (cloak TEXT)') # people who understand each role + # people who understand each role + c.execute('CREATE TABLE IF NOT EXISTS simple_role_notify (cloak TEXT)') c.execute('SELECT * FROM away') for row in c: @@ -219,21 +235,21 @@ with conn: # populate the roles table c.execute('DROP TABLE IF EXISTS roles') - c.execute('CREATE TABLE roles (id INTEGER PRIMARY KEY AUTOINCREMENT, role TEXT)') + c.execute( + 'CREATE TABLE roles (id INTEGER PRIMARY KEY AUTOINCREMENT, role TEXT)') - for x in ["villager"]+list(ROLE_INDICES.values()): + for x in ["villager"] + list(ROLE_INDICES.values()): c.execute("INSERT OR REPLACE INTO roles (role) VALUES (?)", (x,)) + c.execute( + ('CREATE TABLE IF NOT EXISTS rolestats (player TEXT, role TEXT, ' + + 'teamwins SMALLINT, individualwins SMALLINT, totalgames SMALLINT, ' + + 'UNIQUE(player, role))')) - c.execute(('CREATE TABLE IF NOT EXISTS rolestats (player TEXT, role TEXT, '+ - 'teamwins SMALLINT, individualwins SMALLINT, totalgames SMALLINT, '+ - 'UNIQUE(player, role))')) + c.execute( + ('CREATE TABLE IF NOT EXISTS gamestats (size SMALLINT, villagewins SMALLINT, ' + + 'wolfwins SMALLINT, totalgames SMALLINT, UNIQUE(size))')) - - c.execute(('CREATE TABLE IF NOT EXISTS gamestats (size SMALLINT, villagewins SMALLINT, ' + - 'wolfwins SMALLINT, totalgames SMALLINT, UNIQUE(size))')) - - if OPT_IN_PING: c.execute('CREATE TABLE IF NOT EXISTS ping (cloak text)') @@ -246,22 +262,27 @@ def remove_away(clk): with conn: c.execute('DELETE from away where nick=?', (clk,)) + def add_away(clk): with conn: c.execute('INSERT into away VALUES (?)', (clk,)) + def remove_simple_rolemsg(clk): with conn: c.execute('DELETE from simple_role_notify where cloak=?', (clk,)) + def add_simple_rolemsg(clk): with conn: c.execute('INSERT into simple_role_notify VALUES (?)', (clk,)) + def remove_ping(clk): with conn: c.execute('DELETE from ping where cloak=?', (clk,)) - + + def add_ping(clk): with conn: c.execute('INSERT into ping VALUES (?)', (clk,)) @@ -271,8 +292,10 @@ def update_role_stats(acc, role, won, iwon): with conn: wins, iwins, total = 0, 0, 0 - c.execute(("SELECT teamwins, individualwins, totalgames FROM rolestats "+ - "WHERE player=? AND role=?"), (acc, role)) + c.execute( + ("SELECT teamwins, individualwins, totalgames FROM rolestats " + + "WHERE player=? AND role=?"), + (acc, role)) row = c.fetchone() if row: wins, iwins, total = row @@ -286,75 +309,106 @@ def update_role_stats(acc, role, won, iwon): c.execute("INSERT OR REPLACE INTO rolestats VALUES (?,?,?,?,?)", (acc, role, wins, iwins, total)) + def update_game_stats(size, winner): with conn: vwins, wwins, total = 0, 0, 0 - - c.execute("SELECT villagewins, wolfwins, totalgames FROM gamestats "+ - "WHERE size=?", (size,)) + + c.execute("SELECT villagewins, wolfwins, totalgames FROM gamestats " + + "WHERE size=?", (size,)) row = c.fetchone() if row: vwins, wwins, total = row - + if winner == "wolves": wwins += 1 elif winner == "villagers": vwins += 1 total += 1 - + c.execute("INSERT OR REPLACE INTO gamestats VALUES (?,?,?,?)", - (size, vwins, wwins, total)) - + (size, vwins, wwins, total)) + + def get_player_stats(acc, role): - if role.lower() not in ["villager"] + [v.lower() for k, v in ROLE_INDICES.items()]: + if role.lower() not in ["villager"] + [v.lower() + for k, v in ROLE_INDICES.items()]: return "No such role: {0}".format(role) with conn: - c.execute("SELECT player FROM rolestats WHERE player=? COLLATE NOCASE", (acc,)) + c.execute( + "SELECT player FROM rolestats WHERE player=? COLLATE NOCASE", + (acc,)) player = c.fetchone() if player: for row in c.execute("SELECT * FROM rolestats WHERE player=? COLLATE NOCASE AND role=? COLLATE NOCASE", (acc, role)): - msg = "\u0002{0}\u0002 as \u0002{1}\u0002 | Team wins: {2} (%d%%), Individual wins: {3} (%d%%), Total games: {4}".format(*row) - return msg % (round(row[2]/row[4] * 100), round(row[3]/row[4] * 100)) + msg = "\u0002{0}\u0002 as \u0002{1}\u0002 | Team wins: {2} (%d%%), Individual wins: {3} (%d%%), Total games: {4}".format( + * + row) + return msg % ( + round(row[2] / row[4] * 100), + round(row[3] / row[4] * 100)) else: return "No stats for {0} as {1}.".format(player[0], role) return "{0} has not played any games.".format(acc) + def get_player_totals(acc): role_totals = [] with conn: - c.execute("SELECT player FROM rolestats WHERE player=? COLLATE NOCASE", (acc,)) + c.execute( + "SELECT player FROM rolestats WHERE player=? COLLATE NOCASE", + (acc,)) player = c.fetchone() if player: for role in ["villager"] + [v for k, v in ROLE_INDICES.items()]: - c.execute("SELECT totalgames FROM rolestats WHERE player=? COLLATE NOCASE AND role=? COLLATE NOCASE", (acc, role)) + c.execute( + "SELECT totalgames FROM rolestats WHERE player=? COLLATE NOCASE AND role=? COLLATE NOCASE", + (acc, role)) row = c.fetchone() if row: - role_totals.append("\u0002{0}\u0002: {1}".format(role, *row)) - c.execute("SELECT SUM(totalgames) from rolestats WHERE player=? COLLATE NOCASE AND role!='cursed villager' AND role!='gunner'", (acc,)) + role_totals.append( + "\u0002{0}\u0002: {1}".format( + role, + * + row)) + c.execute( + "SELECT SUM(totalgames) from rolestats WHERE player=? COLLATE NOCASE AND role!='cursed villager' AND role!='gunner'", + (acc,)) row = c.fetchone() - return "\u0002{0}\u0002's totals | \u0002{1}\u0002 games | {2}".format(player[0], row[0], ", ".join(role_totals)) + return "\u0002{0}\u0002's totals | \u0002{1}\u0002 games | {2}".format( + player[0], + row[0], + ", ".join(role_totals)) else: return "\u0002{0}\u0002 has not played any games.".format(acc) - + + def get_game_stats(size): with conn: for row in c.execute("SELECT * FROM gamestats WHERE size=?", (size,)): - msg = "\u0002{0}\u0002 player games | Village wins: {1} (%d%%), Wolf wins: {2} (%d%%), Total games: {3}".format(*row) - return msg % (round(row[1]/row[3] * 100), round(row[2]/row[3] * 100)) + msg = "\u0002{0}\u0002 player games | Village wins: {1} (%d%%), Wolf wins: {2} (%d%%), Total games: {3}".format( + * + row) + return msg % ( + round(row[1] / row[3] * 100), + round(row[2] / row[3] * 100)) else: return "No stats for \u0002{0}\u0002 player games.".format(size) + def get_game_totals(): size_totals = [] total = 0 with conn: for size in range(MIN_PLAYERS, MAX_PLAYERS + 1): - c.execute("SELECT size, totalgames FROM gamestats WHERE size=?", (size,)) + c.execute( + "SELECT size, totalgames FROM gamestats WHERE size=?", + (size,)) row = c.fetchone() if row: size_totals.append("\u0002{0}p\u0002: {1}".format(*row)) total += row[1] - + if len(size_totals) == 0: return "No games have been played." else: diff --git a/tools/decorators.py b/tools/decorators.py index 1289017..740d80c 100644 --- a/tools/decorators.py +++ b/tools/decorators.py @@ -1,17 +1,30 @@ # Copyright (c) 2011, Jimmy Cao # All rights reserved. -# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: # Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. # Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from oyoyo.parse import parse_nick import fnmatch import botconfig + def generate(fdict, permissions=True, **kwargs): """Generates a decorator generator. Always use this""" def cmd(*s, raw_nick=False, admin_only=False, owner_only=False, hookid=-1): @@ -26,10 +39,10 @@ def generate(fdict, permissions=True, **kwargs): else: nick = "" cloak = "" - + if not raw_nick and len(largs) > 1 and largs[1]: largs[1] = nick - #if largs[1].startswith("#"): + # if largs[1].startswith("#"): if not permissions or "" in s: return f(*largs) if cloak: @@ -37,7 +50,9 @@ def generate(fdict, permissions=True, **kwargs): if fnmatch.fnmatch(cloak.lower(), pattern.lower()): for cmdname in s: if cmdname in botconfig.DENY[pattern]: - largs[0].notice(nick, "You do not have permission to use that command.") + largs[0].notice( + nick, + "You do not have permission to use that command.") return for pattern in botconfig.ALLOW.keys(): if fnmatch.fnmatch(cloak.lower(), pattern.lower()): @@ -45,15 +60,20 @@ def generate(fdict, permissions=True, **kwargs): if cmdname in botconfig.ALLOW[pattern]: return f(*largs) # no questions if owner_only: - if cloak and [ptn for ptn in botconfig.OWNERS - if fnmatch.fnmatch(cloak.lower(), ptn.lower())]: + if cloak and [ + ptn for ptn in botconfig.OWNERS if fnmatch.fnmatch( + cloak.lower(), + ptn.lower())]: return f(*largs) elif cloak: largs[0].notice(nick, "You are not the owner.") return if admin_only: - if cloak and [ptn for ptn in botconfig.ADMINS+botconfig.OWNERS - if fnmatch.fnmatch(cloak.lower(), ptn.lower())]: + if cloak and[ptn + for ptn in botconfig.ADMINS + botconfig.OWNERS + if fnmatch.fnmatch( + cloak.lower(), + ptn.lower())]: return f(*largs) elif cloak: largs[0].notice(nick, "You are not an admin.") @@ -67,8 +87,11 @@ def generate(fdict, permissions=True, **kwargs): else: for fn in fdict[x]: if (fn.owner_only != owner_only or - fn.admin_only != admin_only): - raise Exception("Command: "+x+" has non-matching protection levels!") + fn.admin_only != admin_only): + raise Exception( + "Command: " + + x + + " has non-matching protection levels!") fdict[x].append(innerf) if alias: innerf.aliases.append(x) @@ -79,12 +102,13 @@ def generate(fdict, permissions=True, **kwargs): innerf.hookid = hookid innerf.__doc__ = f.__doc__ return innerf - + return dec - - return lambda *args, **kwarargs: cmd(*args, **kwarargs) if kwarargs else cmd(*args, **kwargs) - - + + return lambda *args, **kwarargs: cmd(*args, ** + kwarargs) if kwarargs else cmd(*args, **kwargs) + + def unhook(hdict, hookid): for cmd in list(hdict.keys()): for x in hdict[cmd]: diff --git a/tools/moduleloader.py b/tools/moduleloader.py index 39603ab..e4bcc4c 100644 --- a/tools/moduleloader.py +++ b/tools/moduleloader.py @@ -10,15 +10,15 @@ for modfile in os.listdir("modules"): continue if not modfile.endswith(".py"): continue # not a module - if not os.path.isfile("modules/"+modfile): + if not os.path.isfile("modules/" + modfile): continue # not a file - + modfile = modfile[:-3] - - print("Loading module "+modfile) - - MODULES[modfile] = getattr(__import__("modules."+modfile), modfile) - + + print("Loading module " + modfile) + + MODULES[modfile] = getattr(__import__("modules." + modfile), modfile) + if botconfig.DEFAULT_MODULE in MODULES.keys(): CURRENT_MODULE = botconfig.DEFAULT_MODULE.lower() else: diff --git a/tools/wolfgamelogger.py b/tools/wolfgamelogger.py index f50918d..1ebb180 100644 --- a/tools/wolfgamelogger.py +++ b/tools/wolfgamelogger.py @@ -1,38 +1,42 @@ import botconfig from datetime import datetime + class WolfgameLogger(object): def __init__(self, outfile, boutfile): self.outfile = outfile self.boutfile = boutfile - + self.logged = "" self.barelogged = "" - + def log(self, message): self.logged += datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S ") + message + "\n" - + def logBare(self, *args): self.barelogged += datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S ") + " ".join(args) + "\n" - + def logChannelMessage(self, who, message): - self.logged += datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S ") + "<{0}> {1}\n".format(who, message) - + self.logged += datetime.utcnow().strftime( + "%Y-%m-%d %H:%M:%S ") + "<{0}> {1}\n".format(who, message) + def logCommand(self, who, cmd, rest): - self.logged += datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S ") + "<{0}> {1}{2} {3}".format(who, botconfig.CMD_CHAR, cmd, rest) + "\n" - + self.logged += datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S ") + \ + "<{0}> {1}{2} {3}".format(who, botconfig.CMD_CHAR, cmd, rest) + "\n" + def logMessage(self, message): - self.logged += datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S ") + "<{0}> ".format(botconfig.NICK)+message+"\n" - + self.logged += datetime.utcnow().strftime( + "%Y-%m-%d %H:%M:%S ") + "<{0}> ".format(botconfig.NICK) + message + "\n" + def saveToFile(self): if self.outfile: with open(self.outfile, "a") as lf: lf.write(self.logged) - + if self.boutfile: with open(self.boutfile, "a") as bl: bl.write(self.barelogged) - + self.logged = "" self.barelogged = "" diff --git a/wolfbot.py b/wolfbot.py index d007763..c25c9a0 100755 --- a/wolfbot.py +++ b/wolfbot.py @@ -49,17 +49,17 @@ def main(): handler.setFormatter(formatter) cli = IRCClient( - {"privmsg": modules.common.on_privmsg, - "notice": lambda a, b, c, d: modules.common.on_privmsg(a, b, c, d, True), - "": modules.common.__unhandled__}, - host=botconfig.HOST, - port=botconfig.PORT, - authname=botconfig.USERNAME, - password=botconfig.PASS, - nickname=botconfig.NICK, - sasl_auth=botconfig.SASL_AUTHENTICATION, - use_ssl=botconfig.USE_SSL, - connect_cb=modules.common.connect_callback + {"privmsg": modules.common.on_privmsg, + "notice": lambda a, b, c, d: modules.common.on_privmsg(a, b, c, d, True), + "": modules.common.__unhandled__}, + host=botconfig.HOST, + port=botconfig.PORT, + authname=botconfig.USERNAME, + password=botconfig.PASS, + nickname=botconfig.NICK, + sasl_auth=botconfig.SASL_AUTHENTICATION, + use_ssl=botconfig.USE_SSL, + connect_cb=modules.common.connect_callback ) cli.mainLoop()