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 13 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
```
10 changes: 10 additions & 0 deletions steam/ext/dota2/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"""
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 *
176 changes: 176 additions & 0 deletions steam/ext/dota2/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
"""Licensed under The MIT License (MIT) - Copyright (c) 2020-present James H-B. See LICENSE"""

from __future__ import annotations

import asyncio
from functools import partial
from typing import Final

from ..._const import timeout
from ..._gc import Client as Client_
from ...app import DOTA2
from ...ext import commands
from ...utils import MISSING
from .enums import Hero
from .models import LiveMatch
from .protobufs.watch import (
CMsgClientToGCFindTopSourceTVGames,
CMsgGCToClientFindTopSourceTVGamesResponse,
)
from .state import GCState # noqa: TCH001

__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
_state: GCState # type: ignore # PEP 705

async def top_live_matches(
self,
hero: Hero = MISSING,
max_matches: int = 100,
Aluerie marked this conversation as resolved.
Show resolved Hide resolved
) -> 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

* tournament matches
* highest average MMR matches

Parameters
----------
hero_id
Aluerie marked this conversation as resolved.
Show resolved Hide resolved
Filter matches by Hero.
Note, in this case Game Coordinator will still use only current top100 live matches, i.e. requesting
"filter by Muerta" will return only subset of those matches in which Muerta is currently being played.
It will not look into lower MMR games than top100.
max_matches
Aluerie marked this conversation as resolved.
Show resolved Hide resolved
Maximum amount of matches to be fetched.

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

Raises
------
ValueError
`max_games` 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 max_matches < 1 or max_matches > 100:
raise ValueError("max_games value should be between 1 and 100.")

# mini-math: max_matches 100 -> start_game 90, 91 -> 90, 90 -> 80
start_game = (max_matches - 1) // 10 * 10

def callback(start_game: int, msg: CMsgGCToClientFindTopSourceTVGamesResponse) -> bool:
return msg.start_game == start_game

futures = [
self._state.ws.gc_wait_for(
CMsgGCToClientFindTopSourceTVGamesResponse,
check=partial(callback, start_game),
)
for start_game in range(0, start_game + 1, 10)
]

if hero is MISSING:
await self._state.ws.send_gc_message(CMsgClientToGCFindTopSourceTVGames(start_game=start_game))
else:
await self._state.ws.send_gc_message(
CMsgClientToGCFindTopSourceTVGames(start_game=start_game, hero_id=hero.value)
)

async with timeout(30.0):
responses = await asyncio.gather(*futures)
live_matches = [
LiveMatch(self._state, match_info) for response in responses for match_info in response.game_list
]
# still need to slice the list, i.e. in case user asks for 85 games, but live_matches above will have 90 matches
return live_matches[:max_matches]

async def tournament_live_games(
self,
# todo: league_id as integer is not human-readable/gettable thing, introduce methods to easily find those
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.
"""

future = self._state.ws.gc_wait_for(
CMsgGCToClientFindTopSourceTVGamesResponse,
check=lambda msg: msg.league_id == league_id,
)
await self._state.ws.send_gc_message(CMsgClientToGCFindTopSourceTVGames(league_id=league_id))

async with timeout(30.0):
response = await future
return [LiveMatch(self._state, match_info) for match_info in response.game_list]

async def live_matches(
self,
# todo: lobby_ids is not easy to get by the user. Introduce methods to get it, i.e. from Rich Presence
lobby_ids: list[int],
) -> list[LiveMatch]:
"""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.
"""
future = self._state.ws.gc_wait_for(
CMsgGCToClientFindTopSourceTVGamesResponse,
check=lambda msg: msg.specific_games == True,
)
await self._state.ws.send_gc_message(CMsgClientToGCFindTopSourceTVGames(lobby_ids=lobby_ids))

async with timeout(30.0):
response = await future
# todo: test with more than 10 lobby_ids, Game Coordinator will probably chunk it wrongly or fail at all
return [LiveMatch(self._state, match_info) for match_info in response.game_list]


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