Skip to content

Commit

Permalink
🔀: merge pull request #33 from frissyn/dev
Browse files Browse the repository at this point in the history
  • Loading branch information
frissyn authored Mar 14, 2022
2 parents eb5c7f8 + 7f74895 commit fdb0a1c
Show file tree
Hide file tree
Showing 13 changed files with 171 additions and 94 deletions.
8 changes: 7 additions & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,16 @@ def setUp(self):
KEY = os.environ["VALPY-KEY"]

self.access_key = KEY
self.client = valorant.Client(KEY)
self.client = valorant.Client(KEY, locale=None, load_content=False)

def tearDown(self):
self.client.handle.sess.close()

def assertHasAttr(self, obj: object, attr: str):
exp = hasattr(obj, attr)

if not exp:
self.fail(f"{obj.__class__} has no attribute '{attr}'.")


from .client import *
2 changes: 1 addition & 1 deletion tests/__main__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import unittest

if __name__ == "__main__":
unittest.main("tests", exit=False, warnings=None)
unittest.main("tests", exit=False, warnings=None, verbosity=5)
1 change: 1 addition & 0 deletions tests/client/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from .test_account import TestAccount
from .test_content import TestContent
from .test_ranked import TestRanked
18 changes: 5 additions & 13 deletions tests/client/test_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,16 @@
class TestAccount(BaseTest):
def test_account(self):
acc = self.client.get_user(
"B-_urbbPjxhSekDzdIS7fznZ6w82fss8PBd1OnOIiJ_7wNdRyj4cljexSBoGCESzJglohUbAM4H5kw"
"B-"
"_urbbPjxhSekDzdIS7fznZ6w82fss8PBd1OnOIiJ"
"_7wNdRyj4cljexSBoGCESzJglohUbAM4H5kw"
)

self.assertEqual(acc.gameName, "frissyn")
self.assertEqual(getattr(acc, "gameName", None), "frissyn")
self.assertIsInstance(acc, valorant.AccountDTO)

def test_account_by_name(self):
acc = self.client.get_user_by_name("frissyn#6969")

self.assertEqual(acc.gameName, "frissyn")
self.assertEqual(getattr(acc, "gameName", None), "frissyn")
self.assertIsInstance(acc, valorant.AccountDTO)

def test_account_missing(self):
acc = self.client.get_user(".")

self.assertIsNone(acc)

def test_account_missing_by_name(self):
acc = self.client.get_user_by_name("missing#0000")

self.assertIsNone(acc)
29 changes: 13 additions & 16 deletions tests/client/test_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,21 @@


class TestContent(BaseTest):
def test_content(self):
content = self.client.get_content(cache=True)
def setUp(self):
super().setUp()

self.assertIsInstance(content, valorant.ContentDTO)
self.assertEqual(content, self.client.content)
self.content = self.client.get_content(cache=True)

def test_asset(self):
assets = {
"gamemode": self.client.asset(assetName="BombGameMode"),
"agent": self.client.asset(name="Viper"),
"map": self.client.asset(id="7EAECC1B-4337-BBF6-6AB9-04B8F06B3319"),
}
def test_asset_1(self):
asset = self.client.asset(name="Neon")

self.assertEqual(assets["gamemode"].name, "Standard")
self.assertEqual(assets["agent"].name, "Viper")
self.assertEqual(assets["map"].name, "Ascent")
self.assertEqual(asset.name, "Neon")

def test_leaderboard(self):
lb = self.client.get_leaderboard(100)
def test_asset_2(self):
asset = self.client.asset(id="7EAECC1B-4337-BBF6-6AB9-04B8F06B3319")

self.assertIsInstance(lb, valorant.LeaderboardDTO)
self.assertEqual(asset.name, "Ascent")

def test_content_attributes(self):
for name in valorant.Lex.CONTENT_NAMES:
self.assertHasAttr(self.content, name)
43 changes: 43 additions & 0 deletions tests/client/test_ranked.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import valorant

from tests import BaseTest


class TestRanked(BaseTest):
def setUp(self):
super().setUp()

self.lb = self.client.get_leaderboard(10)
self.iterator = self.client.get_leaderboard(pages=3, size=10)

def test_leaderboard_attributes(self):
for name in [
"actId",
"players",
"totalPlayers",
"startIndex",
"query",
"shard",
]:
self.assertHasAttr(self.lb, name)

def test_leaderboard_players(self):
lb = self.client.get_leaderboard(10)

for player in lb.players:
for name in [
"gameName",
"leaderboardRank",
"numberOfWins",
"puuid",
"rankedRating",
"tagLine",
]:
self.assertHasAttr(player, name)

def test_leaderboard_iteration(self):
for i in range(self.iterator.pages):
next(self.iterator)

with self.assertRaises(StopIteration):
next(self.iterator)
2 changes: 1 addition & 1 deletion valorant/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class Version(t.NamedTuple):
release: t.Literal["alpha", "beta", "dev"]


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

if not version_info.release:
tag = ""
Expand Down
20 changes: 0 additions & 20 deletions valorant/caller.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,30 +45,10 @@ def call(
route: t.Optional[t.Text] = False,
**kw,
) -> t.Mapping[str, t.Any]:
if ep not in list(self.eps.keys()):
raise ValueError
else:
pass

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)
r.raise_for_status()

return r.json()


class ClientCaller(object):
def __init__(self, token: t.Text):
self.base = "https://pd.{code}.a.pvp.net/"
self.token = token

self.sess = requests.Session()
self.sess.headers.update(
{
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
"X-Riot-Entitlements-JWT": "riot_entitlement",
}
)
74 changes: 51 additions & 23 deletions valorant/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
ContentDTO,
ContentItemDTO,
LeaderboardDTO,
LeaderboardIterator,
PlatformDataDTO,
)

Expand All @@ -29,23 +30,27 @@ class Client(object):
:type key: str
:param locale:
The region locale to use when making requests. This defaults to the system's
locale as determined by Python. (i.e ``locale.getdefaultlocale()``) If set to
``None``, `localizedNames` will be included in response objects.
locale as determined by Python.
(i.e `locale.getdefaultlocale() <https://docs.python.org/3/library/locale.html#locale.getdefaultlocale>`_)
If set to ``None``, :attr:`ContentItemDTO.localizedNames` will be included in
the response.
:type locale: Optional[str]
:param region:
The region to use when making requests. This defaults to `na`. Valid
regions include `na`, `eu`, `latam`, etc.
The region to use when making requests. This defaults to `na`. Valid regions
include `na`, `eu`, `latam`, etc. See :data:`Lex.REGIONS` for a complete list
of valid regions.
:type region: Optional[str]
:param route:
The region route to user when making requests. This defaults to `americas`.
Valid routes are `americas`, `asia`, and `europe`.
The region route to use when making requests for Riot Accounts. This defaults
to `americas`. Valid routes are `americas`, `asia`, `europe`, and `esports`.
See :data:`Lex.ROUTES` for a complete list of valid routes.
:type route: Optional[str]
:param load_content:
Whether to load and cache content data from VALORANT. Defaults to `True`.
Whether to load and cache content data from VALORANT upon initialization.
Defaults to `True`.
:type load_content: bool
.. versionchanged: 1.0
Renamed *reload* parameter to *load_content*
*Changed in version 1.0:* Renamed *reload* parameter to *load_content*.
"""

def __init__(
Expand Down Expand Up @@ -78,12 +83,15 @@ def _content_if_cache(self) -> ContentDTO:
def __getattribute__(self, name):
return super(Client, self).__getattribute__(name)

def asset(self, **attributes: t.Mapping[t.Text, t.Any]) -> t.Optional[DTO]:
def asset(
self, **attributes: t.Mapping[t.Text, t.Any]
) -> t.Optional[t.Union[ActDTO, ContentItemDTO]]:
"""Find an item in VALORANT content data matching all given attributes.
Returns ``None`` if item is not found.
Returns ``None`` if item is not found. This works because there are no
semantic distinctions between Content Items.
For example, ``client.asset(name="Viper")`` would return a
:class:`ContentItemDTO <contentitemdto>` denoting content data for Viper.
:class:`ContentItemDTO` denoting content data for Viper.
.. note::
If content data is not cached, this function will make a request
Expand All @@ -92,7 +100,7 @@ def asset(self, **attributes: t.Mapping[t.Text, t.Any]) -> t.Optional[DTO]:
:param attributes: A mapping of keyword arguments to match for.
:type attributes: Mapping[str, Any]
:rtype: Optional[DTO]
:rtype: Optional[Union[ActDTO, ContentItemDTO]]
"""
content = self._content_if_cache()

Expand All @@ -105,7 +113,7 @@ def asset(self, **attributes: t.Mapping[t.Text, t.Any]) -> t.Optional[DTO]:
return None

def get_acts(self) -> t.List[ActDTO]:
"""Get a :class:`ContentList` of :class:`ActDTO` objects from VALORANt.
"""Get a :class:`ContentList` of :class:`ActDTO` objects from VALORANT.
:rtype: ContentList[ActDTO]
"""
Expand Down Expand Up @@ -161,7 +169,7 @@ def get_content(self, cache: bool = True) -> ContentDTO:
:param cache: If set to ``True``, the Client will cache the response data,
and subsequent calls wills return the cache. Update this cache by calling
``.get_content`` again with cache set to ``True``.
:func:`Client.get_content` again with cache set to ``True``.
.. note::
The cache provided is stored in memory and will not persist across program
Expand Down Expand Up @@ -204,9 +212,17 @@ def get_game_modes(self) -> t.List[ContentItemDTO]:
return self._content_if_cache().gameModes

def get_leaderboard(
self, size: int = 100, page: int = 0, actID: t.Text = ""
) -> LeaderboardDTO:
self,
size: int = 100,
page: int = 0,
pages: t.Optional[int] = None,
actID: t.Text = "",
) -> t.Union[LeaderboardDTO, LeaderboardIterator]:
actID = self.get_current_act().id if not actID else actID

if pages:
return LeaderboardIterator(self.handle, pages=pages, size=size, actID=actID)

params = {"size": size, "startIndex": size * page}

r = self.handle.call("GET", "leaderboard", params=params, actID=actID)
Expand Down Expand Up @@ -274,33 +290,45 @@ def get_sprays(self) -> t.List[ContentItemDTO]:
"""
return self._content_if_cache().sprays

def get_user(self, puuid: t.Text) -> t.Optional[AccountDTO]:
def get_user(
self, puuid: t.Text, route: t.Text = "americas"
) -> t.Optional[AccountDTO]:
"""Get a Riot Account by their PUUID. Returns ``None`` if user could not
be found.
:param puuid: The PUUID of the account to retrieve.
:type puuid: str
:param route:
Geographical route to get the account from. See :data:`Lex.ROUTES` for
a list of valid routes. Defaults `americas`.
:type route: str
:rtype: Optional[AccountDTO]
"""

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

return AccountDTO(r, self.handle)

def get_user_by_name(self, name: t.Text) -> t.Optional[AccountDTO]:
"""Gets a Riot Account by thier game name and tag line. Returns ``None`` if
def get_user_by_name(
self, name: t.Text, route: t.Text = "americas"
) -> t.Optional[AccountDTO]:
"""Gets a Riot Account by their game name and tag line. Returns ``None`` if
user could not be found.
:param name:
The account's full game name and tag line, split by a hastag.
(i.e `frissyn#6969`)
:type name: str
:param route:
Geographical route to get the account from. See :data:`Lex.ROUTES` for
a list of valid routes. Defaults `americas`.
:type route: str
:rtype: Optional[AccountDTO]
"""
vals = name.split("#")
Expand All @@ -311,7 +339,7 @@ def get_user_by_name(self, name: t.Text) -> t.Optional[AccountDTO]:
"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]:
if e.response.status_code in (400, 404):
return None
else:
e.response.raise_for_status()
Expand Down
8 changes: 0 additions & 8 deletions valorant/lexicon.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,6 @@ def __init__(self):
"""List of content data attribute names."""

ENDPOINTS = {
"client": {
"mmr": "mmr/v1/players/{playerID}/competitiveupdates",
},
"web": {
"content": "val/content/v1/contents",
"game-name": "riot/account/v1/accounts/by-riot-id/{name}/{tag}",
Expand All @@ -101,11 +98,6 @@ def __init__(self):

HEADERS = {
"web": {"Accept-Charset": "application/x-www-form-urlencoded; charset=UTF-8"},
"client": {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"X-Riot-Entitlements-JWT": "riot_entitlement",
},
}
"""Default headers for the client and web API."""

Expand Down
12 changes: 9 additions & 3 deletions valorant/local/client.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import os
import ssl
import json
import base64
import requests

from ..lexicon import Lex


class LocalClient(object):
"""Client for interacting with the local instance of the VALORANT application.
The game must be running for this class to function properly. Currently unstable,
support is coming soon.
This is called the `RCS API`. The game must be running for this class to function
properly. Currently unstable, complete support is coming soon.
.. warning::
While interacting with the RCS API is not
`explicitly disallowed <https://reddit.com/r/VALORANT/comments/oae5g6/comment/h3hwxtf>`_,
please have some common sense. ``valorant.py`` is not liable for any punishment
you may recieve if you break Riot's Terms of Service. (`i.e. creating an Auto
Agent Selector`)
:param region:
The region to instance the client with. If this doesn't match the game's
Expand Down
Loading

0 comments on commit fdb0a1c

Please sign in to comment.