diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d2e19a2..f881468f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Added + +- Support for Convoke's "Precon" bracket level. + +### Changed + +- Updated dependencies. + ## [v18.3.2](https://github.com/lexicalunit/spellbot/releases/tag/v18.3.2) - 2026-04-02 ### Changed diff --git a/src/spellbot/integrations/convoke.py b/src/spellbot/integrations/convoke.py index a73a6fa8..29e1a3d4 100644 --- a/src/spellbot/integrations/convoke.py +++ b/src/spellbot/integrations/convoke.py @@ -132,6 +132,8 @@ async def fetch_convoke_link( payload["spellbotGamePins"] = pins if game["bracket"] != GameBracket.NONE.value: payload["bracketLevel"] = f"B{game['bracket'] - 1}" + if game["format"] == GameFormat.PRE_CONS.value: + payload["bracketLevel"] = "Precon" # Convoke uses Bracket to indicate "pre-cons" if key: payload["password"] = key headers = {"user-agent": f"spellbot/{__version__}"} diff --git a/tests/integrations/test_convoke.py b/tests/integrations/test_convoke.py index a523fbf1..54867e86 100644 --- a/tests/integrations/test_convoke.py +++ b/tests/integrations/test_convoke.py @@ -152,6 +152,41 @@ async def test_fetch_convoke_link_with_bracket(self) -> None: payload = mock_client.post.call_args.kwargs["json"] assert payload["bracketLevel"] == "B2" # BRACKET_2.value (3) -> B{3-1} = B2 + @pytest.mark.asyncio + async def test_fetch_convoke_link_with_precons(self) -> None: + mock_client = MagicMock(spec=httpx.AsyncClient) + mock_response = MagicMock() + mock_response.raise_for_status = MagicMock() + mock_response.json.return_value = {"url": "https://convoke.gg/game/456"} + mock_client.post = AsyncMock(return_value=mock_response) + + game = cast( + "GameDict", + { + "id": 99, + "format": GameFormat.PRE_CONS.value, + "seats": 4, + "guild_xid": 12345, + "channel_xid": 67890, + "bracket": GameBracket.NONE.value, + }, + ) + + with ( + patch.object( + convoke_module.services.games, + "player_data", + AsyncMock(return_value=[]), + ), + patch.object(convoke_module.settings, "CONVOKE_API_KEY", "test_api_key"), + patch.object(convoke_module.settings, "CONVOKE_ROOT", "https://api.convoke.gg"), + ): + result = await fetch_convoke_link(mock_client, game, None, pins=None) + + assert result == {"url": "https://convoke.gg/game/456"} + payload = mock_client.post.call_args.kwargs["json"] + assert payload["bracketLevel"] == "Precon" + @pytest.mark.asyncio async def test_fetch_convoke_link_with_password(self) -> None: mock_client = MagicMock(spec=httpx.AsyncClient)