From a2609c2d9f59ac57b327428e51dfbe07ebb8dc6f Mon Sep 17 00:00:00 2001 From: Virinas-code Date: Wed, 16 Aug 2023 03:29:58 +0200 Subject: [PATCH 1/8] feat(clients): Add bulk pairing client feat(types): Add common types and aliases feat(types): Add bulk pairing types --- berserk/clients/__init__.py | 4 ++ berserk/clients/bulk_pairings.py | 94 ++++++++++++++++++++++++++++++++ berserk/types/bulk_pairings.py | 35 ++++++++++++ berserk/types/core/aliases.py | 7 +++ berserk/types/core/common.py | 11 ++++ 5 files changed, 151 insertions(+) create mode 100644 berserk/clients/bulk_pairings.py create mode 100644 berserk/types/bulk_pairings.py create mode 100644 berserk/types/core/aliases.py create mode 100644 berserk/types/core/common.py diff --git a/berserk/clients/__init__.py b/berserk/clients/__init__.py index 4ecb3b8..32a18b7 100644 --- a/berserk/clients/__init__.py +++ b/berserk/clients/__init__.py @@ -21,6 +21,7 @@ from .tv import TV from .tablebase import Tablebase from .opening_explorer import OpeningExplorer +from .bulk_pairings import BulkPairings __all__ = [ "Client", @@ -41,6 +42,7 @@ "OAuth", "TV", "Tablebase", + "BulkPairings", ] @@ -64,6 +66,7 @@ class Client(BaseClient): - :class:`messaging ` - private message other players - :class:`tv ` - get information on tv channels and games - :class:`tablebase ` - lookup endgame tablebase + - :class:`bulk_pairings ` - manage bulk pairings :param session: request session, authenticated as needed :param base_url: base API URL to use (if other than the default) @@ -102,3 +105,4 @@ def __init__( self.tv = TV(session, base_url) self.tablebase = Tablebase(session, tablebase_url) self.opening_explorer = OpeningExplorer(session, explorer_url) + self.bulk_pairings: BulkPairings = BulkPairings(session, base_url) diff --git a/berserk/clients/bulk_pairings.py b/berserk/clients/bulk_pairings.py new file mode 100644 index 0000000..a5b8a16 --- /dev/null +++ b/berserk/clients/bulk_pairings.py @@ -0,0 +1,94 @@ +"""Bulk pairings client""" +from __future__ import annotations + +from typing import Any, cast, Dict, List, Optional + +from .base import BaseClient +from ..enums import Variant +from ..formats import JSON_LIST, JSON +from ..types.bulk_pairings import BulkPairingGame +from ..types.core.aliases import LichessID +from ..types.core.common import Result + + +class BulkPairings(BaseClient): + """Client for bluk pairing related endpoints.""" + + def view_upcoming(self) -> List[BulkPairingGame]: + """View upcoming bulk pairings. + + :return: List of upcoming bulk pairings. + :rtype: List[BulkPairingGame] + """ + path: str = "/api/bulk-pairing" + return cast(List[BulkPairingGame], self._r.get(path, fmt=JSON_LIST)) + + def create( + self, + players_tokens: List[str], + clock_limit: int, + clock_increment: int, + days: Optional[int] = None, + pair_at: Optional[int] = None, + start_clocks_at: Optional[int] = None, + rated: Optional[bool] = None, + variant: Optional[Variant] = None, + fen: Optional[str] = None, + message: Optional[str] = None, + rules: Optional[list[str]] = None, + ) -> BulkPairingGame: + """Create a bulk pairing. + + :param List[str] players_tokens: players OAuth tokens + :param int clock_limit: clock initial time + :param int clock_increment: clock increment + :param Optional[int] days: days per turn (correspondence) + :param Optional[int] pair_at: day at wich game will be created + :param Optional[int] start_clocks_at: day at wich clocks will start + :param Optional[bool] rated: rated or casual + :param Optional[Variant] variant: game variant + :param Optional[str] fen: starting fen + :param Optional[str] message: message sent to players + :param Optional[list[str]] rules: extra game rules + :return BulkPairingGame: the pairing created + """ + path: str = "/api/bulk-pairing" + payload: Dict[str, Any] = { + "players": ":".join(players_tokens), + "clock.limit": clock_limit, + "clock.increment": clock_increment, + "days": days, + "pairAt": pair_at, + "startClocksAt": start_clocks_at, + "rated": rated, + "variant": variant, + "fen": fen, + "message": message, + "rules": ",".join(rules) if rules else None, + } + return cast( + BulkPairingGame, + self._r.post( + path, + payload=payload, + fmt=JSON, + ), + ) + + def start_clocks(self, pairing_id: LichessID) -> Result: + """Manually start clocks. + + :param LichessID pairing_id: pairing to start clocks of + :return Result: operation result + """ + path: str = f"https://lichess.org/api/bulk-pairing/{pairing_id}/start-clocks" + return cast(Result, self._r.post(path, fmt=JSON)) + + def cancel(self, pairing_id: LichessID) -> Result: + """Cancel a bulk pairing. + + :param LichessID pairing_id: pairing to cancel + :return Result: operation result + """ + path: str = f"https://lichess.org/api/bulk-pairing/{pairing_id}" + return cast(Result, self._r.request("DELETE", path, fmt=JSON)) diff --git a/berserk/types/bulk_pairings.py b/berserk/types/bulk_pairings.py new file mode 100644 index 0000000..9128b04 --- /dev/null +++ b/berserk/types/bulk_pairings.py @@ -0,0 +1,35 @@ +"""Aliases for bulk pairings endpoints""" +from __future__ import annotations + +from typing import List +from typing_extensions import TypedDict + +from .core.aliases import LichessID, Username + + +class BulkPairingGame(TypedDict): + """A bulk pairing game""" + + id: LichessID + black: Username + white: Username + + +class BulkPairingClock(TypedDict): + """A bulk pairing clock""" + + increment: int + limit: int + + +class BulkPairing(TypedDict): + """Represents a bulk pairing.""" + + id: LichessID + games: List[BulkPairingGame] + clock: BulkPairingClock + pairAt: int + pairedAt: None + rated: bool + startClocksAt: int + scheduledAt: int diff --git a/berserk/types/core/aliases.py b/berserk/types/core/aliases.py new file mode 100644 index 0000000..204a284 --- /dev/null +++ b/berserk/types/core/aliases.py @@ -0,0 +1,7 @@ +"""Aliases for common types""" +from __future__ import annotations + +from typing_extensions import TypeAlias + +LichessID: TypeAlias = str +Username: TypeAlias = str diff --git a/berserk/types/core/common.py b/berserk/types/core/common.py new file mode 100644 index 0000000..83826e4 --- /dev/null +++ b/berserk/types/core/common.py @@ -0,0 +1,11 @@ +"""Common types""" +from __future__ import annotations + +from typing_extensions import TypedDict, NotRequired + + +class Result(TypedDict): + """Operation result""" + + ok: NotRequired[bool] + error: NotRequired[str] From 635a17c3f25340f9acd940a8d0e4874b74ec3136 Mon Sep 17 00:00:00 2001 From: Virinas-code Date: Wed, 16 Aug 2023 12:22:24 +0200 Subject: [PATCH 2/8] fix(types): Replace `LichessID` with `ID` --- berserk/clients/bulk_pairings.py | 6 +++--- berserk/types/bulk_pairings.py | 6 +++--- berserk/types/core/aliases.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/berserk/clients/bulk_pairings.py b/berserk/clients/bulk_pairings.py index a5b8a16..df2f09e 100644 --- a/berserk/clients/bulk_pairings.py +++ b/berserk/clients/bulk_pairings.py @@ -7,7 +7,7 @@ from ..enums import Variant from ..formats import JSON_LIST, JSON from ..types.bulk_pairings import BulkPairingGame -from ..types.core.aliases import LichessID +from ..types.core.aliases import ID from ..types.core.common import Result @@ -75,7 +75,7 @@ def create( ), ) - def start_clocks(self, pairing_id: LichessID) -> Result: + def start_clocks(self, pairing_id: ID) -> Result: """Manually start clocks. :param LichessID pairing_id: pairing to start clocks of @@ -84,7 +84,7 @@ def start_clocks(self, pairing_id: LichessID) -> Result: path: str = f"https://lichess.org/api/bulk-pairing/{pairing_id}/start-clocks" return cast(Result, self._r.post(path, fmt=JSON)) - def cancel(self, pairing_id: LichessID) -> Result: + def cancel(self, pairing_id: ID) -> Result: """Cancel a bulk pairing. :param LichessID pairing_id: pairing to cancel diff --git a/berserk/types/bulk_pairings.py b/berserk/types/bulk_pairings.py index 9128b04..cc67c62 100644 --- a/berserk/types/bulk_pairings.py +++ b/berserk/types/bulk_pairings.py @@ -4,13 +4,13 @@ from typing import List from typing_extensions import TypedDict -from .core.aliases import LichessID, Username +from .core.aliases import ID, Username class BulkPairingGame(TypedDict): """A bulk pairing game""" - id: LichessID + id: ID black: Username white: Username @@ -25,7 +25,7 @@ class BulkPairingClock(TypedDict): class BulkPairing(TypedDict): """Represents a bulk pairing.""" - id: LichessID + id: ID games: List[BulkPairingGame] clock: BulkPairingClock pairAt: int diff --git a/berserk/types/core/aliases.py b/berserk/types/core/aliases.py index 204a284..56383ce 100644 --- a/berserk/types/core/aliases.py +++ b/berserk/types/core/aliases.py @@ -3,5 +3,5 @@ from typing_extensions import TypeAlias -LichessID: TypeAlias = str +ID: TypeAlias = str Username: TypeAlias = str From a52c3f2bba4eb1c37af41048d157048e5538ef44 Mon Sep 17 00:00:00 2001 From: Virinas-code Date: Wed, 16 Aug 2023 12:27:56 +0200 Subject: [PATCH 3/8] fix(types): Remove `Result` and return nothing instead --- berserk/clients/bulk_pairings.py | 11 ++++------- berserk/types/core/common.py | 11 ----------- 2 files changed, 4 insertions(+), 18 deletions(-) delete mode 100644 berserk/types/core/common.py diff --git a/berserk/clients/bulk_pairings.py b/berserk/clients/bulk_pairings.py index df2f09e..bcf0284 100644 --- a/berserk/clients/bulk_pairings.py +++ b/berserk/clients/bulk_pairings.py @@ -8,7 +8,6 @@ from ..formats import JSON_LIST, JSON from ..types.bulk_pairings import BulkPairingGame from ..types.core.aliases import ID -from ..types.core.common import Result class BulkPairings(BaseClient): @@ -75,20 +74,18 @@ def create( ), ) - def start_clocks(self, pairing_id: ID) -> Result: + def start_clocks(self, pairing_id: ID) -> None: """Manually start clocks. :param LichessID pairing_id: pairing to start clocks of - :return Result: operation result """ path: str = f"https://lichess.org/api/bulk-pairing/{pairing_id}/start-clocks" - return cast(Result, self._r.post(path, fmt=JSON)) + self._r.post(path) - def cancel(self, pairing_id: ID) -> Result: + def cancel(self, pairing_id: ID) -> None: """Cancel a bulk pairing. :param LichessID pairing_id: pairing to cancel - :return Result: operation result """ path: str = f"https://lichess.org/api/bulk-pairing/{pairing_id}" - return cast(Result, self._r.request("DELETE", path, fmt=JSON)) + self._r.request("DELETE", path) diff --git a/berserk/types/core/common.py b/berserk/types/core/common.py deleted file mode 100644 index 83826e4..0000000 --- a/berserk/types/core/common.py +++ /dev/null @@ -1,11 +0,0 @@ -"""Common types""" -from __future__ import annotations - -from typing_extensions import TypedDict, NotRequired - - -class Result(TypedDict): - """Operation result""" - - ok: NotRequired[bool] - error: NotRequired[str] From 91dc4eeb8f3b24b8e04808a161a35abb7930789b Mon Sep 17 00:00:00 2001 From: Virinas-code <61283052+Virinas-code@users.noreply.github.com> Date: Mon, 21 Aug 2023 02:42:52 +0200 Subject: [PATCH 4/8] fix(docs): Add new endpoints to documentation --- README.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.rst b/README.rst index f0588c1..ab96159 100644 --- a/README.rst +++ b/README.rst @@ -106,6 +106,11 @@ Most of the API is available: client.broadcasts.get_round_pgns client.broadcasts.get_pgns + client.bulk_pairings.view_upcoming + client.bulk_pairings.create + client.bulk_pairings.start_clocks + client.bulk_pairings.cancel + client.challenges.create client.challenges.create_ai client.challenges.create_open From 9d1cd6c07a31b433b9445cb56ed69b7350e7a3f7 Mon Sep 17 00:00:00 2001 From: Virinas-code Date: Mon, 21 Aug 2023 23:46:53 +0200 Subject: [PATCH 5/8] fix(clients): Rework `account` client with typing --- CHANGELOG.rst | 4 +- berserk/clients/account.py | 23 ++++++-- berserk/enums.py | 37 ++++++++++++ berserk/types/account.py | 117 +++++++++++++++++++++++++++++++++++++ 4 files changed, 174 insertions(+), 7 deletions(-) create mode 100644 berserk/types/account.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0becf43..0662702 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,8 +5,10 @@ v0.12.8 (2023-07-25) -------------------- * Added ``client.opening_explorer.get_lichess_games`` to request opening explorer data for a given position based on Lichess games +* Added ``client.bulk_pairings`` to create and manage bulk pairings +* Reworked ``client.account`` with better typing -Thanks to @rpesche for their contributions to this release. +Thanks to @rpesche and @Virinas-code for their contributions to this release. v0.12.7 (2023-07-15) -------------------- diff --git a/berserk/clients/account.py b/berserk/clients/account.py index 789c9a4..6da0640 100644 --- a/berserk/clients/account.py +++ b/berserk/clients/account.py @@ -1,21 +1,26 @@ +"""Account client.""" from __future__ import annotations -from typing import Any, Dict +from deprecated.sphinx import deprecated, versionchanged +from typing_extensions import cast from .. import models +from ..types.account import AccountInformation, Preferences from .base import BaseClient class Account(BaseClient): """Client for account-related endpoints.""" - def get(self) -> Dict[str, Any]: + def get(self) -> AccountInformation: """Get your public information. :return: public information about the authenticated user """ path = "/api/account" - return self._r.get(path, converter=models.Account.convert) + return cast( + AccountInformation, self._r.get(path, converter=models.Account.convert) + ) def get_email(self) -> str: """Get your email address. @@ -25,13 +30,18 @@ def get_email(self) -> str: path = "/api/account/email" return self._r.get(path)["email"] - def get_preferences(self) -> Dict[str, Any]: + @versionchanged( + ":py:meth:`berserk.clients.Account.get_preferences` now returns " + "all the preferences including language", + version="0.12.8", + ) + def get_preferences(self) -> Preferences: """Get your account preferences. :return: preferences of the authenticated user """ path = "/api/account/preferences" - return self._r.get(path)["prefs"] + return cast(Preferences, self._r.get(path)) def get_kid_mode(self) -> bool: """Get your kid mode status. @@ -41,7 +51,7 @@ def get_kid_mode(self) -> bool: path = "/api/account/kid" return self._r.get(path)["kid"] - def set_kid_mode(self, value: bool): + def set_kid_mode(self, value: bool) -> None: """Set your kid mode status. :param bool value: whether to enable or disable kid mode @@ -50,6 +60,7 @@ def set_kid_mode(self, value: bool): params = {"v": value} self._r.post(path, params=params) + @deprecated("Use endpoint from the Bot client instead", version="0.12.8") def upgrade_to_bot(self): """Upgrade your account to a bot account. diff --git a/berserk/enums.py b/berserk/enums.py index 46b3179..1dfdc57 100644 --- a/berserk/enums.py +++ b/berserk/enums.py @@ -1,3 +1,6 @@ +"""Common enumerations""" +from typing_extensions import Final + __all__ = ["PerfType", "Variant", "Color", "Room", "Mode", "Position", "Reason"] @@ -20,6 +23,15 @@ class PerfType(GameType): ULTRA_BULLET = "ultraBullet" +class AllPerfType(PerfType): + """Perfs including puzzles, etc""" + + STORM: str = "storm" + RACER: str = "racer" + STREAK: str = "streak" + PUZZLE: str = "puzzle" + + class Variant(GameType): STANDARD = "standard" @@ -53,6 +65,31 @@ class Reason: ONLYBOT = "onlyBot" +class CountTypes: + """Types of games.""" + + ALL: Final[str] = "all" + RATED: Final[str] = "rated" + AI: Final[str] = "ai" + DRAW: Final[str] = "draw" + DRAW_H: Final[str] = "drawH" + LOSS: Final[str] = "loss" + LOSS_H: Final[str] = "lossH" + WIN: Final[str] = "win" + WIN_H: Final[str] = "winH" + BOOKMARK: Final[str] = "bookmark" + PLAYING: Final[str] = "playing" + IMPORT: Final[str] = "import" + ME: Final[str] = "me" + + +class StreamingService: + """Streaming services.""" + + YOUTUBE: Final[str] = "youTube" + TWITCH: Final[str] = "twitch" + + # fmt: off class Position: ALEKHINES_DEFENCE = 'rnbqkb1r/pppppppp/5n2/8/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 2 2' # noqa: E501 diff --git a/berserk/types/account.py b/berserk/types/account.py new file mode 100644 index 0000000..64bb576 --- /dev/null +++ b/berserk/types/account.py @@ -0,0 +1,117 @@ +"""Aliases for account endpoints.""" +from __future__ import annotations + +from datetime import datetime + +from typing_extensions import TypedDict + +from ..enums import AllPerfType, CountTypes, StreamingService + + +class Perf(TypedDict): + """A perf.""" + + games: int + rating: int + rd: int + prog: int + prov: bool + + +class PlayTime(TypedDict): + """Account play time.""" + + total: int + tv: int + + +class Profile(TypedDict): + """Public profile of an account.""" + + country: str + location: str + bio: str + firstName: str + lastName: str + fideRating: int + uscfRating: int + ecfRating: int + links: str + + +class StreamerInfo(TypedDict): + """Information about the streamer on a given platform.""" + + channel: str + + +class AccountInformation(TypedDict): + """Informations about an account.""" + + id: str + username: str + perfs: dict[AllPerfType, Perf] + createdAt: datetime + disabled: bool + tosViolation: bool + profile: Profile + seenAt: datetime + patron: bool + verified: bool + title: str + url: str + playing: str + count: dict[CountTypes, int] + streaming: bool + streamer: dict[StreamingService, StreamingService] + followable: bool + following: bool + blocking: bool + followsYou: bool + + +class Prefs(TypedDict): + """User settings.""" + + dark: bool + transp: bool + bgImg: str + is3d: bool + theme: str + pieceSet: str + theme3d: str + pieceSet3d: str + soundSet: str + blindfold: int + autoQueen: int + autoThreefold: int + takeback: int + moretime: int + clockTenths: int + clockBar: bool + clockSound: bool + premove: bool + animation: int + captured: bool + follow: bool + highlight: bool + destination: bool + coords: int + replay: int + challenge: int + message: int + coordColor: int + submitMove: int + confirmResign: int + insightShare: int + keyboardMove: int + zen: int + moveEvent: int + rookCastle: int + + +class Preferences(TypedDict): + """Preferences of an account.""" + + prefs: Prefs + language: str From e2315d44a6f3419a1f0f9825af5d0bb4cb8cab47 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Sat, 2 Sep 2023 15:16:09 +0200 Subject: [PATCH 6/8] Cleanup --- CHANGELOG.rst | 16 ++++- README.rst | 2 +- berserk/clients/__init__.py | 2 +- berserk/clients/account.py | 8 --- berserk/clients/bulk_pairings.py | 108 +++++++++++++++++-------------- berserk/enums.py | 37 ----------- berserk/types/__init__.py | 25 +++++-- berserk/types/account.py | 33 ++-------- berserk/types/bulk_pairings.py | 30 +++------ berserk/types/common.py | 30 +++++++++ berserk/types/core/aliases.py | 7 -- 11 files changed, 137 insertions(+), 161 deletions(-) create mode 100644 berserk/types/common.py delete mode 100644 berserk/types/core/aliases.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0662702..96aa9f7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,14 +1,24 @@ Changelog ========= +Next release +------------ + +* Added ``client.bulk_pairings`` to create and manage bulk pairings + * ``client.bulk_pairings.get_upcoming`` to get upcoming bulk pairings you created + * ``client.bulk_pairings.create`` to create a bulk pairing + * ``client.bulk_pairings.start_clocks`` to start the clocks of a bulk pairing + * ``client.bulk_pairings.cancel`` to cancel a bulk pairing +* Added better return types for ``client.account.get`` and ``client.account.get_preferences`` + +Thanks to @Virinas-code for their contributions to this release. + v0.12.8 (2023-07-25) -------------------- * Added ``client.opening_explorer.get_lichess_games`` to request opening explorer data for a given position based on Lichess games -* Added ``client.bulk_pairings`` to create and manage bulk pairings -* Reworked ``client.account`` with better typing -Thanks to @rpesche and @Virinas-code for their contributions to this release. +Thanks to @rpesche for their contributions to this release. v0.12.7 (2023-07-15) -------------------- diff --git a/README.rst b/README.rst index ab96159..4ec3923 100644 --- a/README.rst +++ b/README.rst @@ -106,7 +106,7 @@ Most of the API is available: client.broadcasts.get_round_pgns client.broadcasts.get_pgns - client.bulk_pairings.view_upcoming + client.bulk_pairings.get_upcoming client.bulk_pairings.create client.bulk_pairings.start_clocks client.bulk_pairings.cancel diff --git a/berserk/clients/__init__.py b/berserk/clients/__init__.py index 32a18b7..22df53b 100644 --- a/berserk/clients/__init__.py +++ b/berserk/clients/__init__.py @@ -105,4 +105,4 @@ def __init__( self.tv = TV(session, base_url) self.tablebase = Tablebase(session, tablebase_url) self.opening_explorer = OpeningExplorer(session, explorer_url) - self.bulk_pairings: BulkPairings = BulkPairings(session, base_url) + self.bulk_pairings = BulkPairings(session, base_url) diff --git a/berserk/clients/account.py b/berserk/clients/account.py index 6da0640..48d9788 100644 --- a/berserk/clients/account.py +++ b/berserk/clients/account.py @@ -1,7 +1,5 @@ -"""Account client.""" from __future__ import annotations -from deprecated.sphinx import deprecated, versionchanged from typing_extensions import cast from .. import models @@ -30,11 +28,6 @@ def get_email(self) -> str: path = "/api/account/email" return self._r.get(path)["email"] - @versionchanged( - ":py:meth:`berserk.clients.Account.get_preferences` now returns " - "all the preferences including language", - version="0.12.8", - ) def get_preferences(self) -> Preferences: """Get your account preferences. @@ -60,7 +53,6 @@ def set_kid_mode(self, value: bool) -> None: params = {"v": value} self._r.post(path, params=params) - @deprecated("Use endpoint from the Bot client instead", version="0.12.8") def upgrade_to_bot(self): """Upgrade your account to a bot account. diff --git a/berserk/clients/bulk_pairings.py b/berserk/clients/bulk_pairings.py index bcf0284..62931ca 100644 --- a/berserk/clients/bulk_pairings.py +++ b/berserk/clients/bulk_pairings.py @@ -1,59 +1,62 @@ -"""Bulk pairings client""" from __future__ import annotations -from typing import Any, cast, Dict, List, Optional +from typing import cast -from .base import BaseClient from ..enums import Variant -from ..formats import JSON_LIST, JSON -from ..types.bulk_pairings import BulkPairingGame -from ..types.core.aliases import ID +from ..formats import JSON, JSON_LIST +from ..types.bulk_pairings import BulkPairing +from .base import BaseClient class BulkPairings(BaseClient): - """Client for bluk pairing related endpoints.""" + """Client for bulk pairing related endpoints.""" + + def get_upcoming(self) -> list[BulkPairing]: + """Get a list of upcoming bulk pairings you created. + + Only bulk pairings that are scheduled in the future, or that have a clock start scheduled in the future, are listed. - def view_upcoming(self) -> List[BulkPairingGame]: - """View upcoming bulk pairings. + Bulk pairings are deleted from the server after the pairings are done and the clocks have started. - :return: List of upcoming bulk pairings. - :rtype: List[BulkPairingGame] + :return: list of your upcoming bulk pairings. """ - path: str = "/api/bulk-pairing" - return cast(List[BulkPairingGame], self._r.get(path, fmt=JSON_LIST)) + path = "/api/bulk-pairing" + return cast(list[BulkPairing], self._r.get(path, fmt=JSON_LIST)) def create( self, - players_tokens: List[str], - clock_limit: int, - clock_increment: int, - days: Optional[int] = None, - pair_at: Optional[int] = None, - start_clocks_at: Optional[int] = None, - rated: Optional[bool] = None, - variant: Optional[Variant] = None, - fen: Optional[str] = None, - message: Optional[str] = None, - rules: Optional[list[str]] = None, - ) -> BulkPairingGame: + token_pairings: list[tuple[str, str]], + clock_limit: int | None = None, + clock_increment: int | None = None, + days: int | None = None, + pair_at: int | None = None, + start_clocks_at: int | None = None, + rated: bool = False, + variant: Variant | None = None, + fen: str | None = None, + message: str | None = None, + rules: list[str] | None = None, + ) -> BulkPairing: """Create a bulk pairing. - :param List[str] players_tokens: players OAuth tokens - :param int clock_limit: clock initial time - :param int clock_increment: clock increment - :param Optional[int] days: days per turn (correspondence) - :param Optional[int] pair_at: day at wich game will be created - :param Optional[int] start_clocks_at: day at wich clocks will start - :param Optional[bool] rated: rated or casual - :param Optional[Variant] variant: game variant - :param Optional[str] fen: starting fen - :param Optional[str] message: message sent to players - :param Optional[list[str]] rules: extra game rules - :return BulkPairingGame: the pairing created + :param players_tokens: players OAuth tokens + :param clock_limit: clock initial time + :param clock_increment: clock increment + :param days: days per turn (for correspondence) + :param pair_at: date at which the games will be created as a milliseconds unix timestamp. Up to 7 days in the future. Defaults to now. + :param start_clocks_at: date at which the clocks will be automatically started as a Unix timestamp in milliseconds. + Up to 7 days in the future. Note that the clocks can start earlier than specified, if players start making moves in the game. + If omitted, the clocks will not start automatically. + :param rated: set to true to make the games rated. defaults to false. + :param variant: variant of the games + :param fen: custom initial position (in FEN). Only allowed if variant is standard, fromPosition, or chess960 (if a valid 960 starting position), and the games cannot be rated. + :param message: message sent to players when their game is created + :param rules: extra game rules + :return: the newly created bulk pairing """ - path: str = "/api/bulk-pairing" - payload: Dict[str, Any] = { - "players": ":".join(players_tokens), + path = "/api/bulk-pairing" + payload = { + "players": ",".join(":".join(pair) for pair in token_pairings), "clock.limit": clock_limit, "clock.increment": clock_increment, "days": days, @@ -66,7 +69,7 @@ def create( "rules": ",".join(rules) if rules else None, } return cast( - BulkPairingGame, + BulkPairing, self._r.post( path, payload=payload, @@ -74,18 +77,25 @@ def create( ), ) - def start_clocks(self, pairing_id: ID) -> None: - """Manually start clocks. + def start_clocks(self, bulk_pairing_id: str) -> None: + """Immediately start all clocks of the games of the given bulk pairing. - :param LichessID pairing_id: pairing to start clocks of + This overrides the startClocksAt value of an existing bulk pairing. + + If the games have not yet been created (pairAt is in the future) or the clocks + have already started (startClocksAt is in the past), then this does nothing. + + :param bulk_pairing_id: id of the bulk pairing to start clocks of """ - path: str = f"https://lichess.org/api/bulk-pairing/{pairing_id}/start-clocks" + path = f"/api/bulk-pairing/{bulk_pairing_id}/start-clocks" self._r.post(path) - def cancel(self, pairing_id: ID) -> None: - """Cancel a bulk pairing. + def cancel(self, bulk_pairing_id: str) -> None: + """Cancel and delete a bulk pairing that is scheduled in the future. + + If the games have already been created, then this does nothing. - :param LichessID pairing_id: pairing to cancel + :param bulk_pairing_id: id of the bulk pairing to cancel """ - path: str = f"https://lichess.org/api/bulk-pairing/{pairing_id}" + path = f"/api/bulk-pairing/{bulk_pairing_id}" self._r.request("DELETE", path) diff --git a/berserk/enums.py b/berserk/enums.py index 1dfdc57..46b3179 100644 --- a/berserk/enums.py +++ b/berserk/enums.py @@ -1,6 +1,3 @@ -"""Common enumerations""" -from typing_extensions import Final - __all__ = ["PerfType", "Variant", "Color", "Room", "Mode", "Position", "Reason"] @@ -23,15 +20,6 @@ class PerfType(GameType): ULTRA_BULLET = "ultraBullet" -class AllPerfType(PerfType): - """Perfs including puzzles, etc""" - - STORM: str = "storm" - RACER: str = "racer" - STREAK: str = "streak" - PUZZLE: str = "puzzle" - - class Variant(GameType): STANDARD = "standard" @@ -65,31 +53,6 @@ class Reason: ONLYBOT = "onlyBot" -class CountTypes: - """Types of games.""" - - ALL: Final[str] = "all" - RATED: Final[str] = "rated" - AI: Final[str] = "ai" - DRAW: Final[str] = "draw" - DRAW_H: Final[str] = "drawH" - LOSS: Final[str] = "loss" - LOSS_H: Final[str] = "lossH" - WIN: Final[str] = "win" - WIN_H: Final[str] = "winH" - BOOKMARK: Final[str] = "bookmark" - PLAYING: Final[str] = "playing" - IMPORT: Final[str] = "import" - ME: Final[str] = "me" - - -class StreamingService: - """Streaming services.""" - - YOUTUBE: Final[str] = "youTube" - TWITCH: Final[str] = "twitch" - - # fmt: off class Position: ALEKHINES_DEFENCE = 'rnbqkb1r/pppppppp/5n2/8/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 2 2' # noqa: E501 diff --git a/berserk/types/__init__.py b/berserk/types/__init__.py index 746064e..79047f0 100644 --- a/berserk/types/__init__.py +++ b/berserk/types/__init__.py @@ -1,18 +1,29 @@ from __future__ import annotations -from .team import Team, PaginatedTeams +from .account import AccountInformation, Perf, Preferences, Profile, StreamerInfo +from .bulk_pairings import BulkPairing, BulkPairingGame +from .common import ClockConfig from .opening_explorer import ( - OpeningStatistic, + OpeningExplorerRating, OpeningExplorerVariant, + OpeningStatistic, Speed, - OpeningExplorerRating, ) +from .team import PaginatedTeams, Team __all__ = [ - "Team", - "PaginatedTeams", - "OpeningStatistic", + "AccountInformation", + "BulkPairing", + "BulkPairingGame", + "ClockConfig", + "OpeningExplorerRating", "OpeningExplorerVariant", + "OpeningStatistic", + "PaginatedTeams", + "Perf", + "Preferences", + "Profile", "Speed", - "OpeningExplorerRating", + "StreamerInfo", + "Team", ] diff --git a/berserk/types/account.py b/berserk/types/account.py index 64bb576..d6da3ee 100644 --- a/berserk/types/account.py +++ b/berserk/types/account.py @@ -1,16 +1,11 @@ -"""Aliases for account endpoints.""" from __future__ import annotations from datetime import datetime from typing_extensions import TypedDict -from ..enums import AllPerfType, CountTypes, StreamingService - class Perf(TypedDict): - """A perf.""" - games: int rating: int rd: int @@ -18,13 +13,6 @@ class Perf(TypedDict): prov: bool -class PlayTime(TypedDict): - """Account play time.""" - - total: int - tv: int - - class Profile(TypedDict): """Public profile of an account.""" @@ -40,17 +28,17 @@ class Profile(TypedDict): class StreamerInfo(TypedDict): - """Information about the streamer on a given platform.""" + """Information about the streamer on a specific platform.""" channel: str class AccountInformation(TypedDict): - """Informations about an account.""" + """Information about an account.""" id: str username: str - perfs: dict[AllPerfType, Perf] + perfs: dict[str, Perf] createdAt: datetime disabled: bool tosViolation: bool @@ -61,18 +49,16 @@ class AccountInformation(TypedDict): title: str url: str playing: str - count: dict[CountTypes, int] + count: dict[str, int] streaming: bool - streamer: dict[StreamingService, StreamingService] + streamer: dict[str, StreamerInfo] followable: bool following: bool blocking: bool followsYou: bool -class Prefs(TypedDict): - """User settings.""" - +class Preferences(TypedDict, total=False): dark: bool transp: bool bgImg: str @@ -108,10 +94,3 @@ class Prefs(TypedDict): zen: int moveEvent: int rookCastle: int - - -class Preferences(TypedDict): - """Preferences of an account.""" - - prefs: Prefs - language: str diff --git a/berserk/types/bulk_pairings.py b/berserk/types/bulk_pairings.py index cc67c62..ad71cf0 100644 --- a/berserk/types/bulk_pairings.py +++ b/berserk/types/bulk_pairings.py @@ -1,35 +1,23 @@ -"""Aliases for bulk pairings endpoints""" from __future__ import annotations -from typing import List from typing_extensions import TypedDict -from .core.aliases import ID, Username +from .common import ClockConfig, Variant class BulkPairingGame(TypedDict): - """A bulk pairing game""" - - id: ID - black: Username - white: Username - - -class BulkPairingClock(TypedDict): - """A bulk pairing clock""" - - increment: int - limit: int + id: str + black: str + white: str class BulkPairing(TypedDict): - """Represents a bulk pairing.""" - - id: ID - games: List[BulkPairingGame] - clock: BulkPairingClock + id: str + games: list[BulkPairingGame] + variant: Variant + clock: ClockConfig pairAt: int - pairedAt: None + pairedAt: int | None rated: bool startClocksAt: int scheduledAt: int diff --git a/berserk/types/common.py b/berserk/types/common.py new file mode 100644 index 0000000..5ad7093 --- /dev/null +++ b/berserk/types/common.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from typing import Literal, TypeAlias + +from typing_extensions import TypedDict + + +class ClockConfig(TypedDict): + # starting time in seconds + limit: int + # increment in seconds + increment: int + + +Variant: TypeAlias = Literal[ + "standard", + "chess960", + "kingOfTheHill", + "threeCheck", + "antichess", + "atomic", + "horde", + "racingKings", + "crazyhouse", + "fromPosition", +] + +GameRule: TypeAlias = Literal[ + "noAbort", "noRematch", "noGiveTime", "noClaimWin", "noEarlyDraw" +] diff --git a/berserk/types/core/aliases.py b/berserk/types/core/aliases.py deleted file mode 100644 index 56383ce..0000000 --- a/berserk/types/core/aliases.py +++ /dev/null @@ -1,7 +0,0 @@ -"""Aliases for common types""" -from __future__ import annotations - -from typing_extensions import TypeAlias - -ID: TypeAlias = str -Username: TypeAlias = str From 7fdc029d95b1b4eb2175cd13ee58dad04a90c990 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Sat, 2 Sep 2023 15:26:19 +0200 Subject: [PATCH 7/8] Fix type errors --- berserk/clients/account.py | 2 +- berserk/types/common.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/berserk/clients/account.py b/berserk/clients/account.py index 48d9788..434785b 100644 --- a/berserk/clients/account.py +++ b/berserk/clients/account.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing_extensions import cast +from typing import cast from .. import models from ..types.account import AccountInformation, Preferences diff --git a/berserk/types/common.py b/berserk/types/common.py index 5ad7093..c0709d5 100644 --- a/berserk/types/common.py +++ b/berserk/types/common.py @@ -1,8 +1,8 @@ from __future__ import annotations -from typing import Literal, TypeAlias +from typing import Literal -from typing_extensions import TypedDict +from typing_extensions import TypedDict, TypeAlias class ClockConfig(TypedDict): From 76950e56c8aa35156e2e64a948c43bdfa8a5fe45 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Sat, 2 Sep 2023 15:30:23 +0200 Subject: [PATCH 8/8] Fix cast for Python 3.8 --- berserk/clients/bulk_pairings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/berserk/clients/bulk_pairings.py b/berserk/clients/bulk_pairings.py index 62931ca..4d36d1a 100644 --- a/berserk/clients/bulk_pairings.py +++ b/berserk/clients/bulk_pairings.py @@ -21,7 +21,7 @@ def get_upcoming(self) -> list[BulkPairing]: :return: list of your upcoming bulk pairings. """ path = "/api/bulk-pairing" - return cast(list[BulkPairing], self._r.get(path, fmt=JSON_LIST)) + return cast("list[BulkPairing]", self._r.get(path, fmt=JSON_LIST)) def create( self,