From 34e744beb4794a5505e6ac18f896dd8b3f4df2b8 Mon Sep 17 00:00:00 2001 From: Rushaway Date: Wed, 14 Oct 2026 10:09:40 +0200 Subject: [PATCH 1/2] fix(commands): color tags, encoded queries, missing-key and paging fixes - MyInstantsSearch: the "No sounds found" message was a plain string with doubled braces, so players saw the literal {{darkred}} tags. - UrbanDictionary / OpenWeather: pass user input through params= so it is URL-encoded (fixes "a&b", "#", and appid injection). OpenWeather now checks for a missing API key like WolframAlpha/WUnderground. - MyInstantsSearch: tolerate a missing "parameters" block and a missing VoiceServer.Proxy key. - Disable: drop the unreachable "disabled by a higher level user" branch (it sat inside `if not disabled`). - Search / MyInstantsSearch / Help: clamp max_results to >= 1 and stop shadowing the max builtin; remove the dead first start/end computation in get_menu_page_content. Closes #192 Co-Authored-By: Claude Opus 5.5 --- src/torchlight/Commands.py | 77 +++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 43 deletions(-) diff --git a/src/torchlight/Commands.py b/src/torchlight/Commands.py index 3e288fd..5bfdb32 100644 --- a/src/torchlight/Commands.py +++ b/src/torchlight/Commands.py @@ -115,11 +115,6 @@ def get_menu_page_content( max_items: int, max_pages: int, ) -> tuple[dict[str, str], int, int]: - start = (page - 1) * max_items if page else 0 - end = len(res) - if end > max_items: - end = start + max_items - start = (page - 1) * max_items end = start + max_items command_items = dict(list(res.items())[start:end]) @@ -386,7 +381,10 @@ async def _func(self, message: list[str], player: Player) -> int: if self.check_disabled(player): return -1 - data = await Utils.FetchJson(f"https://api.urbandictionary.com/v0/define?term={message[1]}") + data = await Utils.FetchJson( + "https://api.urbandictionary.com/v0/define", + params={"term": message[1]}, + ) if not data: return 3 @@ -440,27 +438,35 @@ def degreeToCardinal(self, degree: int) -> str: async def _func(self, message: list[str], player: Player) -> int: self.logger.debug(sys._getframe().f_code.co_name + " " + str(message)) + if not self.torchlight.config["OpenWeatherAPIKey"]: + self.torchlight.SayPrivate( + message="OpenWeather is not configured (API key missing)", + player=player, + ) + return 1 + if self.check_chat_cooldown(player): return -1 if self.check_disabled(player): return -1 + params: dict[str, str] = { + "APPID": self.torchlight.config["OpenWeatherAPIKey"], + "units": "metric", + } if not message[1]: # Use GeoIP location if self.geo_ip is None: self.torchlight.SayPrivate(player, "[OW] Location lookup is unavailable, please specify a city.") return 1 info = self.geo_ip.city(player.address.split(":")[0]) - search = f"lat={info.location.latitude}&lon={info.location.longitude}" + params["lat"] = str(info.location.latitude) + params["lon"] = str(info.location.longitude) else: - search = f"q={message[1]}" + params["q"] = message[1] - data = await Utils.FetchJson( - "https://api.openweathermap.org/data/2.5/weather?APPID={}&units=metric&{}".format( - self.torchlight.config["OpenWeatherAPIKey"], search - ) - ) + data = await Utils.FetchJson("https://api.openweathermap.org/data/2.5/weather", params=params) if not data: return 3 @@ -824,9 +830,9 @@ async def _func(self, message: list[str], player: Player) -> int: self.torchlight.SayPrivate(player, "{} results: {}".format(len(res), ", ".join(res.keys()))) actual_count = len(res) - max = self.get_config().get("parameters", {}).get("max_results", 30) + max_results = max(1, self.get_config().get("parameters", {}).get("max_results", 30)) - max_pages = (actual_count + max - 1) // max + max_pages = (actual_count + max_results - 1) // max_results if page > max_pages: page = max_pages if page < 1: @@ -837,7 +843,7 @@ async def _func(self, message: list[str], player: Player) -> int: search=voice_trigger, res=res, page=page, - max_items=max, + max_items=max_results, max_pages=max_pages, ) @@ -1255,15 +1261,6 @@ async def _func(self, message: list[str], player: Player) -> int: class Disable(BaseCommand): async def _func(self, message: list[str], player: Player) -> int: if not self.torchlight.disabled: - if self.torchlight.disabled > player.admin.level: - self.torchlight.SayPrivate( - player, - ( - "You don't have access to disable torchlight" - ", since it was already disabled by a higher level user." - ), - ) - return 1 self.torchlight.SayChat( "Torchlight has been disabled for the duration of this map - Type !enable to enable it again." ) @@ -1449,7 +1446,8 @@ async def _func(self, message: list[str], player: Player) -> int: search_only: bool = False command_config = self.get_config() - if "search_cmd" in command_config["parameters"] and message[0] == command_config["parameters"]["search_cmd"]: + parameters = command_config.get("parameters", {}) + if "search_cmd" in parameters and message[0] == parameters["search_cmd"]: search_only = True # we need to specify the actual page and result if there is any. @@ -1471,8 +1469,8 @@ async def _func(self, message: list[str], player: Player) -> int: keywords_banned: list[str] = [] - if search and "parameters" in command_config and "keywords_banned" in command_config["parameters"]: - keywords_banned = command_config["parameters"]["keywords_banned"] + if search and "keywords_banned" in parameters: + keywords_banned = parameters["keywords_banned"] normalized_search = search.lower() if search else "" for keyword_banned in keywords_banned: @@ -1492,9 +1490,7 @@ async def _func(self, message: list[str], player: Player) -> int: player.myinstants_cooldown = current_time + cooldown - proxy = None - if self.torchlight.config["VoiceServer"]["Proxy"]: - proxy = self.torchlight.config["VoiceServer"]["Proxy"] + proxy = self.torchlight.config["VoiceServer"].get("Proxy") or None urls: dict[str, str] | str | None = None @@ -1504,7 +1500,7 @@ async def _func(self, message: list[str], player: Player) -> int: if search: self.torchlight.SayPrivate(player, f"{{darkred}}[MyInstants]{{default}} No sound found for {search}") else: - self.torchlight.SayPrivate(player, "{{darkred}}[MyInstants]{{default}} No sounds found") + self.torchlight.SayPrivate(player, "{darkred}[MyInstants]{default} No sounds found") return 1 if isinstance(urls, str): @@ -1527,12 +1523,10 @@ async def _func(self, message: list[str], player: Player) -> int: return 0 urls = {f"{play_cmd} {k}": v for k, v in urls.items()} - max = 10 - if "parameters" in command_config and "max_results" in command_config["parameters"]: - max = command_config["parameters"]["max_results"] + max_results = max(1, parameters.get("max_results", 10)) actual_count = len(urls) - max_pages = (actual_count + max - 1) // max + max_pages = (actual_count + max_results - 1) // max_results if page > max_pages: page = max_pages if page < 1: @@ -1543,7 +1537,7 @@ async def _func(self, message: list[str], player: Player) -> int: search=search, res=urls, page=page, - max_items=max, + max_items=max_results, max_pages=max_pages, ) @@ -1603,13 +1597,10 @@ async def _func(self, message: list[str], player: Player) -> int: if message[1] and message[1].isdigit(): page = int(message[1]) - max = 5 - command_config = self.get_config() - if "parameters" in command_config and "max_results" in command_config["parameters"]: - max = command_config["parameters"]["max_results"] + max_results = max(1, self.get_config().get("parameters", {}).get("max_results", 5)) actual_count = len(res) - max_pages = (actual_count + max - 1) // max + max_pages = (actual_count + max_results - 1) // max_results if page > max_pages: page = max_pages if page < 1: @@ -1620,7 +1611,7 @@ async def _func(self, message: list[str], player: Player) -> int: search=None, res=res, page=page, - max_items=max, + max_items=max_results, max_pages=max_pages, ) From b48412d9e50dace9a6f2fd35df4eb7774e39f987 Mon Sep 17 00:00:00 2001 From: Rushaway Date: Wed, 14 Oct 2026 13:44:39 +0200 Subject: [PATCH 2/2] Bump version from 1.8.26 to 1.8.40 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index ea2dee2..f3f263e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.8.26 +1.8.40