From c0417856a2c84cc61c5c3fda0224562f34e904ff Mon Sep 17 00:00:00 2001 From: Maddox Date: Tue, 28 Apr 2026 18:49:26 -0400 Subject: [PATCH] v0.3.2: percent-encode Seerr query strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Seerr's URL validator rejects aiohttp's default '+' for spaces and demands %20. Switch the Seerr client to RFC 3986-style encoding via urllib.parse.quote(safe='') so multi-word searches work. Reported: '!media search Sheep Detectives' → 400. --- maubot.yaml | 2 +- media_bot/clients/seerr.py | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/maubot.yaml b/maubot.yaml index d20e526..cc28362 100644 --- a/maubot.yaml +++ b/maubot.yaml @@ -1,6 +1,6 @@ maubot: 0.3.1 id: com.3ddbrewery.media -version: 0.3.1 +version: 0.3.2 license: MIT modules: - media_bot diff --git a/media_bot/clients/seerr.py b/media_bot/clients/seerr.py index f21bf3c..ce4848a 100644 --- a/media_bot/clients/seerr.py +++ b/media_bot/clients/seerr.py @@ -10,6 +10,7 @@ Docs reference (Overseerr-compatible): from __future__ import annotations from typing import Optional +from urllib.parse import quote import aiohttp @@ -18,6 +19,11 @@ class SeerrError(RuntimeError): pass +def _qs(params: dict) -> str: + """RFC 3986 query string — Seerr rejects aiohttp's default '+' for spaces.""" + return "&".join(f"{quote(k, safe='')}={quote(str(v), safe='')}" for k, v in params.items()) + + class SeerrClient: def __init__(self, session: aiohttp.ClientSession, base_url: str, api_key: str) -> None: self.session = session @@ -25,7 +31,10 @@ class SeerrClient: self.headers = {"X-Api-Key": api_key, "Accept": "application/json"} async def _get(self, path: str, params: dict | None = None) -> dict | list: - async with self.session.get(f"{self.base}{path}", headers=self.headers, params=params) as r: + url = f"{self.base}{path}" + if params: + url += "?" + _qs(params) + async with self.session.get(url, headers=self.headers) as r: if r.status >= 400: raise SeerrError(f"GET {path} → {r.status}: {(await r.text())[:200]}") return await r.json()