def on_retribution_kill(evt, cli, var, victim, orig_target): t = evt.data["target"] if users._get(t) in GHOSTS: drivenoff[t] = GHOSTS[users._get(t)] GHOSTS[users._get(t)] = "!" + GHOSTS[users._get(t)] evt.data["message"].append(messages["totem_banish"].format(victim, t)) evt.data["target"] = None
def on_chk_decision_lynch5(evt, cli, var, voters): votee = evt.data["votee"] if votee in DESPERATION: # Also kill the very last person to vote them, unless they voted themselves last in which case nobody else dies target = voters[-1] if target != votee: prots = deque(var.ACTIVE_PROTECTIONS[target]) while len(prots) > 0: # an event can read the current active protection and cancel the totem # if it cancels, it is responsible for removing the protection from var.ACTIVE_PROTECTIONS # so that it cannot be used again (if the protection is meant to be usable once-only) desp_evt = Event("desperation_totem", {}) if not desp_evt.dispatch(cli, var, votee, target, prots[0]): return prots.popleft() if var.ROLE_REVEAL in ("on", "team"): r1 = get_reveal_role(target) an1 = "n" if r1.startswith(("a", "e", "i", "o", "u")) else "" tmsg = messages["totem_desperation"].format( votee, target, an1, r1) else: tmsg = messages["totem_desperation_no_reveal"].format( votee, target) cli.msg(botconfig.CHANNEL, tmsg) # we lie to this function so it doesn't devoice the player yet. instead, we'll let the call further down do it evt.data["deadlist"].append(target) better_deadlist = [users._get(p) for p in evt.data["deadlist"]] # FIXME target_user = users._get(target) # FIXME evt.params.del_player(target_user, end_game=False, killer_role="shaman", deadlist=better_deadlist, ismain=False)
def guard(cli, nick, chan, rest): """Guard a player, preventing them from being killed that night.""" if nick in GUARDED: pm(cli, nick, messages["already_protecting"]) return role = get_role(nick) self_in_list = role == "guardian angel" and var.GUARDIAN_ANGEL_CAN_GUARD_SELF victim = get_victim(cli, nick, re.split(" +",rest)[0], False, self_in_list) if not victim: return if (role == "bodyguard" or not var.GUARDIAN_ANGEL_CAN_GUARD_SELF) and victim == nick: pm(cli, nick, messages["cannot_guard_self"]) return if role == "guardian angel" and LASTGUARDED.get(nick) == victim: pm(cli, nick, messages["guardian_target_another"].format(victim)) return angel = users._get(nick) # FIXME target = users._get(victim) # FIXME # self-guard ignores luck/misdirection/exchange totem evt = Event("targeted_command", {"target": target, "misdirection": (angel is not target), "exchange": (angel is not target)}) if not evt.dispatch(var, angel, target): return victim = evt.data["target"].nick GUARDED[nick] = victim LASTGUARDED[nick] = victim if victim == nick: pm(cli, nick, messages["guardian_guard_self"]) else: pm(cli, nick, messages["protecting_target"].format(GUARDED[nick])) pm(cli, victim, messages["target_protected"]) debuglog("{0} ({1}) GUARD: {2} ({3})".format(nick, role, victim, get_role(victim)))
def on_transition_day2(evt, var): for k, d in DEATH.items(): shaman = users._get(k) # FIXME target = users._get(d) # FIXME evt.data["victims"].append(target) evt.data["onlybywolves"].discard(target) evt.data["killers"][target].append(shaman)
def on_get_participant_role(evt, var, nick): if users._get(nick) in GHOSTS: # FIXME if nick in drivenoff: against = drivenoff[nick] else: against = GHOSTS[users._get(nick)] if against == "villagers": evt.data["role"] = "wolf" elif against == "wolves": evt.data["role"] = "villager"
def hvisit(var, wrapper, message): """Entrance a player, converting them to your team.""" if VISITED.get(wrapper.source): wrapper.send(messages["succubus_already_visited"].format(VISITED[wrapper.source])) return target = get_target(var, wrapper, re.split(" +", message)[0], not_self_message="succubus_not_self") if not target: return evt = Event("targeted_command", {"target": target, "misdirection": True, "exchange": False}) evt.dispatch(var, "visit", wrapper.source, target, frozenset({"detrimental", "immediate"})) if evt.prevent_default: return target = evt.data["target"] VISITED[wrapper.source] = target PASSED.discard(wrapper.source) if target not in get_all_players(("succubus",)): ENTRANCED.add(target) wrapper.send(messages["succubus_target_success"].format(target)) else: wrapper.send(messages["harlot_success"].format(target)) if wrapper.source is not target: if target not in get_all_players(("succubus",)): target.send(messages["notify_succubus_target"].format(wrapper.source)) else: target.send(messages["harlot_success"].format(wrapper.source)) revt = Event("succubus_visit", {}) revt.dispatch(var, wrapper.source, target) # TODO: split these into assassin, hag, and alpha wolf when they are split off if users._get(var.TARGETED.get(target.nick), allow_none=True) in get_all_players(("succubus",)): # FIXME msg = messages["no_target_succubus"].format(var.TARGETED[target.nick]) del var.TARGETED[target.nick] if target in get_all_players(("village drunk",)): victim = random.choice(list(get_all_players() - get_all_players(("succubus",)) - {target})) msg += messages["drunk_target"].format(victim) var.TARGETED[target.nick] = victim.nick target.send(msg) if target.nick in var.HEXED and users._get(var.LASTHEXED[target.nick]) in get_all_players(("succubus",)): # FIXME target.send(messages["retract_hex_succubus"].format(var.LASTHEXED[target.nick])) var.TOBESILENCED.remove(wrapper.source.nick) var.HEXED.remove(target.nick) del var.LASTHEXED[target.nick] if users._get(var.BITE_PREFERENCES.get(target.nick), allow_none=True) in get_all_players(("succubus",)): # FIXME target.send(messages["no_kill_succubus"].format(var.BITE_PREFERENCES[target.nick])) del var.BITE_PREFERENCES[target.nick] debuglog("{0} (succubus) VISIT: {1} ({2})".format(wrapper.source, target, get_main_role(target)))
def on_transition_day(evt, var): for k, v in list(KILLS.items()): killer = users._get(k) # FIXME victim = users._get(v) # FIXME evt.data["victims"].append(victim) # even though doomsayer is a wolf, remove from onlybywolves since # that particular item indicates that they were the target of a wolf !kill. # If doomsayer doesn't remove this, roles such as harlot or monster will not # die if they are the target of a doomsayer !see that ends up killing the target. evt.data["onlybywolves"].discard(victim) evt.data["killers"][victim].append(killer)
def on_exchange(evt, cli, var, actor, nick, actor_role, nick_role): for k in set(KILLS): if k.nick == actor or k.nick == nick: del KILLS[k] for k in set(TARGETS): if actor_role == "dullahan" and nick_role != "dullahan" and k.nick == actor: TARGETS[users._get(nick)] = TARGETS.pop(k) - {users._get(nick) } # FIXME elif nick_role == "dullahan" and actor_role != "dullahan" and k.nick == nick: TARGET[users._get(actor)] = TARGETS.pop(k) - {users._get(actor) } # FIXME
def on_transition_day(evt, var): for k, v in list(KILLS.items()): killer = users._get(k) # FIXME victim = users._get(v) # FIXME evt.data["victims"].append(victim) evt.data["onlybywolves"].discard(victim) evt.data["killers"][victim].append(killer) # important, otherwise our del_player listener lets hunter kill again del KILLS[k] if get_main_role(victim) not in var.WOLF_ROLES | var.WIN_STEALER_ROLES: var.DYING.add(killer)
def totem( cli, nick, chan, rest, prefix="You" ): # XXX: The transition_day_begin event needs updating alongside this """Give a totem to a player.""" victim = get_victim(cli, nick, re.split(" +", rest)[0], False, True) if not victim: return if LASTGIVEN.get(nick) == victim: pm(cli, nick, messages["shaman_no_target_twice"].format(victim)) return original_victim = victim role = get_role( nick ) # FIXME: this is bad, check if nick is in var.ROLES[thingy] instead once split totem = "" if role != "crazed shaman": totem = " of " + TOTEMS[nick] tags = set() if role != "crazed shaman" and TOTEMS[nick] in var.BENEFICIAL_TOTEMS: tags.add("beneficial") shaman = users._get(nick) # FIXME target = users._get(victim) # FIXME evt = Event("targeted_command", { "target": target, "misdirection": True, "exchange": True }, action="give a totem{0} to".format(totem)) evt.dispatch(var, "totem", shaman, target, frozenset(tags)) if evt.prevent_default: return victim = evt.data["target"].nick victimrole = get_role(victim) pm(cli, nick, messages["shaman_success"].format(prefix, totem, original_victim)) if role == "wolf shaman": relay_wolfchat_command(cli, nick, messages["shaman_wolfchat"].format( nick, original_victim), ("wolf shaman", ), is_wolf_command=True) SHAMANS[nick] = (victim, original_victim) debuglog("{0} ({1}) TOTEM: {2} ({3})".format(nick, role, victim, TOTEMS[nick]))
def on_del_player(evt, cli, var, nick, nickrole, nicktpls, death_triggers): for h, v in list(KILLS.items()): if v.nick == nick: h.send(messages["hunter_discard"]) del KILLS[h] elif h.nick == nick: del KILLS[h] if death_triggers and nickrole == "dullahan": pl = evt.data["pl"] targets = TARGETS[users._get(nick)].intersection( users._get(x) for x in pl) # FIXME if targets: target = random.choice(list(targets)).nick prots = deque(var.ACTIVE_PROTECTIONS[target]) aevt = Event("assassinate", {"pl": evt.data["pl"]}, del_player=evt.params.del_player, deadlist=evt.params.deadlist, original=evt.params.original, refresh_pl=evt.params.refresh_pl, message_prefix="dullahan_die_", nickrole=nickrole, nicktpls=nicktpls, prots=prots) while len(prots) > 0: # an event can read the current active protection and cancel the totem # if it cancels, it is responsible for removing the protection from var.ACTIVE_PROTECTIONS # so that it cannot be used again (if the protection is meant to be usable once-only) if not aevt.dispatch(cli, var, nick, target, prots[0]): evt.data["pl"] = aevt.data["pl"] return prots.popleft() if var.ROLE_REVEAL in ("on", "team"): role = get_reveal_role(target) an = "n" if role.startswith(("a", "e", "i", "o", "u")) else "" cli.msg( botconfig.CHANNEL, messages["dullahan_die_success"].format( nick, target, an, role)) else: cli.msg( botconfig.CHANNEL, messages["dullahan_die_success_noreveal"].format( nick, target)) debuglog("{0} ({1}) DULLAHAN ASSASSINATE: {2} ({3})".format( nick, nickrole, target, get_role(target))) evt.params.del_player(cli, target, True, end_game=False, killer_role=nickrole, deadlist=evt.params.deadlist, original=evt.params.original, ismain=False) evt.data["pl"] = evt.params.refresh_pl(pl)
def investigate(cli, nick, chan, rest): """Investigate a player to determine their exact role.""" if nick in INVESTIGATED: pm(cli, nick, messages["already_investigated"]) return victim = get_victim(cli, nick, re.split(" +", rest)[0], False) if not victim: return if victim == nick: pm(cli, nick, messages["no_investigate_self"]) return det = users._get(nick) # FIXME target = users._get(victim) # FIXME evt = Event("targeted_command", { "target": target, "misdirection": True, "exchange": True }) evt.dispatch(var, "identify", det, target, frozenset({"info", "immediate"})) if evt.prevent_default: return victim = evt.data["target"].nick vrole = get_role(victim) if vrole == "amnesiac": vrole = var.AMNESIAC_ROLES[victim] evt = Event("investigate", {"role": vrole}) evt.dispatch(cli, var, nick, victim) vrole = evt.data["role"] INVESTIGATED.add(nick) pm(cli, nick, (messages["investigate_success"]).format(victim, vrole)) debuglog("{0} ({1}) ID: {2} ({3})".format(nick, get_role(nick), victim, vrole)) if random.random( ) < var.DETECTIVE_REVEALED_CHANCE: # a 2/5 chance (should be changeable in settings) # The detective's identity is compromised! wcroles = var.WOLFCHAT_ROLES if var.RESTRICT_WOLFCHAT & var.RW_REM_NON_WOLVES: if var.RESTRICT_WOLFCHAT & var.RW_TRAITOR_NON_WOLF: wcroles = var.WOLF_ROLES else: wcroles = var.WOLF_ROLES | {"traitor"} mass_privmsg(cli, list_players(wcroles), messages["investigator_reveal"].format(nick)) debuglog("{0} ({1}) PAPER DROP".format(nick, get_role(nick)))
def on_exchange_roles(evt, var, actor, target, actor_role, target_role): if actor_role == "harlot": if actor.nick in VISITED: if VISITED[actor.nick] is not None: visited = users._get(VISITED[actor.nick]) # FIXME visited.send(messages["harlot_disappeared"].format(actor)) del VISITED[actor.nick] if target_role == "harlot": if target.nick in VISITED: if VISITED[target.nick] is not None: visited = users._get(VISITED[target.nick]) # FIXME visited.send(messages["harlot_disappeared"].format(target)) del VISITED[target.nick]
def on_exchange(evt, var, actor, target, actor_role, target_role): if actor_role in ("bodyguard", "guardian angel"): if actor.nick in GUARDED: guarded = users._get(GUARDED.pop(actor.nick)) # FIXME guarded.send(messages["protector disappeared"]) if actor.nick in LASTGUARDED: del LASTGUARDED[actor.nick] if target_role in ("bodyguard", "guardian angel"): if target.nick in GUARDED: guarded = users._get(GUARDED.pop(target.nick)) # FIXME guarded.send(messages["protector disappeared"]) if target.nick in LASTGUARDED: del LASTGUARDED[target.nick]
def mass_privmsg(cli, targets, msg, notice=False, privmsg=False): from src.users import _get targs = [_get(t) for t in targets] for user in targs: user.queue_message(msg) if targs: user.send_messages()
def on_doctor_immunize(evt, var, doctor, target): user = users._get(target) # FIXME if user in SICK.values(): for n, v in list(SICK.items()): if v is user: del SICK[n] evt.data["message"] = "not_sick"
def vg_kill(var, wrapper, message): """Take revenge on someone each night after you die.""" if GHOSTS[wrapper.source][0] == "!": return target = get_target(var, wrapper, re.split(" +", message)[0]) if not target: return if target is wrapper.source: wrapper.pm(messages["player_dead"]) return wolves = get_players(var.WOLFTEAM_ROLES) if GHOSTS[wrapper.source] == "wolves" and target not in wolves: wrapper.pm(messages["vengeful_ghost_wolf"]) return elif GHOSTS[wrapper.source] == "villagers" and target in wolves: wrapper.pm(messages["vengeful_ghost_villager"]) return orig = target evt = Event("targeted_command", {"target": target.nick, "misdirection": True, "exchange": False}) evt.dispatch(wrapper.source.client, var, "kill", wrapper.source.nick, target.nick, frozenset({"detrimental"})) if evt.prevent_default: return target = users._get(evt.data["target"]) # FIXME KILLS[wrapper.source] = target wrapper.pm(messages["player_kill"].format(orig)) debuglog("{0} (vengeful ghost) KILL: {1} ({2})".format(wrapper.source.nick, target, get_main_role(target))) chk_nightdone(wrapper.source.client)
def on_chk_decision_lynch3(evt, cli, var, voters): votee = evt.data["votee"] if votee in REVEALING: role = get_role(votee) rev_evt = Event("revealing_totem", {"role": role}) rev_evt.dispatch(cli, var, votee) role = rev_evt.data["role"] # TODO: once amnesiac is split, roll this into the revealing_totem event if role == "amnesiac": role = var.AMNESIAC_ROLES[votee] change_role(users._get(votee), "amnesiac", role) # FIXME var.AMNESIACS.add(votee) pm(cli, votee, messages["totem_amnesia_clear"]) # If wolfteam, don't bother giving list of wolves since night is about to start anyway # Existing wolves also know that someone just joined their team because revealing totem says what they are # If turncoat, set their initial starting side to "none" just in case game ends before they can set it themselves if role == "turncoat": var.TURNCOATS[votee] = ("none", -1) an = "n" if role.startswith(("a", "e", "i", "o", "u")) else "" cli.msg(botconfig.CHANNEL, messages["totem_reveal"].format(votee, an, role)) evt.data["votee"] = None evt.prevent_default = True evt.stop_processing = True
def on_revealroles(evt, var, wrapper, nickname, role): if role == "mad scientist": pl = get_players() target1, target2 = _get_targets(var, pl, users._get(nickname)) # FIXME evt.data["special_case"].append( messages["mad_scientist_revealroles_targets"].format( target1, target2))
def end_who(cli, bot_server, bot_nick, target, rest): """Handle the end of WHO/WHOX responses from the server. Ordering and meaning of arguments for the end of a WHO/WHOX request: 0 - The IRCClient instance (like everywhere else) 1 - The server the requester (i.e. the bot) is on 2 - The nickname of the requester (i.e. the bot) 3 - The target the request was made against 4 - A string containing some information; traditionally "End of /WHO list." This fires off the "who_end" event, and dispatches it with two arguments: The game state namespace and the channel or user the request was made to, or None if it could not be resolved. """ try: target = channels.get(target) except KeyError: try: target = users._get(target) # FIXME except KeyError: target = None else: if target._pending is not None: for name, params, args in target._pending: Event(name, params).dispatch(*args) target._pending = None Event("who_end", {}).dispatch(var, target)
def on_chk_decision(evt, cli, var, force): for votee, voters in evt.data["votelist"].items(): if users._get(votee) in get_all_players(("succubus",)): # FIXME for vtr in ENTRANCED: if vtr.nick in voters: evt.data["numvotes"][votee] -= evt.data["weights"][votee][vtr.nick] evt.data["weights"][votee][vtr.nick] = 0
def mode_change(cli, rawnick, chan, mode, *targets): """Update the channel and user modes whenever a mode change occurs. Ordering and meaning of arguments for a MODE change: 0 - The IRCClient instance (like everywhere else) 1 - The raw nick of the mode setter/actor 2 - The channel (target) of the mode change 3 - The mode changes * - The targets of the modes (if any) This takes care of properly updating all relevant users and the channel modes to make sure we remain internally consistent. """ if chan == users.Bot.nick: # we only see user modes set to ourselves users.Bot.modes.update(mode) return if "!" not in rawnick: # Only sync modes if a server changed modes because # 1) human ops probably know better # 2) other bots might start a fight over modes # 3) recursion; we see our own mode changes. evt = Event("sync_modes", {}) evt.dispatch(var) return actor = users._get(rawnick, allow_none=True) # FIXME target = channels.add(chan, cli) target.queue("mode_change", { "mode": mode, "targets": targets }, (var, actor, target))
def dullahan_kill(var, wrapper, message): """Kill someone at night as a dullahan until everyone on your list is dead.""" if not TARGETS[wrapper.source] & set(get_players()): wrapper.pm(messages["dullahan_targets_dead"]) return target = get_target(var, wrapper, re.split(" +", message)[0]) if not target: return if target is wrapper.source: wrapper.pm(messages["no_suicide"]) return orig = target evt = Event("targeted_command", {"target": target.nick, "misdirection": True, "exchange": True}) evt.dispatch(wrapper.client, var, "kill", wrapper.source.nick, target.nick, frozenset({"detrimental"})) if evt.prevent_default: return target = users._get(evt.data["target"]) # FIXME: Need to fix once targeted_command uses the new API KILLS[wrapper.source] = target wrapper.pm(messages["player_kill"].format(orig)) debuglog("{0} (dullahan) KILL: {1} ({2})".format(wrapper.source, target, get_role(target.nick))) chk_nightdone(wrapper.client)
def on_transition_day6(evt, cli, var): for k, d in list(KILLS.items()): if GHOSTS[users._get(k)] == "villagers" and k in evt.data["killers"][d]: evt.data["killers"][d].remove(k) evt.data["killers"][d].insert(0, k) # important, otherwise our del_player listener messages the vg del KILLS[k]
def on_chghost(cli, rawnick, ident, host): """Handle a user changing host without a quit. Ordering and meaning of arguments for CHGHOST: 0 - The IRCClient instance (like everywhere else) 1 - The raw nick (nick!ident@host) of the user switching 2 - The new ident for the user (or same if unchanged) 3 - The new host for the user (or same if unchanged) """ user = users._get(rawnick) # FIXME new = users._add(cli, nick=user.nick, ident=ident, host=host, realname=user.realname, account=user.account) # FIXME if user is not new: new.channels = user.channels.copy() new.timestamp = user.timestamp # We lie, but it's ok for chan in set(user.channels): chan.remove_user(user) chan.users.add(new) user.swap(new)
def mode_change(cli, rawnick, chan, mode, *targets): """Update the channel and user modes whenever a mode change occurs. Ordering and meaning of arguments for a MODE change: 0 - The IRCClient instance (like everywhere else) 1 - The raw nick of the mode setter/actor 2 - The channel (target) of the mode change 3 - The mode changes * - The targets of the modes (if any) This takes care of properly updating all relevant users and the channel modes to make sure we remain internally consistent. """ if chan == users.Bot.nick: # we only see user modes set to ourselves users.Bot.modes.update(mode) return if "!" not in rawnick: # Only sync modes if a server changed modes because # 1) human ops probably know better # 2) other bots might start a fight over modes # 3) recursion; we see our own mode changes. evt = Event("sync_modes", {}) evt.dispatch(var) return actor = users._get(rawnick, allow_none=True) # FIXME target = channels.add(chan, cli) target.queue("mode_change", {"mode": mode, "targets": targets}, (var, actor, target))
def see(cli, nick, chan, rest): """Use your paranormal senses to determine a player's doom.""" role = get_role(nick) if nick in SEEN: pm(cli, nick, messages["seer_fail"]) return victim = get_victim(cli, nick, re.split(" +", rest)[0], False) if not victim: return if victim == nick: pm(cli, nick, messages["no_see_self"]) return if in_wolflist(nick, victim): pm(cli, nick, messages["no_see_wolf"]) return doomsayer = users._get(nick) # FIXME target = users._get(victim) # FIXME evt = Event("targeted_command", { "target": target, "misdirection": True, "exchange": True }) evt.dispatch(var, "see", doomsayer, target, frozenset({"detrimental", "immediate"})) if evt.prevent_default: return victim = evt.data["target"].nick victimrole = get_role(victim) mode, mapping = random.choice(_mappings) pm(cli, nick, messages["doomsayer_{0}".format(mode)].format(victim)) if mode != "sick" or nick not in var.IMMUNIZED: mapping[nick] = victim debuglog("{0} ({1}) SEE: {2} ({3}) - {4}".format(nick, role, victim, victimrole, mode.upper())) relay_wolfchat_command(cli, nick, messages["doomsayer_wolfchat"].format(nick, victim), ("doomsayer", ), is_wolf_command=True) SEEN.add(nick)
def on_succubus_visit(evt, var, succubus, target): if (users._get(SHAMANS.get(target.nick, (None, None))[1], allow_none=True) in get_all_players(("succubus", )) and # FIXME (get_main_role(target) == "crazed shaman" or TOTEMS[target.nick] not in var.BENEFICIAL_TOTEMS)): target.send(messages["retract_totem_succubus"].format( SHAMANS[target.nick][1])) del SHAMANS[target.nick]
def on_get_role_metadata(evt, var, kind): if kind == "night_kills": # hunters is the set of all hunters that have not killed in a *previous* night # (if they're in both HUNTERS and KILLS, then they killed tonight and should be counted) hunters = ({users._get(h) for h in var.ROLES["hunter"]} - HUNTERS) | set( KILLS.keys()) # FIXME evt.data["hunter"] = len(hunters)
def on_chk_decision_lynch(evt, cli, var, voters): votee = evt.data["votee"] if users._get(votee) in var.ROLES[ "mayor"] and votee not in REVEALED_MAYORS: # FIXME cli.msg(botconfig.CHANNEL, messages["mayor_reveal"].format(votee)) REVEALED_MAYORS.add(votee) evt.data["votee"] = None evt.prevent_default = True evt.stop_processing = True
def on_role_assignment(evt, cli, var, gamemode, pl, restart): # assign random targets to dullahan to kill if var.ROLES["dullahan"]: max_targets = math.ceil(8.1 * math.log(len(pl), 10) - 5) for dull in var.ROLES["dullahan"]: TARGETS[users._get(dull)] = set() # FIXME dull_targets = Event("dullahan_targets", {"targets": TARGETS}) # support sleepy dull_targets.dispatch(cli, var, {users._get(x) for x in var.ROLES["dullahan"]}, max_targets) # FIXME players = [users._get(x) for x in pl] # FIXME for dull, ts in TARGETS.items(): ps = players[:] ps.remove(dull) while len(ts) < max_targets: target = random.choice(ps) ps.remove(target) ts.add(target)
def kicked_from_chan(cli, rawnick, chan, target, reason): """Handle a user being kicked from a channel. Ordering and meaning of arguments for a channel KICK: 0 - The IRCClient instance (like everywhere else) 1 - The raw nick (nick!ident@host) of the user performing the kick 2 - The channel the kick was performed on 3 - The target of the kick 4 - The reason given for the kick (always present) """ ch = channels.add(chan, cli) actor = users._get(rawnick, allow_none=True) # FIXME user = users._get(target) # FIXME Event("chan_kick", {}).dispatch(var, ch, actor, user, reason) if user is users.Bot: ch._clear() else: ch.remove_user(user)
def on_nick_change(cli, old_rawnick, nick): """Handle a user changing nicks, which may be the bot itself. Ordering and meaning of arguments for a NICK change: 0 - The IRCClient instance (like everywhere else) 1 - The old (raw) nickname the user changed from 2 - The new nickname the user changed to """ user = users._get(old_rawnick, allow_bot=True) # FIXME user.nick = nick Event("nick_change", {}).dispatch(var, user, old_rawnick)
def on_account_change(cli, rawnick, account): """Handle a user changing accounts, if enabled. Ordering and meaning of arguments for an ACCOUNT change: 0 - The IRCClient instance (like everywhere else) 1 - The raw nick (nick!ident@host) of the user changing accounts 2 - The account the user changed to We don't see our own account changes, so be careful! """ user = users._get(rawnick) # FIXME user.account = account # We don't pass it to add(), since we want to grab the existing one (if any) Event("account_change", {}).dispatch(var, user)
def on_quit(cli, rawnick, reason): """Handle a user quitting the IRC server. Ordering and meaning of arguments for a server QUIT: 0 - The IRCClient instance (like everywhere else) 1 - The raw nick (nick!ident@host) of the user quitting 2 - The reason for the quit (always present) """ user = users._get(rawnick, allow_bot=True) # FIXME Event("server_quit", {}).dispatch(var, user, reason) for chan in set(user.channels): if user is users.Bot: chan._clear() else: chan.remove_user(user)
def part_chan(cli, rawnick, chan, reason=""): """Handle a user leaving a channel, which may be the bot itself. Ordering and meaning of arguments for a channel PART: 0 - The IRCClient instance (like everywhere else) 1 - The raw nick (nick!ident@host) of the user leaving the channel 2 - The channel being left The following argument may or may not be present: 3 - The reason the user gave for parting (if any) """ ch = channels.add(chan, cli) user = users._get(rawnick) # FIXME Event("chan_part", {}).dispatch(var, ch, user, reason) if user is users.Bot: # oh snap! we're no longer in the channel! ch._clear() else: ch.remove_user(user)
def get_role(p): # TODO DEPRECATED: replace with get_main_role(user) from src import users from src.functions import get_main_role return get_main_role(users._get(p))
def pm(cli, target, message): from src.users import _get user = _get(target) user.send(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 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)
def relay_wolfchat_command(cli, nick, message, roles, is_wolf_command=False, is_kill_command=False): from src.roles.helper.wolves import send_wolfchat_message from src import users role = "wolf" if is_wolf_command else None command = "kill" if is_kill_command else None send_wolfchat_message(var, users._get(nick), message, roles, role=role, command=command)
def is_admin(nick, ident=None, host=None, acc=None): from src.users import _get user = _get(nick=nick, ident=ident, host=host, account=acc) return user.is_admin()
def update_modes(self, actor, mode, targets): """Update the channel's mode registry with the new modes. This is called whenever a MODE event is received. All of the modes are kept up-to-date in the channel, even if we don't need it. For instance, banlists are updated properly when the bot receives them. We don't need all the mode information, but it's better to have everything stored than only some parts. """ set_time = int(time.time()) # for list modes timestamp list_modes, all_set, only_set, no_set = Features["CHANMODES"] status_modes = Features["PREFIX"].values() i = 0 for c in mode: if c in ("+", "-"): prefix = c continue if prefix == "+": if c in status_modes: # op/voice status; keep it here and update the user's registry too if c not in self.modes: self.modes[c] = set() user = users._get(targets[i], allow_bot=True) # FIXME self.modes[c].add(user) user.channels[self].add(c) if user in var.OLD_MODES: var.OLD_MODES[user].discard(c) i += 1 elif c in list_modes: # stuff like bans, quiets, and ban and invite exempts if c not in self.modes: self.modes[c] = {} self.modes[c][targets[i]] = ((actor.rawnick if actor is not None else None), set_time) i += 1 else: if c in no_set: # everything else; e.g. +m, +i, +f, etc. targ = None else: targ = targets[i] i += 1 if c in only_set and targ.isdigit(): # +l/+j targ = int(targ) self.modes[c] = targ else: if c in status_modes: if c in self.modes: user = users._get(targets[i], allow_bot=True) # FIXME self.modes[c].discard(user) user.channels[self].discard(c) if not self.modes[c]: del self.modes[c] i += 1 elif c in list_modes: if c in self.modes: self.modes[c].pop(targets[i], None) if not self.modes[c]: del self.modes[c] i += 1 else: if c in all_set: i += 1 # -k needs a target, but we don't care about it del self.modes[c] if "k" in mode: self._key = self.modes.get("k", "")
def in_wolflist(nick, who): from src.roles.helper.wolves import is_known_wolf_ally from src import users return is_known_wolf_ally(var, users._get(nick), users._get(who))