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

1.0.3 Release #37

Merged
merged 15 commits into from
Apr 5, 2022
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
13 changes: 5 additions & 8 deletions .github/workflows/library-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,11 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3

- env:
VALORANT_KEY: ${{ secrets.VALORANT_KEY }}
with:
python-version: '3.9'

- name: Install local package
run: |
pip install -e . --user
- env:
VALPY_KEY: ${{ secrets.VALPY_KEY }}

- name: Run library tests
run: |
python -m tests
- run: python -m tests
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.6, 3.7, 3.8]
python-version: [3.8, 3.9]

steps:
- uses: actions/checkout@v2
Expand Down
33 changes: 12 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
# valorant.py

[![GitHub Actions](https://camo.githubusercontent.com/0fc9226929794d4d4dfb9ac05a1786942f8e4b4300207224277ac49e22e9fdb6/68747470733a2f2f7472617669732d63692e636f6d2f7073662f626c61636b2e7376673f6272616e63683d6d6173746572)](https://github.com/frissyn/valorant.py/actions)
[![valorant on PyPI](https://img.shields.io/pypi/v/valorant.svg)](https://pypi.python.org/pypi/valorant)
[![Downloads](https://pepy.tech/badge/valorant/month)](https://pepy.tech/project/valorant)
[![License](https://img.shields.io/pypi/l/valorant.svg)](https://pypi.python.org/pypi/valorant)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Contribute](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/frissyn/valorant.py/issues)
[![Discord Chat](https://img.shields.io/badge/discord-join-7389D8?style=flat&logo=discord)](https://discord.gg/b3qjk4epPr)
[![Actions](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Ffrissyn%2Fvalorant.py%2Fbadge%3Fref%3Dmaster&style=flat-square)](https://actions-badge.atrox.dev/frissyn/valorant.py/goto?ref=master)
[![PyPI](https://img.shields.io/pypi/v/valorant?style=flat-square)](https://pypi.python.org/pypi/valorant)
[![Downloads](https://img.shields.io/pypi/dm/valorant?style=flat-square)](https://pepy.tech/project/valorant)
![LICENSE](https://img.shields.io/github/license/frissyn/valorant.py?style=flat-square)
[![Discord](https://img.shields.io/badge/discord-join-7389D8?style=flat-square&logo=discord)](https://discord.gg/b3qjk4epPr)

`valorant.py` is an unofficial API wrapper for Riot Games' Valorant API endpoints. It's modern, easy to use, feature-rich, and intuitive!

## Features

**Simple:** High-level abstraction of API interactions; easy to use and easy to customize.
+ **Simple:** High-level abstraction of API interactions; easy to use and easy to customize.

**Lightweight:** Doesn't rely on any external dependencies, minimal package size.
+ **Lightweight:** Doesn't rely on any external dependencies, minimal package size.

**Extensive:** Covers all Valorant related endpoints from the Riot Games API. Also includes Account coverage.
+ **Extensive:** Covers all Valorant related endpoints from the Riot Games API. Also includes Account coverage.

**Fast:** HTTP requests and object instancing optimized to use minimal resources and complete tasks quickly!
+ **Fast:** HTTP requests and object instancing optimized to use minimal resources and complete tasks quickly!

**Intuitive:** Complete auto-completion, docstrings, and type-hinting for all library objects and variables.
+ **Intuitive:** Complete auto-completion, docstrings, and type-hinting for all library objects and variables.

## Installation

Expand All @@ -30,7 +28,6 @@
|:----------:|:------------------------|
|PIP |`pip install valorant` |
|Poetry |`poetry add valorant` |
|PIPEnv |`pipenv install valorant`|

## Usage

Expand Down Expand Up @@ -69,14 +66,8 @@ Use `bash bin/docs` to start the documentation server locally. This uses Ruby's

## Help and Questions

Have a bug or issue? Need help with the API? Open an [issue](https://github.com/frissyn/valorant.py/issues) or hop in the [#valorant-py](https://discord.gg/b3qjk4epPr) channel of my Community Discord Server.
Have a bug or issue? Need help with the API? Open an [issue](https://github.com/frissyn/valorant.py/issues) or hop in the [#valorant-py](https://discord.gg/b3qjk4epPr) channel of the Frisscraft Community Discord Server.

## Contributing

1. Fork the repository: [`Fork`](https://github.com/frissyn/valorant.py/fork)
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -a -m 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request! 🎉

You can also re-create these steps with GitHub Desktop, Visual Studio Code, or whatever `git` version control UI you prefer. You don't have to, but I use prefixes for all my commits (i.e `✨: add asyncio run to package namespace`). I have a personal style guide that I use, which you can find [`here`](https://github.com/frissyn/commit-prefixes).
Head over to the [**Contributing Guide**](https://github.com/frissyn/valorant.py/blob/master/.github/CONTRIBUTING.md) page.
4 changes: 4 additions & 0 deletions bin/format
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash

pip install black > /dev/null 2>&1
black valorant tests
7 changes: 7 additions & 0 deletions docs/pages/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,13 @@ LeaderboardDTO
:undoc-members:


LeaderboardIterator
~~~~~~~~~~~~~~~~~~~
.. autoclass:: LeaderboardIterator
:members:
:undoc-members:


LeaderboardPlayerDTO
~~~~~~~~~~~~~~~~~~~~
.. autoclass:: LeaderboardPlayerDTO
Expand Down
2 changes: 1 addition & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

class BaseTest(unittest.TestCase):
def setUp(self):
KEY = os.environ["VALPY-KEY"]
KEY = os.environ["VALPY_KEY"]

self.access_key = KEY
self.client = valorant.Client(KEY, locale=None, load_content=False)
Expand Down
8 changes: 2 additions & 6 deletions valorant/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,7 @@ class Version(t.NamedTuple):
release: t.Literal["alpha", "beta", "dev"]


version_info = Version(major=1, minor=0, micro=2, release="")

if not version_info.release:
tag = ""
else:
tag = "-" + version_info.release
version_info = Version(major=1, minor=0, micro=3, release="dev")

tag = f"-{version_info.release}" if version_info.release else ""
__version__ = ".".join(str(i) for i in version_info[:3]) + tag
7 changes: 6 additions & 1 deletion valorant/caller.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,19 @@ def call(
self,
m: t.Text,
ep: t.Text,
escape_if: t.Tuple[int] = (),
params: t.Optional[t.Mapping] = None,
route: t.Optional[t.Text] = False,
**kw,
) -> t.Mapping[str, t.Any]:
) -> t.Optional[t.Mapping[str, t.Any]]:
prefix = self.base.format(root=self.route if route else self.region)
url = prefix + self.eps[ep].format(**kw)

r = self.sess.request(m, url, params=params)

if r.status_code in escape_if:
return None

r.raise_for_status()

return r.json()
99 changes: 72 additions & 27 deletions valorant/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
LeaderboardDTO,
LeaderboardIterator,
PlatformDataDTO,
MatchDTO,
)


Expand Down Expand Up @@ -73,9 +74,7 @@ def __init__(
self.content = None

def _content_if_cache(self) -> ContentDTO:
content = getattr(self, "content", None)

if content:
if content := getattr(self, "content", None):
return content
else:
return ContentDTO(self.handle.call("GET", "content"))
Expand Down Expand Up @@ -105,9 +104,7 @@ def asset(
content = self._content_if_cache()

for name in Lex.CONTENT_NAMES:
el = getattr(content, name).get(**attributes)

if el:
if el := getattr(content, name).get(**attributes):
return el

return None
Expand Down Expand Up @@ -192,7 +189,7 @@ def get_current_act(self) -> t.Optional[ActDTO]:
:rtype: Optional[ActDTO]
"""

return self.get_acts().find(isActive=True)
return self.get_acts().get(isActive=True)

def get_equips(self) -> t.List[ContentItemDTO]:
"""Get a :class:`ContentList` of :class:`ContentItemDTO` objects that each
Expand All @@ -218,7 +215,51 @@ def get_leaderboard(
pages: t.Optional[int] = None,
actID: t.Text = "",
) -> t.Union[LeaderboardDTO, LeaderboardIterator]:
actID = self.get_current_act().id if not actID else actID
"""Get the ranked leaderboard for an Act in VALORANT.

:param size:
Size of the leaderboard players to include. Can be between ``1`` and ``100``.
If this value is greater than ``100``, the remaining items in leaderboard will
be ``None``.
:type size: int
:param page:
Page of the leaderboard to retrieve. For example, page 4 of a leaderboard
with a size of 50 will skip the first 200 players.
:param pages:
Number of pages to retrieve from the leaderboard. If specified, the ``page``
parameter will be ignored. This will return a :class:`LeaderboardIterator`
of the retrieved pages.
:type page: Optional[int]
:param actID:
ID of the Act to get the leaderboard from. This defaults to the currently
active Act.
:type actID: str

**Examples:**

.. code-block:: python

# Get players from 101-200th rank on the leaderboard.
lb = client.get_leaderboard(size=100, page=2)

.. code-block:: python

# Loop through multiple leaderboard pages.
pages = client.get_leaderboard(size=50, pages=3)

for page in pages:
print(page.totalPlayers)

.. note::

The :class:`LeaderboardIterator` will request the next page of the leaderboard
after each iteration. Be wary of running into ratelimits when iterating over
a large amount of pages.

:rtype: Union[LeaderboardDTO, LeaderboardIterator]
"""

actID = actID or self.get_current_act().id

if pages:
return LeaderboardIterator(self.handle, pages=pages, size=size, actID=actID)
Expand All @@ -237,7 +278,16 @@ def get_maps(self) -> t.List[ContentItemDTO]:
"""
return self._content_if_cache().maps

def get_match(self, id: t.Text) -> t.Optional[MatchDTO]:
r = self.handle.call("GET", "match", matchID=id, escape_if=(400, 404))

return MatchDTO(r) if r else None

def get_platform_status(self) -> PlatformDataDTO:
"""Get status of VALORANT for the given platform.

:rtype: PlatformDataDTO
"""
r = self.handle.call("GET", "status")

return PlatformDataDTO(r)
Expand Down Expand Up @@ -305,15 +355,11 @@ def get_user(
:rtype: Optional[AccountDTO]
"""

try:
r = self.handle.call("GET", "puuid", route=True, puuid=puuid)
except requests.exceptions.HTTPError as e:
if e.response.status_code in (400, 404):
return None
else:
e.response.raise_for_status()
r = self.handle.call(
"GET", "puuid", route=True, puuid=puuid, escape_if=(400, 404)
)

return AccountDTO(r, self.handle)
return AccountDTO(r, self.handle) if r else None

def get_user_by_name(
self, name: t.Text, route: t.Text = "americas"
Expand All @@ -334,14 +380,13 @@ def get_user_by_name(
vals = name.split("#")
vals = [urllib.parse.quote(v, safe=Lex.SAFES) for v in vals]

try:
r = self.handle.call(
"GET", "game-name", route=True, name=vals[0], tag=vals[1]
)
except requests.exceptions.HTTPError as e:
if e.response.status_code in (400, 404):
return None
else:
e.response.raise_for_status()

return AccountDTO(r, self.handle)
r = self.handle.call(
"GET",
"game-name",
route=True,
name=vals[0],
tag=vals[1],
escape_if=(400, 404),
)

return AccountDTO(r, self.handle) if r else None
8 changes: 1 addition & 7 deletions valorant/local/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,6 @@ def get_presences(self, user=False) -> dict:
if user:
puuid = self.get_session()["puuid"]

for u in data["presences"]:
if u["puuid"] == puuid:
return u
else:
pass

return {}
return next((u for u in data["presences"] if u["puuid"] == puuid), {})
else:
return data
10 changes: 2 additions & 8 deletions valorant/objects/dto.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@

class DTOEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, DTO):
return o._json

return json.JSONEncoder.default(self, o)
return o._json if isinstance(o, DTO) else json.JSONEncoder.default(self, o)


class DTO(object):
Expand All @@ -33,10 +30,7 @@ def __repr__(self) -> t.Text:

@classmethod
def optional(cls, obj: t.Optional[t.Mapping]) -> t.Optional:
if obj != None:
return cls(obj)

return None
return cls(obj) if obj != None else None

def json(self) -> dict:
"""Return a JSON (dictionary) representation of this DTO.
Expand Down
28 changes: 16 additions & 12 deletions valorant/objects/ranked.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,13 @@ def __init__(self, obj):


class LeaderboardIterator:
"""Simple iterator utility for getting multiple leaderboard pages.
Each iteraction returns a :class:`LeaderboardDTO`. See
:func:`Client.get_leaderboard` for more info.
"""

def __init__(self, caller: WebCaller, pages: int = 1, **params):
self.handle = caller
self._handle = caller
self.kwargs = params
self.index, self.pages = 0, pages

Expand All @@ -46,17 +51,16 @@ def __iter__(self):
def __next__(self) -> LeaderboardDTO:
if self.index >= self.pages:
raise StopIteration
else:
self.index += 1

payload = {
"actID": self.kwargs["actID"],
"params": {
"size": self.kwargs["size"],
"startIndex": (self.index - 1) * self.kwargs["size"],
},
}
payload = {
"actID": self.kwargs["actID"],
"params": {
"size": self.kwargs["size"],
"startIndex": (self.index) * self.kwargs["size"],
},
}

data = self.handle.call("GET", "leaderboard", **payload)
self.index += 1
data = self._handle.call("GET", "leaderboard", **payload)

return LeaderboardDTO(data)
return LeaderboardDTO(data)