Skip to content

Commit b36cc23

Browse files
authored
FIX: Fix permissions loss on community servers with tag plugins (#102)
* add tag removal for community servers * add retrieval of host steamid3 * using steamid3 to check for permissions inside clear command * use steamid64 instead of username for chats * fix tests for util.text & commands.clear * fix command controller tests * fix winreg import * refactor StatsData * merge StatsData into LobbyManager * remove ENABLE_STATS option * update lobby_manager * fix map property of lobby_manager * refactor lobby manager * status command triggers are now based on logs instead of time * minor improvements * fix logging for stats, add option to disabling stats * fix tests * add a few LobbyManager tests * update bans, add bans tests * fix bug with line parsing * fix typos * fix openai command prefix removal * add send status command on start until response is received * add more tests
1 parent 9c38c56 commit b36cc23

25 files changed

+1147
-570
lines changed

config.ini

+6-1
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ TOS_VIOLATION = 0
9494
CUSTOM_PROMPT =
9595

9696
[STATS]
97-
ENABLE_STATS=0
9897
STEAM_WEBAPI_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
98+
ENABLE_STATS_LOGS=False
9999

100100
[CUSTOM-MODEL-GENERAL]
101101
ENABLE_SOFT_LIMIT_FOR_CUSTOM_MODEL=True
@@ -123,6 +123,11 @@ CUSTOM_MODEL_HOST=127.0.0.1:5000
123123
; CUSTOM_MODEL_SETTINGS = {"echo": true, "max_tokens": 200, "temperature": 2.0, "stop": ["FOO", "BAR"], "logit_bias": {"50256": -100} }
124124
CUSTOM_MODEL_SETTINGS =
125125

126+
[PERMISSIONS]
127+
; Set to True if the function should fall back to checking the username for admin privileges when the user's SteamID3
128+
; does not match the host's SteamID3.
129+
FALLBACK_TO_USERNAME=True
130+
126131
[EXPERIMENTAL]
127132
; Unlike the standard 'Fire-and-Forget' queue which sends messages without acknowledging
128133
; their delivery, the confirmable queue adds reliability by ensuring that each message is

config.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,13 @@ def has_value(cls, value):
3232
class Config(BaseModel):
3333
APP_VERSION: str = "1.3.0"
3434
HOST_USERNAME: str = ""
35+
HOST_STEAMID3: str = "[U:X:XXXXXXX]"
3536
TOS_VIOLATION: bool
37+
FALLBACK_TO_USERNAME: bool
3638

3739
TF2_LOGFILE_PATH: str
3840
OPENAI_API_KEY: str
3941

40-
ENABLE_STATS: bool
4142
STEAM_WEBAPI_KEY: str
4243
DISABLE_KEYBOARD_BINDINGS: bool
4344
GPT4_COMMAND: str
@@ -55,6 +56,7 @@ class Config(BaseModel):
5556
RTD_COMMAND: str
5657
GLOBAL_CHAT_COMMAND: str
5758
GPT4_ADMIN_ONLY: bool
59+
ENABLE_STATS_LOGS: bool
5860

5961
CUSTOM_PROMPT: str
6062

@@ -91,9 +93,6 @@ def api_key_pattern_match(cls, v):
9193

9294
@validator("STEAM_WEBAPI_KEY")
9395
def steam_webapi_key_pattern_match(cls, v, values):
94-
if not values["ENABLE_STATS"]:
95-
return v
96-
9796
if not re.fullmatch(WEB_API_KEY_RE_PATTERN, v):
9897
buffered_fail_message(
9998
"STEAM WEB API key not set or invalid!", type_="BOTH", level="ERROR"

main.py

+2-14
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@
33
# This is required due to config used in imported modules
44
init_config()
55

6-
import contextlib
76
import sys
87
import threading
9-
import time
108
import tkinter as tk
119

1210
from pynput import keyboard
@@ -18,24 +16,16 @@
1816
from modules.gui.log_window import LogWindow, RedirectStdoutToLogWindow
1917
from modules.logs import get_logger, setup_loggers
2018
from modules.message_queueing import message_queue_handler
21-
from modules.servers.tf2 import get_status
22-
from modules.tf_statistics import StatsData
19+
from modules.lobby_manager import lobby_manager
2320

2421
gui_logger = get_logger("gui")
2522

2623

27-
def status_command_sender():
28-
with contextlib.suppress(Exception):
29-
while True:
30-
get_status()
31-
time.sleep(20)
32-
33-
3424
def keyboard_on_press(key):
3525
if key == keyboard.Key.f11:
3626
state_manager.switch_state()
3727
elif key == keyboard.Key.f10:
38-
gui_logger.info(StatsData.get_data())
28+
gui_logger.info(lobby_manager.get_data())
3929

4030

4131
def run_threads():
@@ -48,8 +38,6 @@ def run_threads():
4838

4939
threading.Thread(target=parse_console_logs_and_build_conversation_history, daemon=True).start()
5040
threading.Thread(target=gpt3_cmd_handler, daemon=True).start()
51-
if config.ENABLE_STATS:
52-
threading.Thread(target=status_command_sender, daemon=True).start()
5341
threading.Thread(target=message_queue_handler, daemon=True).start()
5442
if not config.DISABLE_KEYBOARD_BINDINGS:
5543
keyboard.Listener(on_press=keyboard_on_press).start()

modules/bans.py

+31-28
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import codecs
22
import json
33
from json import JSONDecodeError
4+
from typing import Set
45

56
from modules.logs import get_logger, log_gui_general_message
7+
from modules.typing import Player
68

79
main_logger = get_logger("main")
810
gui_logger = get_logger("gui")
@@ -11,62 +13,63 @@
1113

1214
class BansManager:
1315
def __init__(self, bans_file: str = None):
14-
self.banned_usernames = set()
16+
self.banned_players_steamid3: Set[str] = set()
1517
self.__bans_filename = bans_file or "bans.json"
1618
self.load_banned_players()
1719

18-
def load_banned_players(self):
20+
def load_banned_players(self) -> None:
1921
"""
2022
Loads the set of banned players.
2123
"""
2224
try:
2325
with codecs.open(self.__bans_filename, "r", encoding="utf-8") as file:
24-
self.banned_usernames = set(json.load(file))
25-
except (EOFError, JSONDecodeError):
26-
self.banned_usernames = set()
26+
self.banned_players_steamid3 = set(json.load(file))
27+
except (EOFError, JSONDecodeError) as e:
28+
combo_logger.warning(f"Error parsing .json file [{e}].")
29+
self.banned_players_steamid3 = set()
2730
except FileNotFoundError:
2831
combo_logger.warning(f"File {self.__bans_filename} could not be found.")
29-
self.banned_usernames = set()
32+
self.banned_players_steamid3 = set()
3033
except Exception as e:
3134
combo_logger.warning(f"Failed to load banned players. [{e}]")
32-
self.banned_usernames = set()
35+
self.banned_players_steamid3 = set()
3336

34-
def is_banned_username(self, username: str) -> bool:
37+
def is_banned_player(self, player: Player) -> bool:
3538
"""
3639
Checks if a given username is banned.
3740
"""
38-
main_logger.trace(f"Checking if '{username}' is banned.")
39-
return username in self.banned_usernames
41+
main_logger.trace(f"Checking if '{player.name}' [{player.steamid64}] is banned.")
42+
return player.steamid3 in self.banned_players_steamid3
4043

41-
def ban_player(self, username: str) -> None:
44+
def ban_player(self, player: Player) -> None:
4245
"""
4346
Adds a player to the set of banned players.
4447
"""
45-
main_logger.debug(f"Trying to ban '{username}'")
46-
if not self.is_banned_username(username):
47-
self.banned_usernames.add(username)
48-
log_gui_general_message(f"BANNED '{username}'")
49-
main_logger.debug(f"Successfully banned {username}")
48+
main_logger.debug(f"Trying to ban '{player.name}' [{player.steamid64}]")
49+
if not self.is_banned_player(player):
50+
self.banned_players_steamid3.add(player.steamid3)
51+
log_gui_general_message(f"BANNED '{player.name}' [{player.steamid64}]")
52+
main_logger.debug(f"Successfully banned {player.name} [{player.steamid64}]")
5053
with codecs.open(self.__bans_filename, "w", encoding="utf-8") as f:
51-
json.dump(list(self.banned_usernames), f)
54+
json.dump(list(self.banned_players_steamid3), f)
5255
else:
53-
log_gui_general_message(f"='{username}' ALREADY BANNED")
54-
main_logger.debug(f"{username} already banned.")
56+
log_gui_general_message(f"='{player}' [{player.steamid64}] ALREADY BANNED")
57+
main_logger.debug(f"{player.name} [{player.steamid64}] already banned.")
5558

56-
def unban_player(self, username: str) -> None:
59+
def unban_player(self, player: Player) -> None:
5760
"""
5861
Removes a player from the set of banned players.
5962
"""
60-
main_logger.debug(f"Trying to unban '{username}'")
61-
if self.is_banned_username(username):
62-
self.banned_usernames.remove(username)
63-
log_gui_general_message(f"UNBANNED '{username}'")
64-
main_logger.debug(f"Successfully unbanned {username}")
63+
main_logger.debug(f"Trying to unban '{player.name}' [{player.steamid64}]")
64+
if self.is_banned_player(player):
65+
self.banned_players_steamid3.remove(player.steamid3)
66+
log_gui_general_message(f"UNBANNED '{player.name}' [{player.steamid64}]")
67+
main_logger.debug(f"Successfully unbanned {player.name} [{player.steamid64}]")
6568
with codecs.open(self.__bans_filename, "w", encoding="utf-8") as f:
66-
json.dump(list(self.banned_usernames), f)
69+
json.dump(list(self.banned_players_steamid3), f)
6770
else:
68-
log_gui_general_message(f"USER '{username}' WAS NOT BANNED!")
69-
main_logger.debug(f"{username} was not banned, cancelling.")
71+
log_gui_general_message(f"USER '{player.name}' WAS NOT BANNED!")
72+
main_logger.debug(f"{player.name} [{player.steamid64}] was not banned, cancelling.")
7073

7174

7275
bans_manager = BansManager()

modules/chat.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from modules.servers.tf2 import check_connection, set_host_username
1414
from modules.utils.buffered_messages import print_buffered_config_innit_messages
1515
from modules.utils.prompts import load_prompts
16+
from modules.utils.steam import set_host_steamid3
1617
from modules.utils.text import get_console_logline
1718

1819
gui_logger = get_logger("gui")
@@ -37,6 +38,7 @@ def setup() -> None:
3738
check_for_updates()
3839
check_connection()
3940
set_host_username()
41+
set_host_steamid3()
4042
load_prompts()
4143
print_buffered_config_innit_messages()
4244

@@ -68,8 +70,12 @@ def parse_console_logs_and_build_conversation_history() -> None:
6870
controller.register_service(messaging_queue_service)
6971

7072
for logline in get_console_logline():
73+
if logline is None:
74+
continue
7175
if not state_manager.bot_running:
7276
continue
73-
if bans_manager.is_banned_username(logline.username):
77+
if bans_manager.is_banned_player(logline.player):
78+
main_logger.info(f"Player '{logline.player.name}' {logline.player.steamid3} tried to use commands, but "
79+
f"he's banned.")
7480
continue
7581
controller.process_line(logline)

modules/command_controllers.py

+8-8
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from modules.conversation_history import ConversationHistory
88
from modules.logs import get_logger
99
from modules.set_once_dict import SetOnceDictionary
10-
from modules.typing import Command, LogLine
10+
from modules.typing import Command, LogLine, Player
1111

1212
main_logger = get_logger("main")
1313
combo_logger = get_logger("combo")
@@ -17,23 +17,23 @@
1717
class ChatHistoryManager(BaseModel):
1818
GLOBAL = ConversationHistory()
1919

20-
def set_conversation_history_by_name(self, username: str, conv_history: ConversationHistory) -> None:
21-
attr_name = self._get_conv_history_attr_name(username)
20+
def set_conversation_history(self, player: Player, conv_history: ConversationHistory) -> None:
21+
attr_name = self._get_conv_history_attr_name(player.steamid64)
2222

2323
setattr(self, attr_name, conv_history)
2424

25-
def get_conversation_history_by_name(self, username: str) -> ConversationHistory:
26-
attr_name = self._get_conv_history_attr_name(username)
25+
def get_conversation_history(self, player: Player) -> ConversationHistory:
26+
attr_name = self._get_conv_history_attr_name(player.steamid64)
2727

2828
if hasattr(self, attr_name):
2929
return getattr(self, attr_name)
3030
else:
31-
main_logger.info(f"Conversation history for username '{username}' doesn't exist. Creating...")
31+
main_logger.info(f"Conversation history for user '{player.name}' [{player.steamid64}] doesn't exist. Creating...")
3232
setattr(self, attr_name, ConversationHistory())
3333
return getattr(self, attr_name)
3434

35-
def _get_conv_history_attr_name(self, username: str) -> str:
36-
return f"USER_{username}_CH"
35+
def _get_conv_history_attr_name(self, id64: int) -> str:
36+
return f"USER_{id64}_CH"
3737

3838
class Config(BaseConfig):
3939
extra = "allow"

modules/commands/clear_chat.py

+21-11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
from modules.command_controllers import InitializerConfig
2+
from modules.lobby_manager import lobby_manager
3+
from modules.permissions import is_admin
24
from modules.typing import LogLine
35
from modules.utils.text import get_args
46
from modules.logs import get_logger
@@ -9,14 +11,14 @@
911

1012

1113
def handle_clear(logline: LogLine, shared_dict: InitializerConfig):
12-
if logline.username == config.HOST_USERNAME:
14+
if is_admin(logline.player):
1315
args = get_args(logline.prompt.removeprefix(config.CLEAR_CHAT_COMMAND).strip())
1416

1517
if len(args) == 0:
1618
combo_logger.info(f"Clearing chat history for user '{logline.username}'.")
17-
conv_history = shared_dict.CHAT_CONVERSATION_HISTORY.get_conversation_history_by_name(logline.username)
19+
conv_history = shared_dict.CHAT_CONVERSATION_HISTORY.get_conversation_history(logline.player)
1820
conv_history.reset()
19-
shared_dict.CHAT_CONVERSATION_HISTORY.set_conversation_history_by_name(logline.username, conv_history)
21+
shared_dict.CHAT_CONVERSATION_HISTORY.set_conversation_history(logline.player, conv_history)
2022
return
2123

2224
if r"\global" in args:
@@ -46,17 +48,25 @@ def handle_clear(logline: LogLine, shared_dict: InitializerConfig):
4648
name = name.removeprefix("'")
4749
name = name.removesuffix("'")
4850

49-
combo_logger.info(f"Clearing chat history for user '{name}'.")
50-
conv_history = shared_dict.CHAT_CONVERSATION_HISTORY.get_conversation_history_by_name(name)
51-
conv_history.reset()
52-
shared_dict.CHAT_CONVERSATION_HISTORY.set_conversation_history_by_name(name, conv_history)
51+
player = lobby_manager.get_player_by_name(name)
52+
if player is not None:
53+
combo_logger.info(f"Clearing chat history for user '{player.name}'.")
54+
conv_history = shared_dict.CHAT_CONVERSATION_HISTORY.get_conversation_history(player)
55+
conv_history.reset()
56+
shared_dict.CHAT_CONVERSATION_HISTORY.set_conversation_history(player, conv_history)
57+
else:
58+
combo_logger.info(f"Failed to find user with name: '{name}'.")
5359

5460
except Exception as e:
5561
main_logger.trace(f"Failed to parse arg in clear chat command. [{e}]")
5662
continue
5763

5864
else:
59-
combo_logger.info(f"Clearing chat history for user '{logline.username}'.")
60-
conv_history = shared_dict.CHAT_CONVERSATION_HISTORY.get_conversation_history_by_name(logline.username)
61-
conv_history.reset()
62-
shared_dict.CHAT_CONVERSATION_HISTORY.set_conversation_history_by_name(logline.username, conv_history)
65+
player = lobby_manager.get_player_by_name(logline.username)
66+
if player is not None:
67+
combo_logger.info(f"Clearing chat history for user '{player.name}'.")
68+
conv_history = shared_dict.CHAT_CONVERSATION_HISTORY.get_conversation_history(player)
69+
conv_history.reset()
70+
shared_dict.CHAT_CONVERSATION_HISTORY.set_conversation_history(player, conv_history)
71+
else:
72+
combo_logger.info(f"Failed to find user with name: '{logline.username}'.")

modules/commands/gui/bans.py

+18-8
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,31 @@
11
from modules.bans import bans_manager
2+
from modules.command_controllers import InitializerConfig
23
from modules.logs import get_logger
4+
from modules.lobby_manager import lobby_manager
35

46
gui_logger = get_logger("gui")
57

68

7-
def handle_ban(command, shared_dict):
9+
def handle_ban(command: str, shared_dict: InitializerConfig):
810
username = command.removeprefix("ban ").strip()
9-
bans_manager.ban_player(username)
11+
player = lobby_manager.get_player_by_name(username)
12+
if player is not None:
13+
bans_manager.ban_player(player)
14+
return None
15+
gui_logger.info(f"Player '{username}' not found.")
1016

1117

12-
def handle_unban(command, shared_dict):
13-
name = command.removeprefix("unban ").strip()
14-
bans_manager.unban_player(name)
18+
def handle_unban(command: str, shared_dict: InitializerConfig):
19+
username = command.removeprefix("unban ").strip()
20+
player = lobby_manager.get_player_by_name(username)
21+
if player is not None:
22+
bans_manager.unban_player(player)
23+
return None
24+
gui_logger.info(f"Player '{username}' not found.")
1525

1626

17-
def handle_list_bans(command, shared_dict):
18-
if len(bans_manager.banned_usernames) == 0:
27+
def handle_list_bans(command: str, shared_dict: InitializerConfig):
28+
if len(bans_manager.banned_players_steamid3) == 0:
1929
gui_logger.info("### NO BANS ###")
2030
else:
21-
gui_logger.info("### BANNED PLAYERS ###", *list(bans_manager.banned_usernames), sep="\n")
31+
gui_logger.info("### BANNED PLAYERS ###", *list(bans_manager.banned_players_steamid3), sep="\n")

0 commit comments

Comments
 (0)