Skip to content

Commit

Permalink
feat: add skill and stat leaderboard support
Browse files Browse the repository at this point in the history
  • Loading branch information
Jackenmen committed Oct 31, 2024
1 parent 4421d7b commit 40ed7ad
Show file tree
Hide file tree
Showing 6 changed files with 271 additions and 10 deletions.
15 changes: 15 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ All enumerations are subclasses of `enum.Enum`.
.. autoclass:: rlapi.Platform
:members:

.. autoclass:: rlapi.Stat
:members:

Rocket League API Models
------------------------

Expand All @@ -47,6 +50,18 @@ and are not meant to be created by the user of the library.
.. autoclass:: rlapi.Playlist
:members:

.. autoclass:: rlapi.SkillLeaderboard
:members:

.. autoclass:: rlapi.SkillLeaderboardPlayer
:members:

.. autoclass:: rlapi.StatLeaderboard
:members:

.. autoclass:: rlapi.StatLeaderboardPlayer
:members:

.. autoclass:: rlapi.tier_estimates.TierEstimates
:members:

Expand Down
18 changes: 17 additions & 1 deletion rlapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,24 @@

from . import errors as errors # noqa
from .client import Client as Client # noqa
from .enums import Platform as Platform, PlaylistKey as PlaylistKey # noqa
from .enums import ( # noqa
Platform as Platform,
PlaylistKey as PlaylistKey,
Stat as Stat,
)
from .errors import ( # noqa
HTTPException as HTTPException,
IllegalUsername as IllegalUsername,
PlayerNotFound as PlayerNotFound,
RLApiException as RLApiException,
Unauthorized as Unauthorized,
)
from .leaderboard import ( # noqa
SkillLeaderboard as SkillLeaderboard,
SkillLeaderboardPlayer as SkillLeaderboardPlayer,
StatLeaderboard as StatLeaderboard,
StatLeaderboardPlayer as StatLeaderboardPlayer,
)
from .player import ( # noqa
DIVISIONS as DIVISIONS,
PLAYLISTS_WITH_SEASON_REWARDS as PLAYLISTS_WITH_SEASON_REWARDS,
Expand Down Expand Up @@ -57,12 +67,18 @@
# enums
"Platform",
"PlaylistKey",
"Stat",
# errors
"HTTPException",
"IllegalUsername",
"PlayerNotFound",
"RLApiException",
"Unauthorized",
# leaderboard
"SkillLeaderboard",
"SkillLeaderboardPlayer",
"StatLeaderboard",
"StatLeaderboardPlayer",
# player
"DIVISIONS",
"PLAYLISTS_WITH_SEASON_REWARDS",
Expand Down
61 changes: 57 additions & 4 deletions rlapi/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@

from . import errors
from ._utils import TokenInfo, json_or_text
from .enums import Platform
from .enums import PlaylistKey, Platform, Stat
from .leaderboard import SkillLeaderboard, StatLeaderboard
from .player import Player
from .typedefs import TierBreakdownType

Expand Down Expand Up @@ -191,12 +192,10 @@ async def _rlapi_request(
*,
params: Optional[Dict[str, str]] = None,
force_refresh_token: bool = False,
) -> List[Dict[str, Any]]:
) -> Any:
url = self.RLAPI_BASE + endpoint
token = await self._get_access_token(force_refresh=force_refresh_token)
headers = {"Authorization": f"Bearer {token}"}
# RL API returns JSON object on success
data: List[Dict[str, Any]]
try:
data = await self._request(url, headers, params=params)
except errors.Unauthorized:
Expand Down Expand Up @@ -585,3 +584,57 @@ async def _find_steam_ids(self, match: Match[str]) -> List[str]:
)

return ids

async def get_skill_leaderboard(
self, platform: Platform, playlist_key: PlaylistKey
) -> SkillLeaderboard:
"""
Get skill leaderboard for the playlist on the given platform.
Parameters
----------
platform: Platform
Platform to get the leaderboard for.
playlist_key: PlaylistKey
Playlist to get the leaderboard for.
Returns
-------
SkillLeaderboard
Skill leaderboard for the playlist on the given platform.
Raises
------
HTTPException
HTTP request to Rocket League failed.
"""
endpoint = f"/leaderboard/skill/{platform.value}/{playlist_key.value}"
data = await self._rlapi_request(endpoint)
return SkillLeaderboard(platform, playlist_key, data)

async def get_stat_leaderboard(
self, platform: Platform, stat: Stat
) -> StatLeaderboard:
"""
Get leaderboard for the specified stat on the given platform.
Parameters
----------
platform: Platform
Platform to get the leaderboard for.
stat: Stat
Stat to get the leaderboard for.
Returns
-------
StatLeaderboard
Leaderboard for the specified stat on the given platform.
Raises
------
HTTPException
HTTP request to Rocket League failed.
"""
endpoint = f"/leaderboard/stat/{platform.value}/{stat.value}"
data = await self._rlapi_request(endpoint)
return StatLeaderboard(platform, stat, data)
17 changes: 17 additions & 0 deletions rlapi/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,23 @@ def __str__(self) -> str:
return _PLATFORM_FRIENDLY_NAMES[self]


class Stat(Enum):
"""Represents player stat."""

#: Assists.
assists = "Assists"
#: Goals.
goals = "Goals"
#: MVPs.
mvps = "MVPs"
#: Saves.
saves = "Saves"
#: Shots.
shots = "Shots"
#: Wins.
wins = "Wins"


_PLATFORM_FRIENDLY_NAMES = {
Platform.steam: "Steam",
Platform.ps4: "PlayStation 4",
Expand Down
157 changes: 157 additions & 0 deletions rlapi/leaderboard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
from typing import Any, Dict, Optional

from .enums import Platform, PlaylistKey, Stat

__all__ = (
"SkillLeaderboardPlayer",
"SkillLeaderboard",
"StatLeaderboardPlayer",
"StatLeaderboard",
)


class SkillLeaderboardPlayer:
"""SkillLeaderboardPlayer()
Represents Rocket League Player on a platform's skill leaderboard.
Attributes
----------
platform: Platform
Platform that this leaderboard entry refers to.
playlist_key: PlaylistKey
Playlist that this leaderboard entry refers to.
user_id: str, optional
Player's user ID.
Only present for Steam and Epic Games players.
user_name: str
Player's username (display name).
tier: int
Player's tier on the specified playlist.
skill: int
Player's skill rating on the specified playlist.
"""

__slots__ = ("platform", "playlist_key", "user_name", "user_id", "tier", "skill")

def __init__(
self,
platform: Platform,
playlist_key: PlaylistKey,
data: Dict[str, Any],
) -> None:
self.platform = platform
self.playlist_key = playlist_key
self.user_name: str = data["user_name"]
self.user_id: Optional[str] = data.get("user_id")
if (
self.user_id is not None
and self.user_id.startswith(f"{platform.value}|")
and self.user_id.endswith("|0")
):
self.user_id = self.user_id[len(platform.value) + 1 : -2]
self.tier: int = data["tier"]
self.skill: int = data["skill"]


class SkillLeaderboard:
"""SkillLeaderboard()
Represents Rocket League playlist's skill leaderboard for a single platform.
Attributes
----------
platform: Platform
Platform that this leaderboard refers to.
playlist_key: PlaylistKey
Playlist that this leaderboard refers to.
players: list of `StatLeaderboardPlayer`
List of playlist's top 100 players on the platform.
"""

__slots__ = ("platform", "playlist_key", "players")

def __init__(
self,
platform: Platform,
playlist_key: PlaylistKey,
data: Dict[str, Any],
) -> None:
self.platform = platform
self.playlist_key = playlist_key
self.players = [
SkillLeaderboardPlayer(platform, playlist_key, player_data)
for player_data in data["leaderboard"]
]


class StatLeaderboardPlayer:
"""StatLeaderboardPlayer()
Represents Rocket League Player on a platform's stat leaderboard.
Attributes
----------
platform: Platform
Platform that this leaderboard entry refers to.
stat: Stat
Stat that this leaderboard entry refers to.
user_id: str, optional
Player's user ID.
Only present for Steam and Epic Games players.
user_name: str
Player's username (display name).
value: int
Value of the specified stat for the player.
"""

__slots__ = ("platform", "stat", "user_name", "user_id", "value")

def __init__(
self,
platform: Platform,
stat: Stat,
data: Dict[str, Any],
) -> None:
self.platform = platform
self.stat = stat
self.user_name: str = data["user_name"]
self.user_id: Optional[str] = data.get("user_id")
if (
self.user_id is not None
and self.user_id.startswith(f"{platform.value}|")
and self.user_id.endswith("|0")
):
self.user_id = self.user_id[len(platform.value) + 1 : -2]
self.value: int = data[stat.value]


class StatLeaderboard:
"""StatLeaderboard()
Represents Rocket League stat leaderboard for a single platform.
Attributes
----------
platform: Platform
Platform that this leaderboard refers to.
stat: Stat
Stat that this leaderboard refers to.
players: list of `StatLeaderboardPlayer`
List of stat's top 100 players on the platform.
"""

__slots__ = ("platform", "stat", "players")

def __init__(
self,
platform: Platform,
stat: Stat,
data: Dict[str, Any],
) -> None:
self.platform = platform
self.stat = stat
self.players = [
StatLeaderboardPlayer(platform, stat, player_data)
for player_data in data[stat.value]
]
13 changes: 8 additions & 5 deletions rlapi/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import contextlib
from typing import Any, Dict, Final, List, Optional, Union

from .enums import Platform, PlaylistKey
from .enums import Platform, PlaylistKey, Stat
from .tier_estimates import TierEstimates
from .typedefs import PlaylistBreakdownType, TierBreakdownType

Expand Down Expand Up @@ -251,6 +251,11 @@ class PlayerStats:
"""PlayerStats()
Represents player stats (assists, goals, MVPs, etc.).
.. container:: operations
``x[key]``
Lookup player's stat value by `Stat` enum.
Attributes
----------
assists: int
Expand Down Expand Up @@ -285,10 +290,8 @@ def __init__(self, data: List[Dict[str, Any]]) -> None:
self.shots: int = stats.get("shots", 0)
self.wins: int = stats.get("wins", 0)

def __getitem__(self, key: str) -> int:
if key not in self.__slots__:
raise KeyError(key)
return int(getattr(self, key))
def __getitem__(self, stat: Stat) -> int:
return int(getattr(self, stat.name))

def __repr__(self) -> str:
attrs = " ".join(f"{key}={getattr(self, key)}" for key in self.__slots__)
Expand Down

0 comments on commit 40ed7ad

Please sign in to comment.