Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce ext.dota2 - Dota 2 Game Coordinator extension #460

Draft
wants to merge 54 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
de7a99f
🪧Compile protobufs into `.py` mirrors
Aluerie Jan 23, 2024
e21b9ef
🐸Initial implementation for Dota2 GC
Aluerie Jan 23, 2024
513ea0c
📃Initial docs for ext.dota2
Aluerie Jan 23, 2024
60e4c39
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 23, 2024
89bdc97
✒️Stealth edit to `fetch_top_source_tv_games` docstring
Aluerie Jan 23, 2024
dd0a3b8
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 23, 2024
6355e21
Apply suggestions from code review by Gobot
Aluerie Jan 23, 2024
89b5b01
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 23, 2024
ca12e0e
Apply suggestions from code review
Aluerie Jan 23, 2024
728fa0e
⛔Remove Steam NonSense
Aluerie Jan 24, 2024
f96967f
🔨Fix bad enum namings
Aluerie Jan 24, 2024
494bd5c
🪓 Total Overhaul of the PR
Aluerie Jan 24, 2024
f8bd9b6
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 24, 2024
2e9383f
🖋️Small doc edit
Aluerie Jan 24, 2024
da096d4
🆘Apply suggestions from code review by Gobot
Aluerie Jan 25, 2024
ab90872
🩺 More feedback corrections
Aluerie Jan 25, 2024
c07dff9
🪿Remove `fmt: off` from betterproto Enums
Aluerie Jan 25, 2024
dd1b731
🤺Quotes ' -> "
Aluerie Jan 25, 2024
db78cd0
🪥 Few more brush ups
Aluerie Jan 25, 2024
8ec073f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 25, 2024
3c89f4a
🪥and more
Aluerie Jan 25, 2024
e353b49
📺LiveMatchPlayer should subclass Partial user
Aluerie Jan 25, 2024
49ac537
🦸‍♀️heroes property for LiveMatch
Aluerie Jan 25, 2024
9fd115f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 25, 2024
aa7433b
🤧fix import * abd `__all__`
Aluerie Jan 25, 2024
8561fcb
Merge branch 'introduce-ext.dota2' of https://github.com/Aluerie/stea…
Aluerie Jan 25, 2024
59bf6fd
🔣Doc String + Remove C prefix
Aluerie Jan 25, 2024
037355f
📈 Glicko Rating and Behavior Summary
Aluerie Jan 27, 2024
5a19a1d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 27, 2024
7e3ddb1
💥MatchDetails, MM stats, rating - very raw
Aluerie Feb 3, 2024
3b83928
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 3, 2024
b4bd089
Merge branch 'Gobot1234:main' into introduce-ext.dota2
Aluerie Sep 21, 2024
69d0c92
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 21, 2024
bd12ec8
Merge branch 'Gobot1234:main' into introduce-ext.dota2
Aluerie Sep 21, 2024
517e33f
🏅Implement RankTier Medals
Aluerie Sep 24, 2024
04fcdef
🔬Introduce Match Minimal
Aluerie Sep 29, 2024
5d50888
📂Divide `models.py` into a folder
Aluerie Sep 30, 2024
38a8f39
Create models.py
Aluerie Sep 30, 2024
702f883
🧓Match History
Aluerie Sep 30, 2024
f553a8d
📲Social Feed Post Message
Aluerie Sep 30, 2024
bfef6bd
🪥Some Brush-ups
Aluerie Sep 30, 2024
da9d459
〽️Instantiate Partial Match
Aluerie Sep 30, 2024
8d166c3
🖼️Profile Request
Aluerie Sep 30, 2024
37a8fa7
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 30, 2024
55f0486
Doc Edits
Aluerie Sep 30, 2024
87e9220
Merge branch 'introduce-ext.dota2' of https://github.com/Aluerie/stea…
Aluerie Sep 30, 2024
39f907f
🔮Refactor things back to proper state -> models
Aluerie Oct 2, 2024
047da1f
🔝Separate state for TopSource too
Aluerie Oct 2, 2024
6abe799
🦜Separate state for MM stats
Aluerie Oct 2, 2024
12c6df1
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 2, 2024
e47e624
🌐 `replay_url`, `metadata_url`
Aluerie Oct 2, 2024
4a54ad1
🙂Add Facets to protos
Aluerie Oct 3, 2024
f465544
🎶Fix Match History
Aluerie Oct 7, 2024
1a96c93
🆕Add Kez hero
Aluerie Nov 22, 2024
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
28 changes: 28 additions & 0 deletions docs/ext/dota2/api.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.. currentmodule:: steam.ext.dota2

API Reference
===============

The following section outlines the API of steam.py's Dota 2 extension module.


Client
------

.. attributetable:: Client

.. autoclass:: Client
:members:
:inherited-members:

Bot
----

``ext.dota2`` also provides a :class:`Bot` class, which is a subclass of :class:`Client` and :class:`steam.ext.commands.Bot`.

.. attributetable:: steam.ext.dota2.Bot

.. autoclass:: steam.ext.dota2.Bot
:members:
:inherited-members:

11 changes: 11 additions & 0 deletions docs/ext/dota2/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
(steam-ext-dota2)=

# `steam.ext.dota2` - A Dota 2 Game Coordinator client

This extension offers the ability to interact with the Dota 2 Game Coordinator (Dota 2 GC)

```{toctree}
:maxdepth: 2

api
```
2 changes: 2 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ steam API Reference <api>
steam.ext.commands API Reference <ext/commands/api.rst>
steam.ext.csgo API Reference <ext/csgo/api.rst>
steam.ext.tf2 API Reference <ext/tf2/api.rst>
steam.ext.dota2 API Reference <ext/dota2/api.rst>
```

## Extensions
Expand All @@ -41,4 +42,5 @@ steam.py has extension modules to help with common tasks.
ext/commands/index.rst
ext/csgo/index.rst
ext/tf2/index.rst
ext/dota2/index.rst
```
12 changes: 12 additions & 0 deletions steam/ext/dota2/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""
steam.ext.dota2
~~~~~~~~~~~~~~

A library for interacting with the Dota 2 Game Coordinator.

Licensed under The MIT License (MIT) - Copyright (c) 2020-present James H-B. See LICENSE
"""

from .client import *
from .enums import *
from .models import *
186 changes: 186 additions & 0 deletions steam/ext/dota2/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
"""Licensed under The MIT License (MIT) - Copyright (c) 2020-present James H-B. See LICENSE"""

from __future__ import annotations

from typing import TYPE_CHECKING, Final, overload

from ..._const import DOCS_BUILDING
from ..._gc import Client as Client_
from ...app import DOTA2
from ...ext import commands
from ...utils import (
MISSING,
cached_property,
)
from .models import ClientUser, LiveMatch, MatchMinimal, PartialMatch, PartialUser
from .state import GCState # noqa: TCH001

if TYPE_CHECKING:
from ...types.id import Intable
from .enums import Hero
from .models import User

__all__ = (
"Client",
"Bot",
)


class Client(Client_):
"""Represents a client connection that connects to Steam. This class is used to interact with the Steam API, CMs
and the Dota 2 Game Coordinator.

:class:`Client` is a subclass of :class:`steam.Client`, so whatever you can do with :class:`steam.Client` you can
do with :class:`Client`.
"""

_APP: Final = DOTA2
_ClientUserCls = ClientUser
_state: GCState # type: ignore # PEP 705

if TYPE_CHECKING:

@cached_property
def user(self) -> ClientUser: ...

# TODO: maybe this should exist as a part of the whole lib (?)
def instantiate_partial_user(self, id: Intable) -> PartialUser:
return self._state.get_partial_user(id)

def instantiate_partial_match(self, id: int) -> PartialMatch:
"""Instantiate partial match.

Convenience method, allows using match related requests to gc like `match.details`
for any match.
"""
return PartialMatch(self._state, id)

async def top_live_matches(self, *, hero: Hero = MISSING, limit: int = 100) -> list[LiveMatch]:
"""Fetch top live matches.

This is similar to game list in the Watch Tab of Dota 2 game app.
"Top matches" in this context means
* featured tournament matches
* highest average MMR matches

Parameters
----------
hero
Filter matches by Hero. Note, in this case Game Coordinator still only uses current top100 live matches,
i.e. requesting "filter by Muerta" results only in subset of those matches in which
Muerta is currently being played. It does not look into lower MMR match than top100 to extend the return
list to number of games from `limit` argument. This behavior is consistent with how Watch Tab works.
limit
Maximum amount of matches to fetch. This works rather as a boundary limit than "number of matches" to
fetch, i.e. Dota 2 will sometimes give 90 matches when `limit` is 100.
Or even "successfully" return 0 matches.

Returns
-------
List of currently live top matches.

Raises
------
ValueError
`limit` value should be between 1 and 100 inclusively.
asyncio.TimeoutError
Request time-outed. The reason is usually Dota 2 Game Coordinator lagging or being down.
"""
if limit < 1 or limit > 100:
raise ValueError("limit value should be between 1 and 100 inclusively.")

protos = await self._state.fetch_top_source_tv_games(
start_game=(limit - 1) // 10 * 10, # mini-math: limit 100 -> start_game 90, 91 -> 90, 90 -> 80
hero_id=hero.value if hero else 0,
)
live_matches = [LiveMatch(self._state, match) for proto in protos for match in proto.game_list]
# still need to slice the list, i.e. limit = 85, but live_matches above will have 90 matches
return live_matches[:limit]

async def tournament_live_matches(self, league_id: int) -> list[LiveMatch]:
"""Fetch currently live tournament matches

Parameters
----------
league_id
Tournament league_id

Returns
-------
List of currently live tournament matches.

Raises
------
asyncio.TimeoutError
Request time-outed. The reason is usually Dota 2 Game Coordinator lagging or being down.
"""

protos = await self._state.fetch_top_source_tv_games(league_id=league_id)
# TODO: ^ this will only fetch 10 games because of implementation...
# but there is no good way to know if there gonna be more than 10 games
# but does any tournament play more than 10 games at once? :x
return [LiveMatch(self._state, match) for proto in protos for match in proto.game_list]

@overload
async def live_matches(self, *, lobby_id: int = ...) -> LiveMatch: ...

@overload
async def live_matches(self, *, lobby_ids: list[int] = ...) -> list[LiveMatch]: ...

async def live_matches(self, *, lobby_id: int = MISSING, lobby_ids: list[int] = MISSING):
"""Fetch currently live matches by lobby_ids

Parameters
----------
lobby_ids
Lobby IDs

Returns
-------
List of live matches.

Raises
------
asyncio.TimeoutError
Request time-outed. The reason is usually Dota 2 Game Coordinator lagging or being down.
"""
if lobby_id is not MISSING and lobby_ids is not MISSING:
raise TypeError("Cannot mix lobby_id and lobby_ids keyword arguments.")

lobby_ids = [lobby_id] if lobby_id else lobby_ids

protos = await self._state.fetch_top_source_tv_games(
start_game=(len(lobby_ids) - 1) // 10 * 10,
lobby_ids=lobby_ids,
)
live_matches = [LiveMatch(self._state, match) for proto in protos for match in proto.game_list]
if lobby_id:
try:
return live_matches[0]
except IndexError:
# can happen even with valid lobby_id if it's private
raise RuntimeError(f"Failed to fetch match with {lobby_id=}")
else:
return live_matches

async def matches_minimal(self, match_id: int) -> list[MatchMinimal]:
proto = await self._state.fetch_matches_minimal(match_ids=[match_id])
return [MatchMinimal(self._state, match) for match in proto.matches]

async def matchmaking_stats(self):
proto = await self._state.fetch_matchmaking_stats()
return proto # TODO: Modelize (I never figured out what regions are which)

if TYPE_CHECKING or DOCS_BUILDING:

def get_user(self, id: Intable) -> User | None: ...

async def fetch_user(self, id: Intable) -> User: ...


class Bot(commands.Bot, Client):
"""Represents a Steam bot.

:class:`Bot` is a subclass of :class:`~steam.ext.commands.Bot`, so whatever you can do with
:class:`~steam.ext.commands.Bot` you can do with :class:`Bot`.
"""
Loading
Loading