Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Added

- More emojis in the messages that SpellBot sends.
- Send additional data to Convoke when creating games.

## [v17.10.2](https://github.com/lexicalunit/spellbot/releases/tag/v17.10.2) - 2026-02-04

Expand Down
16 changes: 9 additions & 7 deletions src/spellbot/integrations/convoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class ConvokeGameTypes(Enum):
Other = "other"


def convoke_game_format(format: GameFormat) -> ConvokeGameTypes: # pragma: no cover
def convoke_game_format(format: GameFormat) -> ConvokeGameTypes:
match format:
case (
GameFormat.COMMANDER
Expand All @@ -101,13 +101,13 @@ def convoke_game_format(format: GameFormat) -> ConvokeGameTypes: # pragma: no c
return ConvokeGameTypes.Other


def passphrase() -> str | None: # pragma: no cover
def passphrase() -> str | None:
if USE_PASSWORD:
return f"{random.choice(ADJECTIVES)} {random.choice(NOUNS)}" # noqa: S311
return None


async def fetch_convoke_link( # pragma: no cover
async def fetch_convoke_link(
client: httpx.AsyncClient,
game: GameDict,
key: str | None,
Expand All @@ -120,11 +120,14 @@ async def fetch_convoke_link( # pragma: no cover
"apiKey": settings.CONVOKE_API_KEY,
"isPublic": False,
"name": name,
"spellbotGameId": game["id"],
"seatLimit": game["seats"],
"format": format,
"discordGuild": str(game["guild_xid"]),
"discordChannel": str(game["channel_xid"]),
"discordPlayers": [{"id": str(p["xid"]), "name": p["name"]} for p in players],
"discordPlayers": [
{"id": str(p["xid"]), "name": p["name"], "pin": p["pin"]} for p in players
],
}
if game["bracket"] != GameBracket.NONE.value:
payload["bracketLevel"] = f"B{game['bracket'] - 1}"
Expand All @@ -137,9 +140,7 @@ async def fetch_convoke_link( # pragma: no cover
return resp.json()


async def generate_link(
game: GameDict,
) -> tuple[str | None, str | None]: # pragma: no cover
async def generate_link(game: GameDict) -> tuple[str | None, str | None]:
if not settings.CONVOKE_API_KEY:
return None, None

Expand All @@ -160,6 +161,7 @@ async def generate_link(
attempt + 1,
exc_info=True,
)
continue

if not data:
return None, None
Expand Down
3 changes: 2 additions & 1 deletion src/spellbot/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def import_models() -> None: # pragma: no cover
from .post import Post, PostDict # noqa: E402
from .queue import Queue, QueueDict # noqa: E402
from .token import Token, TokenDict # noqa: E402
from .user import User, UserDict # noqa: E402
from .user import PlayerDataDict, User, UserDict # noqa: E402
from .verify import Verify, VerifyDict # noqa: E402
from .watch import Watch, WatchDict # noqa: E402

Expand All @@ -51,6 +51,7 @@ def import_models() -> None: # pragma: no cover
"NotificationDict",
"Play",
"PlayDict",
"PlayerDataDict",
"Post",
"PostDict",
"Queue",
Expand Down
6 changes: 6 additions & 0 deletions src/spellbot/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ class UserDict(TypedDict):
banned: bool


class PlayerDataDict(TypedDict):
xid: int
name: str
pin: str | None


class User(Base):
"""Represents a Discord user."""

Expand Down
11 changes: 6 additions & 5 deletions src/spellbot/services/games.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@
GameDict,
GameStatus,
Play,
PlayerDataDict,
Post,
Queue,
QueueDict,
User,
UserAward,
UserDict,
Watch,
)
from spellbot.settings import settings
Expand Down Expand Up @@ -579,10 +579,11 @@ def select_last_game(self, user_xid: int, guild_xid: int) -> GameDict | None:

@sync_to_async()
@tracer.wrap()
def player_data(self, game_id: int) -> list[UserDict]:
def player_data(self, game_id: int) -> list[PlayerDataDict]:
game = DatabaseSession.query(Game).filter(Game.id == game_id).first()
if not game:
return []
player_xids = game.player_xids
players = DatabaseSession.query(User).filter(User.xid.in_(player_xids)).all()
return [p.to_dict() for p in players]

player_pins = game.player_pins
players = DatabaseSession.query(User).filter(User.xid.in_(game.player_xids)).all()
return [PlayerDataDict(xid=p.xid, name=p.name, pin=player_pins.get(p.xid)) for p in players]
Loading
Loading