From 4cf81714262b4190104b8db669c7504b48f166f3 Mon Sep 17 00:00:00 2001 From: Ryan Schmidt Date: Tue, 15 Dec 2020 16:16:52 -0700 Subject: [PATCH] Fix gstats/rstats --- messages/en.json | 7 ++++--- src/db/__init__.py | 26 ++++++++++++++++---------- src/functions.py | 12 ++++++++++-- src/wolfgame.py | 40 ++++++++++++++++++---------------------- 4 files changed, 48 insertions(+), 37 deletions(-) diff --git a/messages/en.json b/messages/en.json index 81f0ae6e..11d1b929 100644 --- a/messages/en.json +++ b/messages/en.json @@ -85,9 +85,7 @@ "lover": ["lover", "lovers"], "vg activated": ["vg activated", "vgs activated"], "vg driven off": ["vg driven off", "vgs driven off"], - "entranced": ["entranced", "entranced"], - "****": "The following items are old roles that are no longer in use, but need to be here for legacy reasons", - "bureaucrat": ["bureaucrat", "bureaucrats"] + "entranced": ["entranced", "entranced"] }, "_gamemodes": { "*": "For assistance in configuring this section, please see https://werewolf.chat/Translation#Gamemodes", @@ -1291,6 +1289,9 @@ "db_gstats_gm_p": "{0:bold}p: {1}", "db_gstats_gm_all_total": "Total games: {0} | {1:join_simple}", "db_gstats_gm_specific_total": "Total games ({0!mode:bold}): {1} | {2:join_simple}", + "db_gstats_nobody": "Nobody", + "db_gstats_no_team_wins": "No Team", + "db_gstats_everyone": "Everyone", "db_role_stats_global": "{role!role:bold} | Team winners: {team} ({teamp:.0%}), Individual winners: {indiv} ({indivp:.0%}), Overall winners: {overall} ({overallp:.0%}), Total games: {total}.", "db_role_stats_specific": "{role!role:bold} in {0!mode} | Team winners: {team} ({teamp:.0%}), Individual winners: {indiv} ({indivp:.0%}), Overall winners: {overall} ({overallp:.0%}), Total games: {total}.", "db_rstats_none": "No stats for {0!role:bold}.", diff --git a/src/db/__init__.py b/src/db/__init__.py index 9933f628..db35caf8 100644 --- a/src/db/__init__.py +++ b/src/db/__init__.py @@ -331,7 +331,7 @@ def get_game_stats(mode, size): conn = _conn() c = conn.cursor() - if mode == "all": + if mode == "*": c.execute("SELECT COUNT(1) FROM game WHERE gamesize = ?", (size,)) else: c.execute("SELECT COUNT(1) FROM game WHERE gamemode = ? AND gamesize = ?", (mode, size)) @@ -340,7 +340,7 @@ def get_game_stats(mode, size): if not total_games: return messages["db_gstats_no_game"].format(size) - if mode == "all": + if mode == "*": c.execute("""SELECT winner AS team, COUNT(1) AS games, @@ -376,10 +376,16 @@ def get_game_stats(mode, size): bits = [] for row in c: - winner = singular(row[0]) - winner = LocalRole(winner).singular.title() - if not winner: - winner = botconfig.NICK.title() + if row[0] == "no_team_wins": + winner = messages["db_gstats_no_team_wins"] + elif not row[0]: + winner = messages["db_gstats_nobody"] + elif row[0] == "everyone": + winner = messages["db_gstats_everyone"] + else: + # FIXME: kill off singular() and convert the db to just store the role key directly instead of a plural + winner = LocalRole(singular(row[0])).singular.title() + bits.append(messages["db_gstats_win"].format(winner, row[1], row[1]/total_games)) bits.append(messages["db_gstats_total"].format(total_games)) @@ -389,18 +395,18 @@ def get_game_totals(mode): conn = _conn() c = conn.cursor() - if mode == "all": + if mode == "*": c.execute("SELECT COUNT(1) FROM game") else: c.execute("SELECT COUNT(1) FROM game WHERE gamemode = ?", (mode,)) total_games = c.fetchone()[0] if not total_games: - if mode == "all": + if mode == "*": return messages["db_gstats_gm_none_all"] return messages["db_gstats_gm_none"].format(mode) - if mode == "all": + if mode == "*": c.execute("""SELECT gamesize, COUNT(1) AS games @@ -419,7 +425,7 @@ def get_game_totals(mode): for row in c: totals.append(messages["db_gstats_gm_p"].format(row[0], row[1])) - if mode == "all": + if mode == "*": return messages["db_gstats_gm_all_total"].format(total_games, totals) return messages["db_gstats_gm_specific_total"].format(mode, total_games, totals) diff --git a/src/functions.py b/src/functions.py index 754d9961..a0553cf1 100644 --- a/src/functions.py +++ b/src/functions.py @@ -163,7 +163,7 @@ def get_reveal_role(user): else: return "village member" -def match_role(var, role: str, remove_spaces: bool = False, allow_special: bool = True, scope: Optional[Iterable[str]] = None) -> Match[LocalRole]: +def match_role(var, role: str, remove_spaces: bool = False, allow_extra: bool = False, allow_special: bool = True, scope: Optional[Iterable[str]] = None) -> Match[LocalRole]: """ Match a partial role or alias name into the internal role key. :param var: Game state @@ -171,6 +171,8 @@ def match_role(var, role: str, remove_spaces: bool = False, allow_special: bool :param remove_spaces: Whether or not to remove all spaces before matching. This is meant for contexts where we truly cannot allow spaces somewhere; otherwise we should prefer that the user matches including spaces where possible for friendlier-looking commands. + :param allow_extra: Whether to allow keys that are defined in the translation file but do not exist in the bot. + Typically these are roles that were previously removed. :param allow_special: Whether to allow special keys (lover, vg activated, etc.). If scope is set, this parameter is ignored. :param scope: Limit matched roles to these explicitly passed-in roles (iterable of internal role names). @@ -194,6 +196,8 @@ def match_role(var, role: str, remove_spaces: bool = False, allow_special: bool filtered_matches = set() if scope is not None: allowed = set(scope) + elif allow_extra: + allowed = set(role_map.values()) | special_keys else: allowed = All.roles | special_keys @@ -203,7 +207,7 @@ def match_role(var, role: str, remove_spaces: bool = False, allow_special: bool return Match(filtered_matches) -def match_mode(var, mode: str, remove_spaces: bool = False, scope: Optional[Iterable[str]] = None) -> Match[LocalMode]: +def match_mode(var, mode: str, remove_spaces: bool = False, allow_extra: bool = False, scope: Optional[Iterable[str]] = None) -> Match[LocalMode]: """ Match a partial game mode into the internal game mode key. :param var: Game state @@ -211,6 +215,8 @@ def match_mode(var, mode: str, remove_spaces: bool = False, scope: Optional[Iter :param remove_spaces: Whether or not to remove all spaces before matching. This is meant for contexts where we truly cannot allow spaces somewhere; otherwise we should prefer that the user matches including spaces where possible for friendlier-looking commands. + :param allow_extra: Whether to allow keys that are defined in the translation file but do not exist in the bot. + Typically these are game modes that were previously removed. :param scope: Limit matched modes to these explicitly passed-in modes (iterable of internal mode names). :return: Match object with all matches (see src.match.match_all) """ @@ -225,6 +231,8 @@ def match_mode(var, mode: str, remove_spaces: bool = False, scope: Optional[Iter filtered_matches = set() if scope is not None: allowed = set(scope) + elif allow_extra: + allowed = set(mode_map.values()) else: allowed = set(var.GAME_MODES) diff --git a/src/wolfgame.py b/src/wolfgame.py index b339b3a5..29066861 100644 --- a/src/wolfgame.py +++ b/src/wolfgame.py @@ -3126,7 +3126,7 @@ def gamestats(var, wrapper, message): if msg and not msg[0].isdigit(): gamemode = msg[0] if gamemode != "*": - matches = match_mode(var, gamemode, remove_spaces=True) + matches = match_mode(var, gamemode, remove_spaces=True, allow_extra=True) if matches: gamemode = matches.get().key elif len(matches) == 0: @@ -3137,12 +3137,8 @@ def gamestats(var, wrapper, message): return msg.pop(0) - # Check for invalid input if msg and msg[0].isdigit(): gamesize = int(msg[0]) - if gamemode != "all" and not (var.GAME_MODES[gamemode][1] <= gamesize <= var.GAME_MODES[gamemode][2]): - wrapper.pm(messages["integer_range"].format(var.GAME_MODES[gamemode][1], var.GAME_MODES[gamemode][2])) - return # List all games sizes and totals if no size is given if not gamesize: @@ -3200,9 +3196,9 @@ def player_stats(var, wrapper, message): wrapper.pm(*totals, sep=", ") else: role = " ".join(params[1:]) - matches = match_role(var, role) + matches = match_role(var, role, allow_extra=True) - if not matches: + if len(matches) == 0: wrapper.send(messages["no_such_role"].format(role)) return elif len(matches) > 1: @@ -3221,7 +3217,6 @@ def my_stats(var, wrapper, message): @command("rolestats", pm=True) def role_stats(var, wrapper, message): """Gets the stats for a given role in a given gamemode or lists role totals across all games if no role is given.""" - # NOTE: Need to dynamically translate roles and gamemodes if (wrapper.public and var.LAST_RSTATS and var.RSTATS_RATE_LIMIT and var.LAST_RSTATS + timedelta(seconds=var.RSTATS_RATE_LIMIT) > datetime.now()): wrapper.pm(messages["command_ratelimited"]) @@ -3241,16 +3236,25 @@ def role_stats(var, wrapper, message): wrapper.pm(*totals, sep=", ", first=first) return - roles = match_role(var, message) - if params[-1] == "all" and not roles: - roles = match_role(var, " ".join(params[:-1])) + roles = match_role(var, message, allow_extra=True) + if params[-1] == "*" and not roles: + role = " ".join(params[:-1]) + roles = match_role(var, role, allow_extra=True) + if not roles: + if len(roles) > 0: + wrapper.pm(messages["ambiguous_role"].format(roles)) + else: + wrapper.pm(messages["no_such_role"].format(role)) + return + if roles: wrapper.pm(db.get_role_stats(roles.get().key)) return gamemode = params[-1] - matches = match_mode(var, gamemode, remove_spaces=True) - if matches: + roles = match_role(var, " ".join(params[:-1]), allow_extra=True) + matches = match_mode(var, gamemode, remove_spaces=True, allow_extra=True) + if matches and roles: gamemode = matches.get().key else: if len(roles) > 0: @@ -3266,15 +3270,7 @@ def role_stats(var, wrapper, message): wrapper.pm(*totals, sep=", ", first=first) return - role = " ".join(params[:-1]) - roles = match_role(var, role) - if not roles: - if len(roles) == 0: - wrapper.pm(messages["no_such_role"].format(role)) - else: - wrapper.pm(messages["ambiguous_role"].format([r.singular for r in roles])) - return - wrapper.pm(db.get_role_stats(roles[0], gamemode)) + wrapper.pm(db.get_role_stats(roles.get().key, gamemode)) @command("whoami", pm=True) def whoami(var, wrapper, message):