def on_privmsg(cli, rawnick, chan, msg, *, notice=False): if notice and "!" not in rawnick or not rawnick: # server notice; we don't care about those return if not users.equals( chan, users.Bot.nick ) and botconfig.IGNORE_HIDDEN_COMMANDS and not chan.startswith( tuple(hooks.Features["CHANTYPES"])): return if (notice and ((not users.equals(chan, users.Bot.nick) and not botconfig.ALLOW_NOTICE_COMMANDS) or (users.equals(chan, users.Bot.nick) and not botconfig.ALLOW_PRIVATE_NOTICE_COMMANDS))): return # not allowed in settings for fn in decorators.COMMANDS[""]: fn.caller(cli, rawnick, chan, msg) phase = var.PHASE for x in list(decorators.COMMANDS.keys()): if not users.equals(chan, users.Bot.nick) 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 decorators.COMMANDS.get(x, []): if phase == var.PHASE: fn.caller(cli, rawnick, chan, h.lstrip())
def on_privmsg(cli, rawnick, chan, msg, *, notice=False): if notice and "!" not in rawnick or not rawnick: # server notice; we don't care about those return _ignore_locals_ = False if var.USER_DATA_LEVEL == 0 or var.CHANNEL_DATA_LEVEL == 0: _ignore_locals_ = True # don't expose in tb if we're trying to anonymize stuff user = users.get(rawnick, allow_none=True) ch = chan.lstrip("".join(Features["PREFIX"])) if users.equals(chan, users.Bot.nick): # PM target = users.Bot else: target = channels.get(ch, allow_none=True) if user is None or target is None: return wrapper = MessageDispatcher(user, target) if wrapper.public and botconfig.IGNORE_HIDDEN_COMMANDS and not chan.startswith( tuple(Features["CHANTYPES"])): return if (notice and ((wrapper.public and not botconfig.ALLOW_NOTICE_COMMANDS) or (wrapper.private and not botconfig.ALLOW_PRIVATE_NOTICE_COMMANDS))): return # not allowed in settings for fn in decorators.COMMANDS[""]: fn.caller(var, wrapper, msg) parts = msg.split(sep=" ", maxsplit=1) key = parts[0].lower() if len(parts) > 1: message = parts[1].strip() else: message = "" if wrapper.public and not key.startswith(botconfig.CMD_CHAR): return # channel message but no prefix; ignore parse_and_dispatch(var, wrapper, key, message)
def on_privmsg(cli, rawnick, chan, msg, *, notice=False, force_role=None): if notice and "!" not in rawnick or not rawnick: # server notice; we don't care about those return user = users._get(rawnick, allow_none=True) # FIXME if users.equals(chan, users.Bot.nick): # PM target = users.Bot else: target = channels.get(chan, allow_none=True) if user is None or target is None: return wrapper = MessageDispatcher(user, target) if wrapper.public and botconfig.IGNORE_HIDDEN_COMMANDS and not chan.startswith(tuple(hooks.Features["CHANTYPES"])): return if (notice and ((wrapper.public and not botconfig.ALLOW_NOTICE_COMMANDS) or (wrapper.private and not botconfig.ALLOW_PRIVATE_NOTICE_COMMANDS))): return # not allowed in settings if force_role is None: # if force_role isn't None, that indicates recursion; don't fire these off twice for fn in decorators.COMMANDS[""]: fn.caller(cli, rawnick, chan, msg) parts = msg.split(sep=" ", maxsplit=1) key = parts[0].lower() if len(parts) > 1: message = parts[1].lstrip() else: message = "" if wrapper.public and not key.startswith(botconfig.CMD_CHAR): return # channel message but no prefix; ignore if key.startswith(botconfig.CMD_CHAR): key = key[len(botconfig.CMD_CHAR):] if not key: # empty key ("") already handled above return # Don't change this into decorators.COMMANDS[key] even though it's a defaultdict, # as we don't want to insert bogus command keys into the dict. cmds = [] phase = var.PHASE if user in get_participants(): roles = get_all_roles(user) # A user can be a participant but not have a role, for example, dead vengeful ghost has_roles = len(roles) != 0 if force_role is not None: roles &= {force_role} # only fire off role commands for the forced role common_roles = set(roles) # roles shared by every eligible role command have_role_cmd = False for fn in decorators.COMMANDS.get(key, []): if not fn.roles: cmds.append(fn) continue if roles.intersection(fn.roles): have_role_cmd = True cmds.append(fn) common_roles.intersection_update(fn.roles) if force_role is not None and not have_role_cmd: # Trying to force a non-role command with a role. # We allow non-role commands to execute if a role is forced if a role # command is also executed, as this would allow (for example) a bot admin # to add extra effects to all "kill" commands without needing to continually # update the list of roles which can use "kill". However, we don't want to # allow things like "wolf pstats" because that just doesn't make sense. return if has_roles and not common_roles: # getting here means that at least one of the role_cmds is disjoint # from the others. For example, augur see vs seer see when a bare see # is executed. In this event, display a helpful error message instructing # the user to resolve the ambiguity. common_roles = set(roles) info = [0,0] for fn in cmds: fn_roles = roles.intersection(fn.roles) if not fn_roles: continue for role1 in common_roles: info[0] = role1 break for role2 in fn_roles: info[1] = role2 break common_roles &= fn_roles if not common_roles: break wrapper.pm(messages["ambiguous_command"].format(key, info[0], info[1])) return elif force_role is None: cmds = decorators.COMMANDS.get(key, []) for fn in cmds: if phase == var.PHASE: # FIXME: pass in var, wrapper, message instead of cli, rawnick, chan, message fn.caller(cli, rawnick, chan, message)
def caller(self, cli, rawnick, chan, rest): _ignore_locals_ = True if users.equals(chan, users.Bot.nick): chan = users.parse_rawnick_as_dict(rawnick)["nick"] largs = [cli, rawnick, chan, rest] cli, rawnick, chan, rest = largs nick, mode, ident, host = parse_nick(rawnick) if ident is None: ident = "" if host is None: host = "" 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 (self.flag or self.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 not in var.USERS and not is_fake_nick(nick): return if nick in var.USERS and var.USERS[nick]["account"] != "*": acc = irc_lower(var.USERS[nick]["account"]) else: acc = None ident = irc_lower(ident) host = host.lower() hostmask = nick + "!" + ident + "@" + host if "" in self.cmds: self.func(*largs) return if self.phases and var.PHASE not in self.phases: return if self.playing and (nick not in list_players() or users._get(nick) in var.DISCONNECTED): return for role in self.roles: if users._get(nick) in var.ROLES[role]: break else: if (self.nicks is not None and nick not in self.nicks) or self.roles: return if self.silenced and nick in var.SILENCED: if chan == nick: pm(cli, nick, messages["silenced"]) else: cli.notice(nick, messages["silenced"]) return if self.roles or (self.nicks is not None and nick in self.nicks): self.func(*largs) # don't check restrictions for role commands # Role commands might end the night if it's nighttime if var.PHASE == "night": from src.wolfgame import chk_nightdone chk_nightdone() return forced_owner_only = False if hasattr(botconfig, "OWNERS_ONLY_COMMANDS"): for command in self.cmds: if command in botconfig.OWNERS_ONLY_COMMANDS: forced_owner_only = True break owner = is_owner(nick, ident, host) if self.owner_only or forced_owner_only: if owner: adminlog(chan, rawnick, self.name, rest) self.func(*largs) return if chan == nick: pm(cli, nick, messages["not_owner"]) else: cli.notice(nick, messages["not_owner"]) return flags = var.FLAGS[hostmask] + var.FLAGS_ACCS[acc] admin = is_admin(nick, ident, host) if self.flag and (admin or owner): adminlog(chan, rawnick, self.name, rest) self.func(*largs) return denied_cmds = var.DENY[hostmask] | var.DENY_ACCS[acc] for command in self.cmds: if command in denied_cmds: if chan == nick: pm(cli, nick, messages["invalid_permissions"]) else: cli.notice(nick, messages["invalid_permissions"]) return if self.flag: if self.flag in flags: adminlog(chan, rawnick, self.name, rest) self.func(*largs) return elif chan == nick: pm(cli, nick, messages["not_an_admin"]) else: cli.notice(nick, messages["not_an_admin"]) return self.func(*largs)
def caller(self, cli, rawnick, chan, rest): _ignore_locals_ = True user = users._get(rawnick, allow_none=True) # FIXME if users.equals(chan, users.Bot.nick): # PM target = users.Bot else: target = channels.get(chan, allow_none=True) if user is None or target is None: return dispatcher = MessageDispatcher(user, target) if (not self.pm and dispatcher.private) or (not self.chan and dispatcher.public): return # channel or PM command that we don't allow if dispatcher.public and target is not channels.Main and not (self.flag or self.owner_only): if "" in self.commands or not self.alt_allowed: return # commands not allowed in alt channels if "" in self.commands: self.func(var, dispatcher, rest) return if self.phases and var.PHASE not in self.phases: return if self.playing and (user not in get_players() or user in var.DISCONNECTED): return for role in self.roles: if user in var.ROLES[role]: break else: if (self.users is not None and user not in self.users) or self.roles: return if self.silenced and user.nick in var.SILENCED: # FIXME: Need to change this once var.SILENCED holds User instances dispatcher.pm(messages["silenced"]) return if self.roles or (self.users is not None and user in self.users): self.func(var, dispatcher, rest) # don't check restrictions for role commands # Role commands might end the night if it's nighttime if var.PHASE == "night": from src.wolfgame import chk_nightdone chk_nightdone() return if self.owner_only: if user.is_owner(): adminlog(chan, rawnick, self.name, rest) self.func(var, dispatcher, rest) return dispatcher.pm(messages["not_owner"]) return temp = user.lower() flags = var.FLAGS[temp.rawnick] + var.FLAGS_ACCS[temp.account] # TODO: add flags handling to User if self.flag and (user.is_admin() or user.is_owner()): adminlog(chan, rawnick, self.name, rest) return self.func(var, dispatcher, rest) denied_commands = var.DENY[temp.rawnick] | var.DENY_ACCS[temp.account] # TODO: add denied commands handling to User if self.commands & denied_commands: dispatcher.pm(messages["invalid_permissions"]) return if self.flag: if self.flag in flags: adminlog(chan, rawnick, self.name, rest) self.func(var, dispatcher, rest) return dispatcher.pm(messages["not_an_admin"]) return self.func(var, dispatcher, rest)
def on_privmsg(cli, rawnick, chan, msg, *, notice=False, force_role=None): if notice and "!" not in rawnick or not rawnick: # server notice; we don't care about those return user = users._get(rawnick, allow_none=True) # FIXME ch = chan.lstrip("".join(hooks.Features["PREFIX"])) if users.equals(chan, users.Bot.nick): # PM target = users.Bot else: target = channels.get(ch, allow_none=True) if user is None or target is None: return wrapper = MessageDispatcher(user, target) if wrapper.public and botconfig.IGNORE_HIDDEN_COMMANDS and not chan.startswith(tuple(hooks.Features["CHANTYPES"])): return if (notice and ((wrapper.public and not botconfig.ALLOW_NOTICE_COMMANDS) or (wrapper.private and not botconfig.ALLOW_PRIVATE_NOTICE_COMMANDS))): return # not allowed in settings if force_role is None: # if force_role isn't None, that indicates recursion; don't fire these off twice for fn in decorators.COMMANDS[""]: fn.caller(cli, rawnick, ch, msg) parts = msg.split(sep=" ", maxsplit=1) key = parts[0].lower() if len(parts) > 1: message = parts[1].lstrip() else: message = "" if wrapper.public and not key.startswith(botconfig.CMD_CHAR): return # channel message but no prefix; ignore if key.startswith(botconfig.CMD_CHAR): key = key[len(botconfig.CMD_CHAR):] if not key: # empty key ("") already handled above return # Don't change this into decorators.COMMANDS[key] even though it's a defaultdict, # as we don't want to insert bogus command keys into the dict. cmds = [] phase = var.PHASE if user in get_participants(): roles = get_all_roles(user) # A user can be a participant but not have a role, for example, dead vengeful ghost has_roles = len(roles) != 0 if force_role is not None: roles &= {force_role} # only fire off role commands for the forced role common_roles = set(roles) # roles shared by every eligible role command have_role_cmd = False for fn in decorators.COMMANDS.get(key, []): if not fn.roles: cmds.append(fn) continue if roles.intersection(fn.roles): have_role_cmd = True cmds.append(fn) common_roles.intersection_update(fn.roles) if force_role is not None and not have_role_cmd: # Trying to force a non-role command with a role. # We allow non-role commands to execute if a role is forced if a role # command is also executed, as this would allow (for example) a bot admin # to add extra effects to all "kill" commands without needing to continually # update the list of roles which can use "kill". However, we don't want to # allow things like "wolf pstats" because that just doesn't make sense. return if has_roles and not common_roles: # getting here means that at least one of the role_cmds is disjoint # from the others. For example, augur see vs seer see when a bare see # is executed. In this event, display a helpful error message instructing # the user to resolve the ambiguity. common_roles = set(roles) info = [0,0] for fn in cmds: fn_roles = roles.intersection(fn.roles) if not fn_roles: continue for role1 in common_roles: info[0] = role1 break for role2 in fn_roles: info[1] = role2 break common_roles &= fn_roles if not common_roles: break wrapper.pm(messages["ambiguous_command"].format(key, info[0], info[1])) return elif force_role is None: cmds = decorators.COMMANDS.get(key, []) for fn in cmds: if phase == var.PHASE: # FIXME: pass in var, wrapper, message instead of cli, rawnick, chan, message fn.caller(cli, rawnick, ch, message)