Merge pull request #145 from lykoss/new-decorators

Change decorators
This commit is contained in:
Ryan Schmidt 2015-06-04 15:43:12 -07:00
commit 562fba2029
3 changed files with 268 additions and 360 deletions

View File

@ -1,167 +1,191 @@
# 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:
# 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.
import fnmatch import fnmatch
from collections import defaultdict
import botconfig import botconfig
import src.settings as var
from oyoyo.parse import parse_nick from oyoyo.parse import parse_nick
from src import settings as var
from src import logger from src import logger
adminlog = logger(None) adminlog = logger(None)
def generate(fdict, permissions=True, **kwargs): COMMANDS = defaultdict(list)
"""Generates a decorator generator. Always use this""" HOOKS = defaultdict(list)
def cmd(*s, raw_nick=False, admin_only=False, owner_only=False, chan=True, pm=False,
game=False, join=False, none=False, playing=False, roles=(), hookid=-1): class cmd:
def dec(f): def __init__(self, *cmds, raw_nick=False, admin_only=False, owner_only=False,
def innerf(*args): chan=True, pm=False, playing=False, silenced=False, phases=(), roles=()):
self.cmds = cmds
self.raw_nick = raw_nick
self.admin_only = admin_only
self.owner_only = owner_only
self.chan = chan
self.pm = pm
self.playing = playing
self.silenced = silenced
self.phases = phases
self.roles = roles
self.func = None
self.aftergame = False
self.name = cmds[0]
alias = False
self.aliases = []
for name in cmds:
for func in COMMANDS[name]:
if (func.owner_only != owner_only or
func.admin_only != admin_only):
raise ValueError("unmatching protection levels for " + func.name)
COMMANDS[name].append(self)
if alias:
self.aliases.append(self)
alias = True
def __call__(self, func):
self.func = func
self.__doc__ = self.func.__doc__
return self
def caller(self, *args):
largs = list(args) largs = list(args)
rawnick = largs[1]
if not permissions: cli, rawnick, chan, rest = largs
return f(*largs) nick, mode, user, cloak = parse_nick(rawnick)
if len(largs) > 1 and largs[1]:
nick, _, _, cloak = parse_nick(largs[1])
if cloak is None: if cloak is None:
cloak = "" cloak = ""
else:
nick = "" if not self.raw_nick:
cloak = ""
if not raw_nick and len(largs) > 1 and largs[1]:
largs[1] = nick largs[1] = nick
if nick == "<console>":
return f(*largs) # special case; no questions if not self.pm and chan == nick:
if not pm and largs[2] == nick: # PM command return # PM command, not allowed
return
if not chan and largs[2] != nick: # channel command if not self.chan and chan != nick:
return return # channel command, not allowed
if largs[2].startswith("#") and largs[2] != botconfig.CHANNEL and not admin_only and not owner_only:
if "" in s: if chan.startswith("#") and chan != botconfig.CHANNEL and not (admin_only or owner_only):
return # Don't have empty commands triggering in other channels if "" in self.cmds:
allowed = False return # don't have empty commands triggering in other channels
for cmdname in s: for command in self.cmds:
if cmdname in botconfig.ALLOWED_ALT_CHANNELS_COMMANDS: if command in botconfig.ALLOWED_ALT_CHANNELS_COMMANDS:
allowed = True
break break
if not allowed: else:
return return
if nick in var.USERS.keys() and var.USERS[nick]["account"] != "*":
if nick in var.USERS and var.USERS[nick]["account"] != "*":
acc = var.USERS[nick]["account"] acc = var.USERS[nick]["account"]
else: else:
acc = None acc = None
if "" in s:
return f(*largs) if "" in self.cmds:
if game and var.PHASE not in ("day", "night") + (("join",) if join else ()): return self.func(*largs)
largs[0].notice(nick, "No game is currently running.")
if self.phases and var.PHASE not in self.phases:
return return
if ((join and none and var.PHASE not in ("join", "none"))
or (none and not join and var.PHASE != "none")): if self.playing and (nick not in var.list_players() or nick in var.DISCONNECTED):
largs[0].notice(nick, "Sorry, but the game is already running. Try again next time.") if chan == nick:
pm(cli, nick, "You're not currently playing.")
else:
cli.notice(nick, "You're not currently playing.")
return return
if join and not none:
if var.PHASE == "none": if self.silenced and nick in var.SILENCED:
largs[0].notice(nick, "No game is currently running.") if chan == nick:
pm(cli, nick, "You have been silenced, and are unable to use any special powers.")
else:
cli.notice(nick, "You have been silenced, and are unable to use any special powers.")
return return
if var.PHASE != "join" and not game:
largs[0].notice(nick, "Werewolf is already in play.") if self.roles:
return for role in self.roles:
if playing and (nick not in var.list_players() or nick in var.DISCONNECTED.keys()):
largs[0].notice(nick, "You're not currently playing.")
return
if roles:
for role in roles:
if nick in var.ROLES[role]: if nick in var.ROLES[role]:
break break
else: else:
return return
if acc:
for pattern in var.DENY_ACCOUNTS.keys(): return self.func(*largs) # don't check restrictions for role commands
if fnmatch.fnmatch(acc.lower(), pattern.lower()):
for cmdname in s: if self.owner_only:
if cmdname in var.DENY_ACCOUNTS[pattern]:
largs[0].notice(nick, "You do not have permission to use that command.")
return
for pattern in var.ALLOW_ACCOUNTS.keys():
if fnmatch.fnmatch(acc.lower(), pattern.lower()):
for cmdname in s:
if cmdname in var.ALLOW_ACCOUNTS[pattern]:
if admin_only or owner_only:
adminlog(largs[2], rawnick, s[0], largs[3])
return f(*largs)
if not var.ACCOUNTS_ONLY and cloak:
for pattern in var.DENY.keys():
if fnmatch.fnmatch(cloak.lower(), pattern.lower()):
for cmdname in s:
if cmdname in var.DENY[pattern]:
largs[0].notice(nick, "You do not have permission to use that command.")
return
for pattern in var.ALLOW.keys():
if fnmatch.fnmatch(cloak.lower(), pattern.lower()):
for cmdname in s:
if cmdname in var.ALLOW[pattern]:
if admin_only or owner_only:
adminlog(largs[2], rawnick, s[0], largs[3])
return f(*largs) # no questions
if owner_only:
if var.is_owner(nick, cloak): if var.is_owner(nick, cloak):
adminlog(largs[2], rawnick, s[0], largs[3]) adminlog(chan, rawnick, self.name, rest)
return f(*largs) return self.func(*largs)
if chan == nick:
pm(cli, nick, "You are not the owner.")
else: else:
largs[0].notice(nick, "You are not the owner.") cli.notice(nick, "You are not the owner.")
return return
if admin_only:
if not self.admin_only:
return self.func(*largs)
if var.is_admin(nick, cloak): if var.is_admin(nick, cloak):
adminlog(largs[2], rawnick, s[0], largs[3]) adminlog(chan, rawnick, self.name, rest)
return f(*largs) return self.func(*largs)
if acc:
for pattern in var.DENY_ACCOUNTS:
if fnmatch.fnmatch(acc.lower(), pattern.lower()):
for command in self.cmds:
if command in var.DENY_ACCOUNTS[pattern]:
if chan == nick:
pm(cli, nick, "You do not have permission to use that command.")
else: else:
largs[0].notice(nick, "You are not an admin.") cli.notice(nick, "You do not have permission to use that command.")
return return
return f(*largs)
alias = False for pattern in var.ALLOW_ACCOUNTS:
innerf.aliases = [] if fnmatch.fnmatch(acc.lower(), pattern.lower()):
for x in s: for command in self.cmds:
if x not in fdict.keys(): if command in var.ALLOW_ACCOUNTS[pattern]:
fdict[x] = [] adminlog(chan, rawnick, self.name, rest)
return self.func(*largs)
if not var.ACCOUNTS_ONLY and cloak:
for pattern in var.DENY:
if fnmatch.fnmatch(cloak.lower(), pattern.lower()):
for command in self.cmds:
if command in var.DENY[pattern]:
if chan == nick:
pm(cli, nick, "You do not have permission to use that command.")
else: else:
for fn in fdict[x]: cli.notice(nick, "You do not have permission to use that command.")
if (fn.owner_only != owner_only or return
fn.admin_only != admin_only):
raise Exception("Command: "+x+" has non-matching protection levels!")
fdict[x].append(innerf)
if alias:
innerf.aliases.append(x)
alias = True
innerf.owner_only = owner_only
innerf.raw_nick = raw_nick
innerf.admin_only = admin_only
innerf.chan = chan
innerf.pm = pm
innerf.none = none
innerf.join = join
innerf.game = game
innerf.playing = playing
innerf.roles = roles
innerf.hookid = hookid
innerf.aftergame = False
innerf.__doc__ = f.__doc__
return innerf
return dec for pattern in var.ALLOW:
if fnmatch.fnmatch(cloak.lower(), pattern.lower()):
for command in self.cmds:
if command in var.ALLOW[pattern]:
adminlog(chan, rawnick, self.name, rest)
return self.func(*largs)
return lambda *args, **kwarargs: cmd(*args, **kwarargs) if kwarargs else cmd(*args, **kwargs) if chan == nick:
pm(cli, nick, "You are not an admin.")
else:
cli.notice(nick, "You are not an admin.")
return
class hook:
def __init__(self, name, hookid=-1):
self.name = name
self.hookid = hookid
self.func = None
def unhook(hdict, hookid): HOOKS[name].append(self)
for cmd in list(hdict.keys()):
for x in hdict[cmd]: def __call__(self, func):
if x.hookid == hookid: self.func = func
hdict[cmd].remove(x) self.__doc__ = self.func.__doc__
if not hdict[cmd]: return self
del hdict[cmd]
@staticmethod
def unhook(hookid):
for each in list(HOOKS):
for inner in list(HOOKS[each]):
if inner.hookid == hookid:
HOOKS[each].remove(inner)
if not HOOKS[each]:
del HOOKS[each]

View File

@ -15,6 +15,7 @@ from src import wolfgame
log = logger("errors.log") log = logger("errors.log")
alog = logger(None) alog = logger(None)
hook = decorators.hook
def notify_error(cli, chan, target_logger): def notify_error(cli, chan, target_logger):
msg = "An error has occurred and has been logged." msg = "An error has occurred and has been logged."
@ -56,10 +57,9 @@ def on_privmsg(cli, rawnick, chan, msg, notice = False):
if chan == botconfig.NICK: if chan == botconfig.NICK:
chan = parse_nick(rawnick)[0] chan = parse_nick(rawnick)[0]
if "" in wolfgame.COMMANDS.keys(): for fn in decorators.COMMANDS[""]:
for fn in wolfgame.COMMANDS[""]:
try: try:
fn(cli, rawnick, chan, msg) fn.caller(cli, rawnick, chan, msg)
except Exception: except Exception:
if botconfig.DEBUG_MODE: if botconfig.DEBUG_MODE:
raise raise
@ -67,7 +67,7 @@ def on_privmsg(cli, rawnick, chan, msg, notice = False):
notify_error(cli, chan, log) notify_error(cli, chan, log)
for x in set(list(COMMANDS.keys()) + list(wolfgame.COMMANDS.keys())): for x in decorators.COMMANDS:
if chan != parse_nick(rawnick)[0] and not msg.lower().startswith(botconfig.CMD_CHAR): if chan != parse_nick(rawnick)[0] and not msg.lower().startswith(botconfig.CMD_CHAR):
break # channel message but no prefix; ignore break # channel message but no prefix; ignore
if msg.lower().startswith(botconfig.CMD_CHAR+x): if msg.lower().startswith(botconfig.CMD_CHAR+x):
@ -77,9 +77,9 @@ def on_privmsg(cli, rawnick, chan, msg, notice = False):
else: else:
continue continue
if not h or h[0] == " ": if not h or h[0] == " ":
for fn in COMMANDS.get(x, []) + (wolfgame.COMMANDS.get(x, [])): for fn in decorators.COMMANDS.get(x, []):
try: try:
fn(cli, rawnick, chan, h.lstrip()) fn.caller(cli, rawnick, chan, h.lstrip())
except Exception: except Exception:
if botconfig.DEBUG_MODE: if botconfig.DEBUG_MODE:
raise raise
@ -88,26 +88,19 @@ def on_privmsg(cli, rawnick, chan, msg, notice = False):
def unhandled(cli, prefix, cmd, *args): def unhandled(cli, prefix, cmd, *args):
if cmd in set(list(HOOKS.keys()) + list(wolfgame.HOOKS.keys())): if cmd in decorators.HOOKS:
largs = list(args) largs = list(args)
for i,arg in enumerate(largs): for i,arg in enumerate(largs):
if isinstance(arg, bytes): largs[i] = arg.decode('ascii') if isinstance(arg, bytes): largs[i] = arg.decode('ascii')
for fn in HOOKS.get(cmd, []) + wolfgame.HOOKS.get(cmd, []): for fn in decorators.HOOKS.get(cmd, []):
try: try:
fn(cli, prefix, *largs) fn.func(cli, prefix, *largs)
except Exception as e: except Exception as e:
if botconfig.DEBUG_MODE: if botconfig.DEBUG_MODE:
raise e raise e
else: else:
notify_error(cli, botconfig.CHANNEL, log) notify_error(cli, botconfig.CHANNEL, log)
COMMANDS = {}
HOOKS = {}
cmd = decorators.generate(COMMANDS)
hook = decorators.generate(HOOKS, raw_nick=True, permissions=False)
def connect_callback(cli): def connect_callback(cli):
def prepare_stuff(*args): def prepare_stuff(*args):
@ -147,7 +140,7 @@ def connect_callback(cli):
cli.nick(botconfig.NICK+"_") cli.nick(botconfig.NICK+"_")
cli.user(botconfig.NICK, "") cli.user(botconfig.NICK, "")
decorators.unhook(HOOKS, 239) hook.unhook(HOOKS, 239)
hook("unavailresource")(mustrelease) hook("unavailresource")(mustrelease)
hook("nicknameinuse")(mustregain) hook("nicknameinuse")(mustregain)

View File

@ -49,14 +49,12 @@ debuglog = logger("debug.log", write=False, display=False) # will be True if in
errlog = logger("errors.log") errlog = logger("errors.log")
plog = logger(None) #use this instead of print so that logs have timestamps plog = logger(None) #use this instead of print so that logs have timestamps
COMMANDS = {}
HOOKS = {}
is_admin = var.is_admin is_admin = var.is_admin
is_owner = var.is_owner is_owner = var.is_owner
cmd = decorators.generate(COMMANDS) cmd = decorators.cmd
hook = decorators.generate(HOOKS, raw_nick=True, permissions=False) hook = decorators.hook
COMMANDS = decorators.COMMANDS
# Game Logic Begins: # Game Logic Begins:
@ -137,12 +135,12 @@ def connect_callback(cli):
def sighandler(signum, frame): def sighandler(signum, frame):
if signum in (signal.SIGINT, signal.SIGTERM): if signum in (signal.SIGINT, signal.SIGTERM):
forced_exit(cli, "<console>", botconfig.CHANNEL, "") forced_exit.func(cli, "<console>", botconfig.CHANNEL, "")
elif signum == SIGUSR1: elif signum == SIGUSR1:
restart_program(cli, "<console>", botconfig.CHANNEL, "") restart_program.func(cli, "<console>", botconfig.CHANNEL, "")
elif signum == SIGUSR2: elif signum == SIGUSR2:
plog("Scheduling aftergame restart") plog("Scheduling aftergame restart")
aftergame(cli, "<console>", botconfig.CHANNEL, "frestart") aftergame.func(cli, "<console>", botconfig.CHANNEL, "frestart")
signal.signal(signal.SIGINT, sighandler) signal.signal(signal.SIGINT, sighandler)
signal.signal(signal.SIGTERM, sighandler) signal.signal(signal.SIGTERM, sighandler)
@ -228,7 +226,7 @@ def connect_callback(cli):
notify_error(cli, botconfig.CHANNEL, errlog) notify_error(cli, botconfig.CHANNEL, errlog)
# Unhook the WHO hooks # Unhook the WHO hooks
decorators.unhook(HOOKS, 295) hook.unhook(295)
#bot can be tricked into thinking it's still opped by doing multiple modes at once #bot can be tricked into thinking it's still opped by doing multiple modes at once
@ -378,6 +376,8 @@ def pm(cli, target, message): # message either privmsg or notice, depending on
cli.msg(target, message) cli.msg(target, message)
decorators.pm = pm
def reset_settings(): def reset_settings():
var.CURRENT_GAMEMODE.teardown() var.CURRENT_GAMEMODE.teardown()
var.CURRENT_GAMEMODE = var.GAME_MODES["default"][0]() var.CURRENT_GAMEMODE = var.GAME_MODES["default"][0]()
@ -902,7 +902,7 @@ def join_timer_handler(cli):
# I was lucky to catch this in testing, as it requires precise timing # I was lucky to catch this in testing, as it requires precise timing
# it only failed if a join happened while this outer func had started # it only failed if a join happened while this outer func had started
var.PINGING_IFS = False var.PINGING_IFS = False
decorators.unhook(HOOKS, 387) hook.unhook(387)
if to_ping: if to_ping:
to_ping.sort(key=lambda x: x.lower()) to_ping.sort(key=lambda x: x.lower())
@ -913,7 +913,7 @@ def join_timer_handler(cli):
cli.who(botconfig.CHANNEL, "%uhsnfa") cli.who(botconfig.CHANNEL, "%uhsnfa")
@cmd("join", "j", none=True, join=True) @cmd("join", "j", phases=("none", "join"))
def join(cli, nick, chan, rest): def join(cli, nick, chan, rest):
"""Either starts a new game of Werewolf or joins an existing game that has not started yet.""" """Either starts a new game of Werewolf or joins an existing game that has not started yet."""
if var.ACCOUNTS_ONLY: if var.ACCOUNTS_ONLY:
@ -1071,7 +1071,7 @@ def kill_join(cli, chan):
var.AFTER_FLASTGAME = None var.AFTER_FLASTGAME = None
@cmd("fjoin", admin_only=True, none=True, join=True) @cmd("fjoin", admin_only=True, phases=("none", "join"))
def fjoin(cli, nick, chan, rest): def fjoin(cli, nick, chan, rest):
"""Forces someone to join a game.""" """Forces someone to join a game."""
noticed = False noticed = False
@ -1107,7 +1107,7 @@ def fjoin(cli, nick, chan, rest):
if fake: if fake:
cli.msg(chan, "\u0002{0}\u0002 used fjoin and raised the number of players to \u0002{1}\u0002.".format(nick, len(var.list_players()))) cli.msg(chan, "\u0002{0}\u0002 used fjoin and raised the number of players to \u0002{1}\u0002.".format(nick, len(var.list_players())))
@cmd("fleave", "fquit", admin_only=True, join=True, game=True) @cmd("fleave", "fquit", admin_only=True, phases=("join", "day", "night"))
def fleave(cli, nick, chan, rest): def fleave(cli, nick, chan, rest):
"""Forces someone to leave the game.""" """Forces someone to leave the game."""
if chan != botconfig.CHANNEL: if chan != botconfig.CHANNEL:
@ -1139,7 +1139,7 @@ def fleave(cli, nick, chan, rest):
del_player(cli, a, death_triggers = False) del_player(cli, a, death_triggers = False)
@cmd("fstart", admin_only=True, join=True) @cmd("fstart", admin_only=True, phases=("join",))
def fstart(cli, nick, chan, rest): def fstart(cli, nick, chan, rest):
"""Forces the game to start immediately.""" """Forces the game to start immediately."""
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))
@ -1185,7 +1185,7 @@ def on_account(cli, rnick, acc):
if nick in var.DCED_PLAYERS.keys(): if nick in var.DCED_PLAYERS.keys():
var.PLAYERS[nick] = var.DCED_PLAYERS.pop(nick) var.PLAYERS[nick] = var.DCED_PLAYERS.pop(nick)
@cmd("stats", "players", pm=True, game=True, join=True) @cmd("stats", "players", pm=True, phases=("join", "day", "night"))
def stats(cli, nick, chan, rest): def stats(cli, nick, chan, rest):
"""Displays the player statistics.""" """Displays the player statistics."""
@ -1545,7 +1545,7 @@ def chk_decision(cli, force = ""):
transition_night(cli) transition_night(cli)
break break
@cmd("votes", pm=True, game=True, join=True) @cmd("votes", pm=True, phases=("join", "day", "night"))
def show_votes(cli, nick, chan, rest): def show_votes(cli, nick, chan, rest):
"""Displays the voting statistics.""" """Displays the voting statistics."""
@ -2546,14 +2546,10 @@ def on_join(cli, raw_nick, chan, acc="*", rname=""):
cli.msg("ChanServ", "op " + chan) cli.msg("ChanServ", "op " + chan)
@cmd("goat", game=True, playing=True) @cmd("goat", playing=True, phases=("day",))
def goat(cli, nick, chan, rest): def goat(cli, nick, chan, rest):
"""Use a goat to interact with anyone in the channel during the day.""" """Use a goat to interact with anyone in the channel during the day."""
if var.PHASE != "day":
cli.notice(nick, "You can only do that in the day.")
return
if var.GOATED and nick not in var.SPECIAL_ROLES["goat herder"]: if var.GOATED and nick not in var.SPECIAL_ROLES["goat herder"]:
cli.notice(nick, "This can only be done once per day.") cli.notice(nick, "This can only be done once per day.")
return return
@ -2912,7 +2908,7 @@ hook("quit")(lambda cli, nick, *rest: leave(cli, "quit", nick, rest[0]))
hook("kick")(lambda cli, nick, *rest: leave(cli, "kick", rest[1], rest[0])) hook("kick")(lambda cli, nick, *rest: leave(cli, "kick", rest[1], rest[0]))
@cmd("quit", "leave", join=True, game=True, playing=True) @cmd("quit", "leave", playing=True, phases=("join", "day", "night"))
def leave_game(cli, nick, chan, rest): def leave_game(cli, nick, chan, rest):
"""Quits the game.""" """Quits the game."""
if var.PHASE == "join": if var.PHASE == "join":
@ -3568,7 +3564,7 @@ def chk_nightdone(cli):
if var.PHASE == "night": # Double check if var.PHASE == "night": # Double check
transition_day(cli) transition_day(cli)
@cmd("nolynch", "nl", "novote", "nv", "abstain", "abs", game=True, playing=True) @cmd("nolynch", "nl", "novote", "nv", "abstain", "abs", playing=True, phases=("day",))
def no_lynch(cli, nick, chan, rest): def no_lynch(cli, nick, chan, rest):
"""Allows you to abstain from voting for the day.""" """Allows you to abstain from voting for the day."""
if chan == botconfig.CHANNEL: if chan == botconfig.CHANNEL:
@ -3581,9 +3577,6 @@ def no_lynch(cli, nick, chan, rest):
elif var.LIMIT_ABSTAIN and var.FIRST_DAY: elif var.LIMIT_ABSTAIN and var.FIRST_DAY:
cli.notice(nick, "The village may not abstain on the first day.") cli.notice(nick, "The village may not abstain on the first day.")
return return
elif var.PHASE != "day":
cli.notice(nick, "Lynching is only during the day. Please wait patiently for morning.")
return
elif nick in var.WOUNDED: elif 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 return
@ -3600,21 +3593,17 @@ def no_lynch(cli, nick, chan, rest):
chk_decision(cli) chk_decision(cli)
return return
@cmd("lynch", game=True, playing=True, pm=True) @cmd("lynch", playing=True, pm=True, phases=("day",))
def lynch(cli, nick, chan, rest): def lynch(cli, nick, chan, rest):
"""Use this to vote for a candidate to be lynched.""" """Use this to vote for a candidate to be lynched."""
if not rest: if not rest:
show_votes(cli, nick, chan, rest) show_votes.caller(cli, nick, chan, rest)
return return
if chan != botconfig.CHANNEL: if chan != botconfig.CHANNEL:
return return
rest = re.split(" +",rest)[0].strip() rest = re.split(" +",rest)[0].strip()
if var.PHASE != "day":
cli.notice(nick, ("Lynching is only allowed during the day. "+
"Please wait patiently for morning."))
return
if nick in var.WOUNDED: if nick in var.WOUNDED:
cli.msg(chan, ("{0}: You are wounded and resting, "+ cli.msg(chan, ("{0}: You are wounded and resting, "+
"thus you are unable to vote for the day.").format(nick)) "thus you are unable to vote for the day.").format(nick))
@ -3950,11 +3939,11 @@ def check_exchange(cli, actor, nick):
return True return True
return False return False
@cmd("retract", pm=True, game=True, playing=True) @cmd("retract", pm=True, playing=True, phases=("day", "night"))
def retract(cli, nick, chan, rest): def retract(cli, nick, chan, rest):
"""Takes back your vote during the day (for whom to lynch).""" """Takes back your vote during the day (for whom to lynch)."""
if not chan in (botconfig.CHANNEL, nick): if chan not in (botconfig.CHANNEL, nick):
return return
if chan == nick: # PM, use different code if chan == nick: # PM, use different code
@ -3962,7 +3951,6 @@ def retract(cli, nick, chan, rest):
if role not in ("wolf", "werecrow", "alpha wolf", "werekitten", "hunter") and nick not in var.VENGEFUL_GHOSTS.keys(): if role not in ("wolf", "werecrow", "alpha wolf", "werekitten", "hunter") and nick not in var.VENGEFUL_GHOSTS.keys():
return return
if var.PHASE != "night": if var.PHASE != "night":
pm(cli, nick, "You may only retract at night.")
return return
if role == "werecrow": # Check if already observed if role == "werecrow": # Check if already observed
if var.OBSERVED.get(nick): if var.OBSERVED.get(nick):
@ -3992,8 +3980,6 @@ def retract(cli, nick, chan, rest):
return return
if var.PHASE != "day": if var.PHASE != "day":
cli.notice(nick, ("Lynching is only allowed during the day. "+
"Please wait patiently for morning."))
return return
if nick in var.NO_LYNCH: if nick in var.NO_LYNCH:
var.NO_LYNCH.remove(nick) var.NO_LYNCH.remove(nick)
@ -4013,17 +3999,13 @@ def retract(cli, nick, chan, rest):
else: else:
cli.notice(nick, "You haven't voted yet.") cli.notice(nick, "You haven't voted yet.")
@cmd("shoot", game=True, playing=True) @cmd("shoot", playing=True, silenced=True, phases=("day",))
def shoot(cli, nick, chan, rest): def shoot(cli, nick, chan, rest):
"""Use this to fire off a bullet at someone in the day if you have bullets.""" """Use this to fire off a bullet at someone in the day if you have bullets."""
if chan != botconfig.CHANNEL: if chan != botconfig.CHANNEL:
return return
if var.PHASE != "day":
cli.notice(nick, ("Shooting is only allowed during the day. "+
"Please wait patiently for morning."))
return
if nick not in var.GUNNERS.keys() and nick not in var.WOLF_GUNNERS.keys(): if nick not in var.GUNNERS.keys() and nick not in var.WOLF_GUNNERS.keys():
cli.notice(nick, "You don't have a gun.") cli.notice(nick, "You don't have a gun.")
return return
@ -4032,9 +4014,6 @@ def shoot(cli, nick, chan, rest):
nick in var.WOLF_GUNNERS.keys() and not var.WOLF_GUNNERS[nick])): nick in var.WOLF_GUNNERS.keys() and not var.WOLF_GUNNERS[nick])):
cli.notice(nick, "You don't have any more bullets.") cli.notice(nick, "You don't have any more bullets.")
return return
elif nick in var.SILENCED:
cli.notice(nick, "You have been silenced, and are unable to use any special powers.")
return
victim = get_victim(cli, nick, re.split(" +",rest)[0], True) victim = get_victim(cli, nick, re.split(" +",rest)[0], True)
if not victim: if not victim:
return return
@ -4118,7 +4097,7 @@ def shoot(cli, nick, chan, rest):
@cmd("kill", chan=False, pm=True, game=True) @cmd("kill", chan=False, pm=True, phases=("night",))
def kill(cli, nick, chan, rest): def kill(cli, nick, chan, rest):
"""Kill a player. Behaviour varies depending on your role.""" """Kill a player. Behaviour varies depending on your role."""
if (nick not in var.VENGEFUL_GHOSTS.keys() and nick not in var.list_players()) or nick in var.DISCONNECTED.keys(): if (nick not in var.VENGEFUL_GHOSTS.keys() and nick not in var.list_players()) or nick in var.DISCONNECTED.keys():
@ -4137,9 +4116,6 @@ def kill(cli, nick, chan, rest):
if nick in var.VENGEFUL_GHOSTS.keys() and var.VENGEFUL_GHOSTS[nick][0] == "!": if nick in var.VENGEFUL_GHOSTS.keys() and var.VENGEFUL_GHOSTS[nick][0] == "!":
# ghost was driven away by retribution # ghost was driven away by retribution
return return
if var.PHASE != "night":
pm(cli, nick, "You may only kill people at night.")
return
if role == "hunter" and nick in var.HUNTERS and nick not in var.OTHER_KILLS: if role == "hunter" and nick in var.HUNTERS and nick not in var.OTHER_KILLS:
# they are a hunter and did not kill this night (if they killed this night, this allows them to switch) # they are a hunter and did not kill this night (if they killed this night, this allows them to switch)
pm(cli, nick, "You have already killed someone this game.") pm(cli, nick, "You have already killed someone this game.")
@ -4240,15 +4216,9 @@ def kill(cli, nick, chan, rest):
debuglog("{0} ({1}) KILL : {2} ({3})".format(nick, role, victim2, var.get_role(victim2))) debuglog("{0} ({1}) KILL : {2} ({3})".format(nick, role, victim2, var.get_role(victim2)))
chk_nightdone(cli) chk_nightdone(cli)
@cmd("guard", "protect", "save", chan=False, pm=True, game=False, playing=True, roles=("bodyguard", "guardian angel")) @cmd("guard", "protect", "save", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("bodyguard", "guardian angel"))
def guard(cli, nick, chan, rest): def guard(cli, nick, chan, rest):
"""Guard a player, preventing them from being targetted that night.""" """Guard a player, preventing them from being targetted that night."""
if var.PHASE != "night":
pm(cli, nick, "You may only protect people at night.")
return
if nick in var.SILENCED:
pm(cli, nick, "You have been silenced, and are unable to use any special powers.")
return
if var.GUARDED.get(nick): if var.GUARDED.get(nick):
pm(cli, nick, "You are already protecting someone tonight.") pm(cli, nick, "You are already protecting someone tonight.")
return return
@ -4284,19 +4254,10 @@ def guard(cli, nick, chan, rest):
@cmd("observe", chan=False, pm=True, game=True, playing=True, roles=("werecrow", "sorcerer")) @cmd("observe", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("werecrow", "sorcerer"))
def observe(cli, nick, chan, rest): def observe(cli, nick, chan, rest):
"""Observe a player to obtain various information.""" """Observe a player to obtain various information."""
role = var.get_role(nick) role = var.get_role(nick)
if var.PHASE != "night":
if role == "werecrow":
pm(cli, nick, "You may only transform into a crow at night.")
else:
pm(cli, nick, "You may only observe at night.")
return
if nick in var.SILENCED:
pm(cli, nick, "You have been silenced, and are unable to use any special powers.")
return
victim = get_victim(cli, nick, re.split(" +",rest)[0], False) victim = get_victim(cli, nick, re.split(" +",rest)[0], False)
if not victim: if not victim:
return return
@ -4343,15 +4304,9 @@ def observe(cli, nick, chan, rest):
debuglog("{0} ({1}) OBSERVE: {2} ({3})".format(nick, role, victim, var.get_role(victim))) debuglog("{0} ({1}) OBSERVE: {2} ({3})".format(nick, role, victim, var.get_role(victim)))
chk_nightdone(cli) chk_nightdone(cli)
@cmd("id", chan=False, pm=True, game=True, playing=True, roles=("detective",)) @cmd("id", chan=False, pm=True, playing=True, silenced=True, phases=("day",), roles=("detective",))
def investigate(cli, nick, chan, rest): def investigate(cli, nick, chan, rest):
"""Investigate a player to determine their exact role.""" """Investigate a player to determine their exact role."""
if var.PHASE != "day":
pm(cli, nick, "You may only investigate people during the day.")
return
if nick in var.SILENCED:
pm(cli, nick, "You have been silenced, and are unable to use any special powers.")
return
if nick in var.INVESTIGATED: if nick in var.INVESTIGATED:
pm(cli, nick, "You may only investigate one person per round.") pm(cli, nick, "You may only investigate one person per round.")
return return
@ -4378,15 +4333,9 @@ def investigate(cli, nick, chan, rest):
"that \u0002{0}\u0002 is the detective!").format(nick)) "that \u0002{0}\u0002 is the detective!").format(nick))
debuglog("{0} ({1}) PAPER DROP".format(nick, var.get_role(nick))) debuglog("{0} ({1}) PAPER DROP".format(nick, var.get_role(nick)))
@cmd("visit", chan=False, pm=True, game=True, playing=True, roles=("harlot",)) @cmd("visit", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("harlot",))
def hvisit(cli, nick, chan, rest): def hvisit(cli, nick, chan, rest):
"""Visit a player. You will die if you visit a wolf or a target of the wolves.""" """Visit a player. You will die if you visit a wolf or a target of the wolves."""
if var.PHASE != "night":
pm(cli, nick, "You may only visit someone at night.")
return
if nick in var.SILENCED:
pm(cli, nick, "You have been silenced, and are unable to use any special powers.")
return
if var.HVISITED.get(nick): if var.HVISITED.get(nick):
pm(cli, nick, ("You are already spending the night "+ pm(cli, nick, ("You are already spending the night "+
"with \u0002{0}\u0002.").format(var.HVISITED[nick])) "with \u0002{0}\u0002.").format(var.HVISITED[nick]))
@ -4414,16 +4363,10 @@ def hvisit(cli, nick, chan, rest):
def is_fake_nick(who): def is_fake_nick(who):
return re.search(r"^[0-9]+$", who) return re.search(r"^[0-9]+$", who)
@cmd("see", chan=False, pm=True, game=True, playing=True, roles=("seer", "oracle", "augur")) @cmd("see", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("seer", "oracle", "augur"))
def see(cli, nick, chan, rest): def see(cli, nick, chan, rest):
"""Use your paranormal powers to determine the role or alignment of a player.""" """Use your paranormal powers to determine the role or alignment of a player."""
role = var.get_role(nick) role = var.get_role(nick)
if var.PHASE != "night":
pm(cli, nick, "You may only have visions at night.")
return
if nick in var.SILENCED:
pm(cli, nick, "You have been silenced, and are unable to use any special powers.")
return
if nick in var.SEEN: if nick in var.SEEN:
pm(cli, nick, "You may only have one vision per round.") pm(cli, nick, "You may only have one vision per round.")
return return
@ -4473,24 +4416,18 @@ def see(cli, nick, chan, rest):
var.SEEN.append(nick) var.SEEN.append(nick)
chk_nightdone(cli) chk_nightdone(cli)
@cmd("give", chan=False, pm=True, game=True, playing=True, roles=var.TOTEM_ORDER+("doctor",)) @cmd("give", chan=False, pm=True, playing=True, silenced=True, phases=("day", "night"), roles=var.TOTEM_ORDER+("doctor",))
def give(cli, nick, chan, rest): def give(cli, nick, chan, rest):
"""Give a totem or immunization to a player.""" """Give a totem or immunization to a player."""
role = var.get_role(nick) role = var.get_role(nick)
if role in var.TOTEM_ORDER: if role in var.TOTEM_ORDER:
totem(cli, nick, chan, rest) totem.caller(cli, nick, chan, rest)
elif role == "doctor": elif role == "doctor":
immunize(cli, nick, chan, rest) immunize.caller(cli, nick, chan, rest)
@cmd("totem", chan=False, pm=True, game=True, playing=True, roles=var.TOTEM_ORDER) @cmd("totem", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=var.TOTEM_ORDER)
def totem(cli, nick, chan, rest): def totem(cli, nick, chan, rest):
"""Give a totem to a player.""" """Give a totem to a player."""
if var.PHASE != "night":
pm(cli, nick, "You may only give totems at night.")
return
if nick in var.SILENCED:
pm(cli, nick, "You have been silenced, and are unable to use any special powers.")
return
if nick in var.SHAMANS: if nick in var.SHAMANS:
pm(cli, nick, "You have already given out your totem this round.") pm(cli, nick, "You have already given out your totem this round.")
return return
@ -4559,15 +4496,9 @@ def totem(cli, nick, chan, rest):
debuglog("{0} ({1}) TOTEM: {2} ({3})".format(nick, role, victim, totem)) debuglog("{0} ({1}) TOTEM: {2} ({3})".format(nick, role, victim, totem))
chk_nightdone(cli) chk_nightdone(cli)
@cmd("immunize", "immunise", chan=False, pm=True, game=True, playing=True, roles=("doctor",)) @cmd("immunize", "immunise", chan=False, pm=True, playing=True, silenced=True, phases=("day",), roles=("doctor",))
def immunize(cli, nick, chan, rest): def immunize(cli, nick, chan, rest):
"""Immunize a player, preventing them from turning into a wolf.""" """Immunize a player, preventing them from turning into a wolf."""
if var.PHASE != "day":
pm(cli, nick, "You may only immunize people during the day.")
return
if nick in var.SILENCED:
pm(cli, nick, "You have been silenced, and are unable to use any special powers.")
return
if nick in var.DOCTORS and var.DOCTORS[nick] == 0: if nick in var.DOCTORS and var.DOCTORS[nick] == 0:
pm(cli, nick, "You have run out of immunizations.") pm(cli, nick, "You have run out of immunizations.")
return return
@ -4628,18 +4559,12 @@ def get_bitten_message(nick):
"who or what the pursuer was, you woke with a start.") "who or what the pursuer was, you woke with a start.")
return message return message
@cmd("bite", chan=False, pm=True, game=True, playing=True, roles=("alpha wolf",)) @cmd("bite", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("alpha wolf",))
def bite_cmd(cli, nick, chan, rest): def bite_cmd(cli, nick, chan, rest):
"""Bite a player, turning them into a wolf after a certain number of nights.""" """Bite a player, turning them into a wolf after a certain number of nights."""
if var.PHASE != "night":
pm(cli, nick, "You may only bite at night.")
return
if nick in var.ALPHA_WOLVES and nick not in var.BITE_PREFERENCES: if nick in var.ALPHA_WOLVES and nick not in var.BITE_PREFERENCES:
pm(cli, nick, "You have already bitten someone this game.") pm(cli, nick, "You have already bitten someone this game.")
return return
if nick in var.SILENCED:
pm(cli, nick, "You have been silenced, and are unable to use any special powers.")
return
if not var.ALPHA_ENABLED: if not var.ALPHA_ENABLED:
pm(cli, nick, "You may only bite someone after another wolf has died during the day.") pm(cli, nick, "You may only bite someone after another wolf has died during the day.")
return return
@ -4648,7 +4573,7 @@ def bite_cmd(cli, nick, chan, rest):
vrole = None vrole = None
# also mark the victim as the kill target # also mark the victim as the kill target
if victim: if victim:
kill(cli, nick, chan, rest) kill.caller(cli, nick, chan, rest)
if var.ANGRY_WOLVES: if var.ANGRY_WOLVES:
if not victim: if not victim:
@ -4673,16 +4598,9 @@ def bite_cmd(cli, nick, chan, rest):
pm(cli, nick, "You have chosen to bite tonight. Whomever the wolves select to be killed tonight will be bitten instead.") pm(cli, nick, "You have chosen to bite tonight. Whomever the wolves select to be killed tonight will be bitten instead.")
debuglog("{0} ({1}) BITE: {2} ({3})".format(nick, var.get_role(nick), victim if victim else "wolves' target", vrole if vrole else "unknown")) debuglog("{0} ({1}) BITE: {2} ({3})".format(nick, var.get_role(nick), victim if victim else "wolves' target", vrole if vrole else "unknown"))
@cmd("pass", chan=False, pm=True, game=True, playing=True, roles=("hunter",)) @cmd("pass", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("hunter",))
def pass_cmd(cli, nick, chan, rest): def pass_cmd(cli, nick, chan, rest):
"""Decline to kill someone for that night.""" """Decline to kill someone for that night."""
if var.PHASE != "night":
pm(cli, nick, "You may only pass at night.")
return
if nick in var.SILENCED:
pm(cli, nick, "You have been silenced, and are unable to use any special powers.")
return
if nick in var.OTHER_KILLS.keys(): if nick in var.OTHER_KILLS.keys():
del var.OTHER_KILLS[nick] del var.OTHER_KILLS[nick]
var.HUNTERS.remove(nick) var.HUNTERS.remove(nick)
@ -4693,11 +4611,10 @@ def pass_cmd(cli, nick, chan, rest):
debuglog("{0} ({1}) PASS".format(nick, var.get_role(nick))) debuglog("{0} ({1}) PASS".format(nick, var.get_role(nick)))
chk_nightdone(cli) chk_nightdone(cli)
@cmd("choose", "match", chan=False, pm=True, game=True, playing=True, roles=("matchmaker",)) @cmd("choose", "match", chan=False, pm=True, playing=True, phases=("night",), roles=("matchmaker",))
def choose(cli, nick, chan, rest): def choose(cli, nick, chan, rest):
"""Select two players to fall in love. You may select yourself as one of the lovers.""" """Select two players to fall in love. You may select yourself as one of the lovers."""
if var.PHASE != "night" or not var.FIRST_NIGHT: if not var.FIRST_NIGHT:
pm(cli, nick, "You may only choose lovers during the first night.")
return return
if nick in var.MATCHMAKERS: if nick in var.MATCHMAKERS:
pm(cli, nick, "You have already chosen lovers.") pm(cli, nick, "You have already chosen lovers.")
@ -4759,18 +4676,12 @@ def choose(cli, nick, chan, rest):
debuglog("{0} ({1}) MATCH: {2} ({3}) + {4} ({5})".format(nick, var.get_role(nick), victim, var.get_role(victim), victim2, var.get_role(victim2))) debuglog("{0} ({1}) MATCH: {2} ({3}) + {4} ({5})".format(nick, var.get_role(nick), victim, var.get_role(victim), victim2, var.get_role(victim2)))
chk_nightdone(cli) chk_nightdone(cli)
@cmd("target", chan=False, pm=True, game=True, playing=True, roles=("assassin",)) @cmd("target", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("assassin",))
def target(cli, nick, chan, rest): def target(cli, nick, chan, rest):
"""Pick a player as your target, killing them if you die.""" """Pick a player as your target, killing them if you die."""
if var.PHASE != "night":
pm(cli, nick, "You may only target people at night.")
return
if nick in var.TARGETED and var.TARGETED[nick] != None: if nick in var.TARGETED and var.TARGETED[nick] != None:
pm(cli, nick, "You have already chosen a target.") pm(cli, nick, "You have already chosen a target.")
return return
if nick in var.SILENCED:
pm(cli, nick, "You have been silenced, and are unable to use any special powers.")
return
victim = get_victim(cli, nick, re.split(" +",rest)[0], False) victim = get_victim(cli, nick, re.split(" +",rest)[0], False)
if not victim: if not victim:
return return
@ -4787,18 +4698,12 @@ def target(cli, nick, chan, rest):
debuglog("{0} ({1}-{2}) TARGET: {3} ({4})".format(nick, "-".join(var.get_templates(nick)), var.get_role(nick), victim, var.get_role(victim))) debuglog("{0} ({1}-{2}) TARGET: {3} ({4})".format(nick, "-".join(var.get_templates(nick)), var.get_role(nick), victim, var.get_role(victim)))
chk_nightdone(cli) chk_nightdone(cli)
@cmd("hex", chan=False, pm=True, game=True, playing=True, roles=("hag",)) @cmd("hex", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("hag",))
def hex_target(cli, nick, chan, rest): def hex_target(cli, nick, chan, rest):
"""Hex someone, preventing them from acting the next day and night.""" """Hex someone, preventing them from acting the next day and night."""
if var.PHASE != "night":
pm(cli, nick, "You may only hex at night.")
return
if nick in var.HEXED: if nick in var.HEXED:
pm(cli, nick, "You have already hexed someone tonight.") pm(cli, nick, "You have already hexed someone tonight.")
return return
if nick in var.SILENCED:
pm(cli, nick, "You have been silenced, and are unable to use any special powers.")
return
victim = get_victim(cli, nick, re.split(" +",rest)[0], False) victim = get_victim(cli, nick, re.split(" +",rest)[0], False)
if not victim: if not victim:
return return
@ -4827,19 +4732,13 @@ def hex_target(cli, nick, chan, rest):
debuglog("{0} ({1}) HEX: {2} ({3})".format(nick, var.get_role(nick), victim, var.get_role(victim))) debuglog("{0} ({1}) HEX: {2} ({3})".format(nick, var.get_role(nick), victim, var.get_role(victim)))
chk_nightdone(cli) chk_nightdone(cli)
@cmd("curse", chan=False, pm=True, game=True, playing=True, roles=("warlock",)) @cmd("curse", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("warlock",))
def curse(cli, nick, chan, rest): def curse(cli, nick, chan, rest):
if var.PHASE != "night":
pm(cli, nick, "You may only curse at night.")
return
if nick in var.CURSED: if nick in var.CURSED:
# CONSIDER: this happens even if they choose to not curse, should maybe let them # CONSIDER: this happens even if they choose to not curse, should maybe let them
# pick again in that case instead of locking them into doing nothing. # pick again in that case instead of locking them into doing nothing.
pm(cli, nick, "You have already cursed someone tonight.") pm(cli, nick, "You have already cursed someone tonight.")
return return
if nick in var.SILENCED:
pm(cli, nick, "You have been silenced, and are unable to use any special powers.")
return
victim = get_victim(cli, nick, re.split(" +",rest)[0], False) victim = get_victim(cli, nick, re.split(" +",rest)[0], False)
if not victim: if not victim:
return return
@ -4874,11 +4773,10 @@ def curse(cli, nick, chan, rest):
debuglog("{0} ({1}) CURSE: {2} ({3})".format(nick, var.get_role(nick), victim, var.get_role(victim))) debuglog("{0} ({1}) CURSE: {2} ({3})".format(nick, var.get_role(nick), victim, var.get_role(victim)))
chk_nightdone(cli) chk_nightdone(cli)
@cmd("clone", chan=False, pm=True, game=True, playing=True, roles=("clone",)) @cmd("clone", chan=False, pm=True, playing=True, phases=("night",), roles=("clone",))
def clone(cli, nick, chan, rest): def clone(cli, nick, chan, rest):
"""Clone another player. You will turn into their role if they die.""" """Clone another player. You will turn into their role if they die."""
if var.PHASE != "night" or not var.FIRST_NIGHT: if not var.FIRST_NIGHT:
pm(cli, nick, "You may only clone someone during the first night.")
return return
if nick in var.CLONED.keys(): if nick in var.CLONED.keys():
pm(cli, nick, "You have already chosen to clone someone.") pm(cli, nick, "You have already chosen to clone someone.")
@ -4901,18 +4799,12 @@ def clone(cli, nick, chan, rest):
debuglog("{0} ({1}) CLONE: {2} ({3})".format(nick, var.get_role(nick), victim, var.get_role(victim))) debuglog("{0} ({1}) CLONE: {2} ({3})".format(nick, var.get_role(nick), victim, var.get_role(victim)))
chk_nightdone(cli) chk_nightdone(cli)
@cmd("charm", chan=False, pm=True, game=True, playing=True, roles=("piper",)) @cmd("charm", chan=False, pm=True, playing=True, silenced=True, phases=("night",), roles=("piper",))
def charm(cli, nick, chan, rest): def charm(cli, nick, chan, rest):
"""Charm a player, slowly leading to your win!""" """Charm a player, slowly leading to your win!"""
if var.PHASE != "night":
pm(cli, nick, "You may only charm other players during the night.")
return
if nick in var.CHARMERS: if nick in var.CHARMERS:
pm(cli, nick, "You have already charmed players for tonight.") pm(cli, nick, "You have already charmed players for tonight.")
return return
if nick in var.SILENCED:
pm(cli, nick, "You are silenced, and are unable to use any special powers.")
return
pieces = re.split(" +",rest) pieces = re.split(" +",rest)
victim = pieces[0] victim = pieces[0]
@ -5789,8 +5681,8 @@ def cgamemode(cli, arg):
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", join=True) @cmd("start", phases=("join",))
def fstart(cli, nick, chan, rest): def start_cmd(cli, nick, chan, rest):
"""Starts a game of Werewolf.""" """Starts a game of Werewolf."""
start(cli, nick, chan) start(cli, nick, chan)
@ -5891,11 +5783,11 @@ def start(cli, nick, chan, forced = False, restart = ""):
if var.ADMIN_TO_PING and not restart: if var.ADMIN_TO_PING and not restart:
if "join" in COMMANDS.keys(): if "join" in COMMANDS.keys():
COMMANDS["join"] = [lambda *spam: cli.msg(chan, "This command has been disabled by an admin.")] COMMANDS["join"].caller = [lambda *spam: cli.msg(chan, "This command has been disabled by an admin.")]
if "j" in COMMANDS.keys(): if "j" in COMMANDS.keys():
COMMANDS["j"] = [lambda *spam: cli.msg(chan, "This command has been disabled by an admin.")] COMMANDS["j"].caller = [lambda *spam: cli.msg(chan, "This command has been disabled by an admin.")]
if "start" in COMMANDS.keys(): if "start" in COMMANDS.keys():
COMMANDS["start"] = [lambda *spam: cli.msg(chan, "This command has been disabled by an admin.")] COMMANDS["start"].caller = [lambda *spam: cli.msg(chan, "This command has been disabled by an admin.")]
if not restart: # will already be stored if restarting if not restart: # will already be stored if restarting
var.ALL_PLAYERS = copy.copy(var.ROLES["person"]) var.ALL_PLAYERS = copy.copy(var.ROLES["person"])
@ -6115,7 +6007,7 @@ def start(cli, nick, chan, forced = False, restart = ""):
@hook("error") @hook("error")
def on_error(cli, pfx, msg): def on_error(cli, pfx, msg):
if msg.endswith("(Excess Flood)"): if msg.endswith("(Excess Flood)"):
restart_program(cli, "excess flood", "") restart_program.func(cli, "excess flood", "")
elif msg.startswith("Closing Link:"): elif msg.startswith("Closing Link:"):
raise SystemExit raise SystemExit
@ -6455,7 +6347,7 @@ def fdeny(cli, nick, chan, rest):
"""Deny someone from using a command.""" """Deny someone from using a command."""
allow_deny(cli, nick, chan, rest, "deny") allow_deny(cli, nick, chan, rest, "deny")
@cmd("wait", "w", join=True, playing=True) @cmd("wait", "w", playing=True, phases=("join",))
def wait(cli, nick, chan, rest): def wait(cli, nick, chan, rest):
"""Increases the wait time until !start can be used.""" """Increases the wait time until !start can be used."""
pl = var.list_players() pl = var.list_players()
@ -6488,7 +6380,7 @@ def wait(cli, nick, chan, rest):
"{1} seconds.").format(nick, var.EXTRA_WAIT)) "{1} seconds.").format(nick, var.EXTRA_WAIT))
@cmd("fwait", admin_only=True, join=True) @cmd("fwait", admin_only=True, phases=("join",))
def fwait(cli, nick, chan, rest): def fwait(cli, nick, chan, rest):
"""Forces an increase (or decrease) in wait time. Can be used with a number of seconds to wait.""" """Forces an increase (or decrease) in wait time. Can be used with a number of seconds to wait."""
@ -6515,7 +6407,7 @@ def fwait(cli, nick, chan, rest):
"s" if extra != 1 else "")) "s" if extra != 1 else ""))
@cmd("fstop", admin_only=True, game=True, join=True) @cmd("fstop", admin_only=True, phases=("join", "day", "night"))
def reset_game(cli, nick, chan, rest): def reset_game(cli, nick, chan, rest):
"""Forces the game to stop.""" """Forces the game to stop."""
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))
@ -6709,7 +6601,7 @@ def show_admins(cli, nick, chan, rest):
else: else:
cli.msg(chan, msg) cli.msg(chan, msg)
decorators.unhook(HOOKS, 4) hook.unhook(4)
var.ADMIN_PINGING = False var.ADMIN_PINGING = False
if nick == chan: if nick == chan:
@ -6748,7 +6640,7 @@ def pony(cli, nick, chan, rest):
cmsg = "The pony lands on \2{0}\2.".format(pony) cmsg = "The pony lands on \2{0}\2.".format(pony)
cli.msg(chan, cmsg) cli.msg(chan, cmsg)
@cmd("time", pm=True, game=True, join=True) @cmd("time", pm=True, phases=("join", "day", "night"))
def timeleft(cli, nick, chan, rest): def timeleft(cli, nick, chan, rest):
"""Returns the time left until the next day/night transition.""" """Returns the time left until the next day/night transition."""
@ -6872,7 +6764,7 @@ def listroles(cli, nick, chan, rest):
else: else:
cli.msg(chan, txt) cli.msg(chan, txt)
@cmd("myrole", pm=True, game=True) @cmd("myrole", pm=True, phases=("day", "night"))
def myrole(cli, nick, chan, rest): def myrole(cli, nick, chan, rest):
"""Reminds you of your current role.""" """Reminds you of your current role."""
@ -6961,7 +6853,7 @@ def aftergame(cli, rawnick, chan, rest):
def do_action(): def do_action():
for fn in COMMANDS[cmd]: for fn in COMMANDS[cmd]:
fn.aftergame = True fn.aftergame = True
fn(cli, rawnick, botconfig.CHANNEL if fn.chan else nick, " ".join(rst)) fn.caller(cli, rawnick, botconfig.CHANNEL if fn.chan else nick, " ".join(rst))
fn.aftergame = False fn.aftergame = False
else: else:
cli.notice(nick, "That command was not found.") cli.notice(nick, "That command was not found.")
@ -7012,7 +6904,7 @@ def flastgame(cli, rawnick, chan, rest):
var.ADMIN_TO_PING = nick var.ADMIN_TO_PING = nick
if rest.strip(): if rest.strip():
aftergame(cli, rawnick, botconfig.CHANNEL, rest) aftergame.func(cli, rawnick, botconfig.CHANNEL, rest)
@cmd("gamestats", "gstats", pm=True) @cmd("gamestats", "gstats", pm=True)
def game_stats(cli, nick, chan, rest): def game_stats(cli, nick, chan, rest):
@ -7121,9 +7013,9 @@ def player_stats(cli, nick, chan, rest):
def my_stats(cli, nick, chan, rest): def my_stats(cli, nick, chan, rest):
"""Get your own stats.""" """Get your own stats."""
rest = rest.split() rest = rest.split()
player_stats(cli, nick, chan, " ".join([nick] + rest)) player_stats.func(cli, nick, chan, " ".join([nick] + rest))
@cmd("game", join=True, playing=True) @cmd("game", playing=True, phases=("join",))
def game(cli, nick, chan, rest): def game(cli, nick, chan, rest):
"""Vote for a game mode to be picked.""" """Vote for a game mode to be picked."""
if rest: if rest:
@ -7158,16 +7050,16 @@ def game_help(args=""):
game.__doc__ = game_help game.__doc__ = game_help
@cmd("vote", "v", pm=True) @cmd("vote", "v", pm=True, phases=("join", "day"))
def vote(cli, nick, chan, rest): def vote(cli, nick, chan, rest):
"""Vote for a game mode if no game is running, or for a player to be lynched.""" """Vote for a game mode if no game is running, or for a player to be lynched."""
if rest: if rest:
if var.PHASE == "join" and chan != nick: if var.PHASE == "join" and chan != nick:
return game(cli, nick, chan, rest) return game.caller(cli, nick, chan, rest)
else: else:
return lynch(cli, nick, chan, rest) return lynch.caller(cli, nick, chan, rest)
else: else:
return show_votes(cli, nick, chan, rest) return show_votes.caller(cli, nick, chan, rest)
@cmd("fpull", admin_only=True, pm=True) @cmd("fpull", admin_only=True, pm=True)
def fpull(cli, nick, chan, rest): def fpull(cli, nick, chan, rest):
@ -7271,7 +7163,7 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS:
except Exception as e: 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, pm=True, game=True) @cmd("revealroles", admin_only=True, pm=True, phases=("day", "night"))
def revealroles(cli, nick, chan, rest): def revealroles(cli, nick, chan, rest):
"""Reveal role information.""" """Reveal role information."""
def is_authorized(): def is_authorized():
@ -7300,7 +7192,6 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS:
pm(cli, nick, "You are not allowed to use that command right now.") pm(cli, nick, "You are not allowed to use that command right now.")
else: else:
cli.notice(nick, "You are not allowed to use that command right now.") cli.notice(nick, "You are not allowed to use that command right now.")
return return
output = [] output = []
@ -7377,7 +7268,7 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS:
cli.notice(nick, var.break_long_message(output, " | ")) cli.notice(nick, var.break_long_message(output, " | "))
@cmd("fgame", admin_only=True, raw_nick=True, join=True) @cmd("fgame", admin_only=True, raw_nick=True, phases=("join",))
def fgame(cli, nick, chan, rest): def fgame(cli, nick, chan, rest):
"""Force a certain game mode to be picked. Disable voting for game modes upon use.""" """Force a certain game mode to be picked. Disable voting for game modes upon use."""
nick = parse_nick(nick)[0] nick = parse_nick(nick)[0]
@ -7457,9 +7348,9 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS:
continue continue
for user in who: for user in who:
if fn.chan: if fn.chan:
fn(cli, user, chan, " ".join(rst)) fn.caller(cli, user, chan, " ".join(rst))
else: else:
fn(cli, user, user, " ".join(rst)) fn.caller(cli, user, user, " ".join(rst))
cli.msg(chan, "Operation successful.") cli.msg(chan, "Operation successful.")
else: else:
cli.msg(chan, "That command was not found.") cli.msg(chan, "That command was not found.")
@ -7497,9 +7388,9 @@ if botconfig.DEBUG_MODE or botconfig.ALLOWED_NORMAL_MODE_COMMANDS:
continue continue
for user in tgt[:]: for user in tgt[:]:
if fn.chan: if fn.chan:
fn(cli, user, chan, " ".join(rst)) fn.caller(cli, user, chan, " ".join(rst))
else: else:
fn(cli, user, user, " ".join(rst)) fn.caller(cli, user, user, " ".join(rst))
cli.msg(chan, "Operation successful.") cli.msg(chan, "Operation successful.")
else: else:
cli.msg(chan, "That command was not found.") cli.msg(chan, "That command was not found.")