Huge overhaul to the decorators + small changes.

- Merge channel and PM commands in a single decorator.
- Add many parameters for the decorators, reducing commands cluster.
- Added back the admin_only check for some commands; special-cased
'<console>'.
- Moved the checking for which roles are seen wolf in the settings; same
for seen as default.
- Added a variable to pick if one is seen as the default role or as a
villager where applicable (default: True).
- Fixed seer/oracle seeing through amnesiac.
- Some fixes to the totem chances that I forgot to add in the previous
commit.
- Removed some useless module imports.
- Moved some functions to settings.
- Changed all single-quotes to double-quotes in commands for
consistency.
- Renamed 'cmd' variable in both force and rforce commands to avoid
confusion.
- Probably a bunch of other things. Don't consider this commit
description to be 100% complete.
This commit is contained in:
Vgr E.Barry 2014-12-31 09:33:06 -05:00
parent 53cf4970ca
commit df26d8d25e
4 changed files with 443 additions and 812 deletions

View File

@ -7,6 +7,7 @@ import tools.moduleloader as ld
import traceback import traceback
from settings import common as var from settings import common as var
from base64 import b64encode from base64 import b64encode
from oyoyo.parse import parse_nick
import imp import imp
def on_privmsg(cli, rawnick, chan, msg, notice = False): def on_privmsg(cli, rawnick, chan, msg, notice = False):
@ -19,50 +20,39 @@ def on_privmsg(cli, rawnick, chan, msg, notice = False):
(chan == botconfig.NICK and not botconfig.ALLOW_PRIVATE_NOTICE_COMMANDS))): (chan == botconfig.NICK and not botconfig.ALLOW_PRIVATE_NOTICE_COMMANDS))):
return # not allowed in settings return # not allowed in settings
if chan != botconfig.NICK: #not a PM if chan == botconfig.NICK:
if currmod and "" in currmod.COMMANDS.keys(): chan = parse_nick(rawnick)[0]
for fn in currmod.COMMANDS[""]:
if currmod and "" in currmod.COMMANDS.keys():
for fn in currmod.COMMANDS[""]:
try:
fn(cli, rawnick, chan, msg)
except:
if botconfig.DEBUG_MODE:
raise
else:
logging.error(traceback.format_exc())
cli.msg(chan, "An error has occurred and has been logged.")
for x in set(list(COMMANDS.keys()) + (list(currmod.COMMANDS.keys()) if currmod else list())):
if chan != parse_nick(rawnick)[0] and not msg.lower().startswith(botconfig.CMD_CHAR):
break # channel message but no prefix; ignore
if msg.lower().startswith(botconfig.CMD_CHAR+x):
h = msg[len(x)+len(botconfig.CMD_CHAR):]
elif not x or msg.lower().startswith(x):
h = msg[len(x):]
else:
continue
if not h or h[0] == " ":
for fn in COMMANDS.get(x, []) + (currmod.COMMANDS.get(x, []) if currmod else []):
try: try:
fn(cli, rawnick, chan, msg) fn(cli, rawnick, chan, h.lstrip())
except Exception as e: except:
if botconfig.DEBUG_MODE: if botconfig.DEBUG_MODE:
raise e raise
else: else:
logging.error(traceback.format_exc()) 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)+len(botconfig.CMD_CHAR):]
if not h or h[0] == " " or not x:
for fn in COMMANDS.get(x,[])+(currmod.COMMANDS.get(x,[]) if currmod else []):
try:
fn(cli, rawnick, chan, h.lstrip())
except Exception as e:
if botconfig.DEBUG_MODE:
raise e
else:
logging.error(traceback.format_exc())
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)+len(botconfig.CMD_CHAR):]
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 []):
try:
fn(cli, rawnick, h.lstrip())
except Exception as e:
if botconfig.DEBUG_MODE:
raise e
else:
logging.error(traceback.format_exc())
cli.msg(chan, "An error has occurred and has been logged.")
def __unhandled__(cli, prefix, cmd, *args): def __unhandled__(cli, prefix, cmd, *args):
currmod = ld.MODULES[ld.CURRENT_MODULE] currmod = ld.MODULES[ld.CURRENT_MODULE]
@ -87,11 +77,9 @@ def __unhandled__(cli, prefix, cmd, *args):
COMMANDS = {} COMMANDS = {}
PM_COMMANDS = {}
HOOKS = {} HOOKS = {}
cmd = decorators.generate(COMMANDS) cmd = decorators.generate(COMMANDS)
pmcmd = decorators.generate(PM_COMMANDS)
hook = decorators.generate(HOOKS, raw_nick=True, permissions=False) hook = decorators.generate(HOOKS, raw_nick=True, permissions=False)
def connect_callback(cli): def connect_callback(cli):

File diff suppressed because it is too large Load Diff

View File

@ -66,6 +66,7 @@ START_WITH_DAY = False
WOLF_STEALS_GUN = True # at night, the wolf can steal steal the victim's bullets WOLF_STEALS_GUN = True # at night, the wolf can steal steal the victim's bullets
ROLE_REVEAL = True ROLE_REVEAL = True
LOVER_WINS_WITH_FOOL = False # if fool is lynched, does their lover win with them? LOVER_WINS_WITH_FOOL = False # if fool is lynched, does their lover win with them?
DEFAULT_SEEN_AS_VILL = True # non-wolves are seen as villager regardless of the default role
# Minimum number of players needed for mad scientist to skip over dead people when determining who is next to them # Minimum number of players needed for mad scientist to skip over dead people when determining who is next to them
# Set to 0 to always skip over dead players. Note this is number of players that !joined, NOT number of players currently alive # Set to 0 to always skip over dead players. Note this is number of players that !joined, NOT number of players currently alive
@ -180,13 +181,17 @@ ROLE_GUIDE = {# village roles
# If every wolf role dies, and there are no remaining traitors, the game ends and villagers win (monster may steal win) # If every wolf role dies, and there are no remaining traitors, the game ends and villagers win (monster may steal win)
WOLF_ROLES = ["wolf", "alpha wolf", "werecrow", "wolf cub"] WOLF_ROLES = ["wolf", "alpha wolf", "werecrow", "wolf cub"]
# Access to wolfchat, and counted towards the # of wolves vs villagers when determining if a side has won # Access to wolfchat, and counted towards the # of wolves vs villagers when determining if a side has won
WOLFCHAT_ROLES = ["wolf", "alpha wolf", "werecrow", "wolf cub", "traitor", "hag", "sorcerer"] WOLFCHAT_ROLES = WOLF_ROLES + ["traitor", "hag", "sorcerer"]
# Wins with the wolves, even if the roles are not necessarily wolves themselves # Wins with the wolves, even if the roles are not necessarily wolves themselves
WOLFTEAM_ROLES = ["wolf", "alpha wolf", "werecrow", "wolf cub", "traitor", "hag", "sorcerer", "minion", "cultist"] WOLFTEAM_ROLES = WOLFCHAT_ROLES + ["minion", "cultist"]
# These roles never win as a team, only ever individually (either instead of or in addition to the regular winners) # These roles never win as a team, only ever individually (either instead of or in addition to the regular winners)
TRUE_NEUTRAL_ROLES = ["crazed shaman", "fool", "jester", "monster", "clone"] TRUE_NEUTRAL_ROLES = ["crazed shaman", "fool", "jester", "monster", "clone"]
# These are the roles that will NOT be used for when amnesiac turns, everything else is fair game! (var.DEFAULT_ROLE is also appended if not in this list) # These are the roles that will NOT be used for when amnesiac turns, everything else is fair game! (var.DEFAULT_ROLE is also appended if not in this list)
AMNESIAC_BLACKLIST = ["monster", "amnesiac", "minion", "matchmaker", "clone", "doctor", "villager", "cultist"] AMNESIAC_BLACKLIST = ["monster", "amnesiac", "minion", "matchmaker", "clone", "doctor", "villager", "cultist"]
# These roles are seen as wolf by the seer/oracle
SEEN_WOLF = WOLF_ROLES + ["monster", "mad scientist"]
# These are seen as the default role (or villager) when seen by seer
SEEN_DEFAULT = ["traitor", "hag", "sorcerer", "village elder", "time lord", "villager", "cultist", "minion", "vengeful ghost", "lycan", "clone", "fool", "jester"]
# The roles in here are considered templates and will be applied on TOP of other roles. The restrictions are a list of roles that they CANNOT be applied to # The roles in here are considered templates and will be applied on TOP of other roles. The restrictions are a list of roles that they CANNOT be applied to
# NB: if you want a template to apply to everyone, list it here but make the restrictions an empty list. Templates not listed here are considered full roles instead # NB: if you want a template to apply to everyone, list it here but make the restrictions an empty list. Templates not listed here are considered full roles instead
@ -223,7 +228,7 @@ QUIT_MESSAGES_NO_REVEAL = ("\u0002{0}\u0002 suddenly falls over dead before the
"\u0002{0}\u0002 fell off the roof of their house and is now dead.", "\u0002{0}\u0002 fell off the roof of their house and is now dead.",
"\u0002{0}\u0002 is crushed to death by a falling tree. The villagers desperately try to save them, but it is too late.") "\u0002{0}\u0002 is crushed to death by a falling tree. The villagers desperately try to save them, but it is too late.")
import botconfig, fnmatch import botconfig, fnmatch, sys, time, re
RULES = (botconfig.CHANNEL + " channel rules: http://wolf.xnrand.com/rules") RULES = (botconfig.CHANNEL + " channel rules: http://wolf.xnrand.com/rules")
DENY = {} DENY = {}
@ -240,6 +245,32 @@ PING_IN_ACCS = [] # accounts of people who have opted in for ping
is_role = lambda plyr, rol: rol in ROLES and plyr in ROLES[rol] is_role = lambda plyr, rol: rol in ROLES and plyr in ROLES[rol]
def pm(cli, target, message): # message either privmsg or notice, depending on user settings
if is_fake_nick(target) and botconfig.DEBUG_MODE:
print("[{0}] Would send message to fake nick {1}: {2}".format(
time.strftime("%d/%b/%Y %H:%M:%S"),
target,
message), file=sys.stderr)
return
if is_user_notice(target):
cli.notice(target, message)
return
cli.msg(target, message)
def is_user_notice(nick):
if nick in USERS and USERS[nick]["account"] and USERS[nick]["account"] != "*":
if USERS[nick]["account"] in PREFER_NOTICE_ACCS:
return True
if nick in USERS and USERS[nick]["cloak"] in PREFER_NOTICE and not ACCOUNTS_ONLY:
return True
return False
def is_fake_nick(who):
return re.match("[0-9]+", who)
def is_admin(nick): def is_admin(nick):
if nick not in USERS.keys(): if nick not in USERS.keys():
return False return False

View File

@ -15,7 +15,8 @@ import settings.wolfgame as var
def generate(fdict, permissions=True, **kwargs): def generate(fdict, permissions=True, **kwargs):
"""Generates a decorator generator. Always use this""" """Generates a decorator generator. Always use this"""
def cmd(*s, raw_nick=False, admin_only=False, owner_only=False, hookid=-1): 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):
def dec(f): def dec(f):
def innerf(*args): def innerf(*args):
largs = list(args) largs = list(args)
@ -29,13 +30,15 @@ def generate(fdict, permissions=True, **kwargs):
else: else:
nick = "" nick = ""
cloak = "" cloak = ""
if len(largs) > 3 and largs[2] and largs[2][0] == "#":
chan = largs[2]
else:
chan = ""
if not raw_nick and len(largs) > 1 and largs[1]: if not raw_nick and len(largs) > 1 and largs[1]:
largs[1] = nick largs[1] = nick
if chan and not chan == botconfig.CHANNEL and not admin_only and not owner_only: if nick == "<console>":
return f(*largs) # special case; no questions
if not pm and largs[2] == nick: # PM command
return
if not chan and largs[2] != nick: # channel command
return
if largs[2].startswith("#") and largs[2] != botconfig.CHANNEL and not admin_only and not owner_only:
if "" in s: if "" in s:
return # Don't have empty commands triggering in other channels return # Don't have empty commands triggering in other channels
allowed = False allowed = False
@ -51,6 +54,33 @@ def generate(fdict, permissions=True, **kwargs):
acc = None acc = None
if "" in s: if "" in s:
return f(*largs) return f(*largs)
if game and var.PHASE not in ("day", "night") + (("join",) if join else ()):
largs[0].notice(nick, "No game is currently running.")
return
if ((join and none and var.PHASE not in ("join", "none"))
or (none and not join and var.PHASE != "none")):
largs[0].notice(nick, "Sorry, but the game is already running. Try again next time.")
return
if join and not none:
if var.PHASE == "none":
largs[0].notice(nick, "No game is currently running.")
return
if var.PHASE != "join" and not game:
largs[0].notice(nick, "Werewolf is already in play.")
return
if playing and nick not in var.list_players() or nick in var.DISCONNECTED.keys():
cli.notice(nick, "You're not currently playing.")
return
if roles:
for role in roles:
if nick in var.ROLES[role]:
break
else:
if len(roles) == 1:
var.pm(largs[0], nick, "Only a{0} {1} may use this command.".format("n" if roles[0][0] in "aeiou" else "", roles[0]))
else:
var.pm(largs[0], nick, "Only a{0} {1} or {2} may use this command.".format("n" if roles[0][0] in "aeiou" else "", ", ".join(roles[:-1]), roles[-1]))
return
if acc: if acc:
for pattern in var.DENY_ACCOUNTS.keys(): for pattern in var.DENY_ACCOUNTS.keys():
if fnmatch.fnmatch(acc.lower(), pattern.lower()): if fnmatch.fnmatch(acc.lower(), pattern.lower()):
@ -105,6 +135,13 @@ def generate(fdict, permissions=True, **kwargs):
innerf.owner_only = owner_only innerf.owner_only = owner_only
innerf.raw_nick = raw_nick innerf.raw_nick = raw_nick
innerf.admin_only = admin_only 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.hookid = hookid
innerf.__doc__ = f.__doc__ innerf.__doc__ = f.__doc__
return innerf return innerf