diff --git a/botconfig.example.yml b/botconfig.example.yml index bf10a27d..423755e6 100644 --- a/botconfig.example.yml +++ b/botconfig.example.yml @@ -164,3 +164,14 @@ gameplay: notice: true # Include which user is spectating in the above alert, if enabled. include_user: false + +# Reaper. Users that leave during the game or are otherwise idle will be automatically removed from the game +# after a period of time (by killing them in-game). This allows a game to continue onwards with the active players +# rather than being in a standstill. It is recommended to keep the reaper enabled for this reason, however the reaper +# will also automatically issue warning points to users by default, which can eventually cause stasis (aka time-outs) +# or temporary bans from the channel. +reaper: + # Whether or not the reaper is enabled. If disabled, idle or disconnected users are NOT automatically killed + enabled: true + # If the reaper is enabled, should it issue automatic warning points to people it kills? + autowarn: true diff --git a/src/debug/decorators.py b/src/debug/decorators.py index 6206abee..11a3bbe3 100644 --- a/src/debug/decorators.py +++ b/src/debug/decorators.py @@ -79,6 +79,8 @@ def __exit__(self, exc_type: Optional[type], exc_value: Optional[BaseException], if _local.handler is None: _local.handler = chain_exceptions(exc_value) + user_data_level = config.Main.get("telemetry.errors.user_data_level") + channel_data_level = config.Main.get("telemetry.errors.user_data_level") traceback_verbosity = config.Main.get("telemetry.errors.traceback_verbosity") if traceback_verbosity > 0: word = "\nLocal variables from frame #{0} (in {1}):\n" @@ -159,7 +161,7 @@ def __exit__(self, exc_type: Optional[type], exc_value: Optional[BaseException], variables.append("{0} = {1!r}".format(key, value)) # dump full list of known users with verbose output, as everything above has truncated output for readability - if config.Main.get("telemetry.errors.user_data_level") > 1: + if user_data_level > 1: import src.users variables.append("\nAll connected users:\n") for user in src.users.users(): @@ -183,7 +185,14 @@ def __exit__(self, exc_type: Optional[type], exc_value: Optional[BaseException], # sanitize paths in tb: convert backslash to forward slash and remove prefixes from src and library paths variables[1] = variables[1].replace("\\", "/") - variables[1] = re.sub(r'File "[^"]*/(src|gamemodes|oyoyo|roles|[Ll]ib|wolfbot)', r'File "/\1', variables[1]) + variables[1] = re.sub(r'File "[^"]*?/(src|gamemodes|oyoyo|messages|hooks|roles|[Ll]ib|wolfbot)', r'File "/\1', variables[1]) + + # the exception message may leak user/channel data since it was stringified without our custom "for_tb" format + if user_data_level < 2: + variables[1] = re.sub(r'User\(([^,]+),.*\)', r'User(\1)' if user_data_level == 1 else "User(?)", variables[1]) + + if channel_data_level < 1: + variables[1] = re.sub(r'Channel\(.*\)', "Channel(?)", variables[1]) # sanitize values within local frames if len(variables) > 3: diff --git a/src/defaultsettings.yml b/src/defaultsettings.yml index 5f331009..afdeae82 100644 --- a/src/defaultsettings.yml +++ b/src/defaultsettings.yml @@ -1076,6 +1076,14 @@ reaper: &reaper _desc: Whether or not the reaper is enabled. If disabled, nobody will idle out or get automatic warnings. _type: bool _default: true + autowarn: + _desc: > + Whether or not to automatically issue warnings to users who are killed by the reaper. If disabled, + users will still be removed from the game and are guaranteed a game loss, but there will be no further + consequences applied to them. If this setting is disabled, the various points settings elsewhere in reaper + settings have no effect. + _type: bool + _default: true idle: _desc: > Idle time is defined as how long it has been since the user has spoken in channel. If gameplay.nightchat diff --git a/src/hooks.py b/src/hooks.py index ba292c2d..dc59670a 100644 --- a/src/hooks.py +++ b/src/hooks.py @@ -166,7 +166,9 @@ def end_who(cli, bot_server, bot_nick, target, rest): else: target.dispatch_queue() - old = _who_old.get(target.name, target) + old = None + if target is not None: + old = _who_old.get(target.name, target) _who_old.clear() Event("who_end", {}, old=old).dispatch(target) diff --git a/src/reaper.py b/src/reaper.py index 2c1acc79..0916e638 100644 --- a/src/reaper.py +++ b/src/reaper.py @@ -75,7 +75,7 @@ def reaper(var: GameState, gameid: int): # part_death, part_death_no_reveal, part_warning # account_death, account_death_no_reveal, account_warning channels.Main.send(messages[f"{what}_death{reveal}"].format(dcedplayer, revealrole)) - if var.current_phase != "join": + if config.Main.get("reaper.autowarn") and var.current_phase != "join": NIGHT_IDLED.discard(dcedplayer) # don't double-dip if they idled out night as well add_warning(dcedplayer, config.Main.get(f"reaper.{what}.points"), @@ -120,7 +120,7 @@ def reaper(var: GameState, gameid: int): channels.Main.send(messages[f"idle_death{reveal}"].format(user, get_reveal_role(var, user))) if var.in_game: DCED_LOSERS.add(user) - if config.Main.get("reaper.idle.enabled"): + if config.Main.get("reaper.autowarn") and config.Main.get("reaper.idle.enabled"): NIGHT_IDLED.discard(user) # don't double-dip if they idled out night as well add_warning(user, config.Main.get("reaper.idle.points"), users.Bot, messages["idle_warning"], expires=config.Main.get("reaper.idle.expiration")) add_dying(var, user, "bot", "idle", death_triggers=False) @@ -198,7 +198,7 @@ def on_del_player(evt: Event, var: GameState, player: User, all_roles: set[str], @event_listener("reset") def on_reset(evt: Event, var: GameState): # Add warnings for people that idled out night - if config.Main.get("reaper.night_idle.enabled"): + if config.Main.get("reaper.autowarn") and config.Main.get("reaper.night_idle.enabled"): for player in NIGHT_IDLED: if player.is_fake: continue