diff --git a/rcon/api_commands.py b/rcon/api_commands.py index ecaf970ae..b13bb043f 100644 --- a/rcon/api_commands.py +++ b/rcon/api_commands.py @@ -1,8 +1,9 @@ +import functools import inspect from collections import defaultdict from datetime import datetime, timedelta from logging import getLogger -from typing import Any, Iterable, Literal, Optional, Sequence, Type +from typing import Any, Dict, Iterable, Literal, Optional, Sequence, Type from rcon import blacklist, game_logs, maps, player_history from rcon.audit import ingame_mods, online_mods @@ -20,7 +21,6 @@ from rcon.settings import SERVER_INFO from rcon.types import ( AdminUserType, - BlacklistRecordWithPlayerType, BlacklistSyncMethod, BlacklistType, BlacklistWithRecordsType, @@ -78,6 +78,23 @@ CTL: Optional["RconAPI"] = None +def parameter_aliases(alias_to_param: Dict[str, str]): + """Specify parameter aliases of a function. This might be useful to preserve backwards + compatibility or to handle parameters named after a Python reserved keyword. + + Takes a mapping of aliases to their parameter name.""" + def decorator(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + for alias, param in alias_to_param.items(): + if alias in kwargs: + kwargs[param] = kwargs.pop(alias) + return func(*args, **kwargs) + + wrapper._parameter_aliases = alias_to_param + return wrapper + return decorator + def get_rcon_api(credentials: ServerInfoType | None = None) -> "RconAPI": """Return a initialized Rcon connection to the game server @@ -561,6 +578,9 @@ def get_online_mods(self) -> list[AdminUserType]: def get_ingame_mods(self) -> list[AdminUserType]: return ingame_mods() + @parameter_aliases({ + "from": "from_", + }) def get_historical_logs( self, player_name: str | None = None, diff --git a/rcon/hooks.py b/rcon/hooks.py index c4dd1270e..7bce0675f 100644 --- a/rcon/hooks.py +++ b/rcon/hooks.py @@ -221,7 +221,7 @@ def handle_new_match_start(rcon: Rcon, struct_log): # Check that the log is less than 5min old if (datetime.utcnow() - log_time).total_seconds() < 5 * 60: # then we use the current map to be more accurate - if current_map.map.name.lower() == log_map_name.lower(): + if current_map.map.name.lower() == log_map_name.lower().removesuffix(" night"): map_name_to_save = current_map guessed = False else: diff --git a/rcon/maps.py b/rcon/maps.py index b9b73c059..b674bdc17 100644 --- a/rcon/maps.py +++ b/rcon/maps.py @@ -262,7 +262,7 @@ def image_name(self) -> str: shortname="Utah", allies=Faction(name=FactionName.US.value, team=Team.ALLIES), axis=Faction(name=FactionName.GER.value, team=Team.AXIS), - orientation=Orientation.VERTICAL, + orientation=Orientation.HORIZONTAL, ), Map( id="omahabeach", @@ -272,7 +272,7 @@ def image_name(self) -> str: shortname="Omaha", allies=Faction(name=FactionName.US.value, team=Team.ALLIES), axis=Faction(name=FactionName.GER.value, team=Team.AXIS), - orientation=Orientation.VERTICAL, + orientation=Orientation.HORIZONTAL, ), Map( id="purpleheartlane", diff --git a/rcongui/public/tac-maps/hurtgenforrest.webp b/rcongui/public/tac-maps/hurtgenforest.webp similarity index 100% rename from rcongui/public/tac-maps/hurtgenforrest.webp rename to rcongui/public/tac-maps/hurtgenforest.webp diff --git a/rconweb/api/barricade.py b/rconweb/api/barricade.py index 67040725c..c0788816b 100644 --- a/rconweb/api/barricade.py +++ b/rconweb/api/barricade.py @@ -130,7 +130,7 @@ async def _scan_players_loop(self): self._last_seen_session = rows[-1][1] await self.send_request( ServerRequestType.SCAN_PLAYERS, - ScanPlayersRequestPayload(player_ids=[row[0] for row in rows]), + ScanPlayersRequestPayload(player_ids=[row[0] for row in rows]).model_dump(), ) except Exception: logger.exception("Failed to perform scan_players task") diff --git a/rconweb/api/views.py b/rconweb/api/views.py index bf8fdbef9..835485880 100644 --- a/rconweb/api/views.py +++ b/rconweb/api/views.py @@ -192,6 +192,7 @@ def expose_api_endpoint( @wraps(func) def wrapper(request: HttpRequest): parameters = inspect.signature(func).parameters + aliases = getattr(func, '_parameter_aliases', {}) arguments = {} failure = False others = None @@ -219,6 +220,9 @@ def wrapper(request: HttpRequest): # Scrape out special case parameters, like the author of a request is the user name making the request # This does not cast argument types, so things that come in from GET parameters are all going to be strings # so we need to handle this properly inside methods if the types matter + for alias, pname in aliases.items(): + if alias in data: + data[pname] = data.pop(alias) for pname, param in parameters.items(): if pname in ("by", "admin_name"): arguments[pname] = request.user.username @@ -844,10 +848,10 @@ def run_raw_command(request): if not os.getenv("HLL_MAINTENANCE_CONTAINER"): logger.info("Initializing endpoints") - try: - # Dynamically register all the methods from ServerCtl - # TODO: remove deprecated endpoints check once endpoints are removed - for func in ENDPOINT_PERMISSIONS.keys(): + # Dynamically register all the methods from ServerCtl + # TODO: remove deprecated endpoints check once endpoints are removed + for func in ENDPOINT_PERMISSIONS.keys(): + try: name = func.__name__ commands.append( ( @@ -860,9 +864,9 @@ def run_raw_command(request): ), ), ) - logger.info("Done Initializing endpoints") - except: - logger.exception( - "Failed to initialized endpoints - Most likely bad configuration" - ) - raise + except: + logger.exception( + "Failed to initialized endpoint for %r - Most likely bad configuration", func + ) + raise + logger.info("Done Initializing endpoints")