import fnmatch from collections import defaultdict import botconfig from oyoyo.parse import parse_nick from src import settings as var from src import logger adminlog = logger(None) COMMANDS = defaultdict(list) HOOKS = defaultdict(list) class cmd: def __init__(self, *cmds, raw_nick=False, admin_only=False, owner_only=False, 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) cli, rawnick, chan, rest = largs nick, mode, user, cloak = parse_nick(rawnick) if cloak is None: cloak = "" if not self.raw_nick: largs[1] = nick if not self.pm and chan == nick: return # PM command, not allowed if not self.chan and chan != nick: return # channel command, not allowed if chan.startswith("#") and chan != botconfig.CHANNEL and not (admin_only or owner_only): if "" in self.cmds: return # don't have empty commands triggering in other channels for command in self.cmds: if command in botconfig.ALLOWED_ALT_CHANNELS_COMMANDS: break else: return if nick in var.USERS and var.USERS[nick]["account"] != "*": acc = var.USERS[nick]["account"] else: acc = None if "" in self.cmds: return self.func(*largs) if self.phases and var.PHASE not in self.phases: return if self.playing and (nick not in var.list_players() or nick in var.DISCONNECTED): if chan == nick: pm(cli, nick, "You're not currently playing.") else: cli.notice(nick, "You're not currently playing.") return if self.silenced and nick in var.SILENCED: 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 if self.roles: for role in self.roles: if nick in var.ROLES[role]: break else: return return self.func(*largs) # don't check restrictions for role commands if self.owner_only: if var.is_owner(nick, cloak): adminlog(chan, rawnick, self.name, rest) return self.func(*largs) if chan == nick: pm(cli, nick, "You are not the owner.") else: cli.notice(nick, "You are not the owner.") return if not self.admin_only: return self.func(*largs) if var.is_admin(nick, cloak): adminlog(chan, rawnick, self.name, rest) 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: cli.notice(nick, "You do not have permission to use that command.") return for pattern in var.ALLOW_ACCOUNTS: if fnmatch.fnmatch(acc.lower(), pattern.lower()): for command in self.cmds: if command in var.ALLOW_ACCOUNTS[pattern]: 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: cli.notice(nick, "You do not have permission to use that command.") return 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) 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 HOOKS[name].append(self) def __call__(self, func): self.func = func self.__doc__ = self.func.__doc__ return self def caller(self, *args): return self.func(*args) @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]