Skip to content

Commit 20ddd46

Browse files
committed
Merge branch '3.0.0'
2 parents 02d60d7 + 33970ad commit 20ddd46

32 files changed

Lines changed: 1418 additions & 231 deletions

CHANGELOG.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
# Change Log
22
All notable changes to this project will be documented in this file.
33

4+
## [3.0.0]
5+
### Added
6+
- Official API support (all endpoints implemented, all methods documented)
7+
- `Forbidden` error raised when code 403 received
8+
- New terminology! "BrawlAPI" refers to the [unofficial API](https://api.brawlapi.cf/v1) while "OfficialAPI" refers to the [official API](https://developer.brawlstars.com)
9+
### Changed (BREAKING)
10+
- The unofficial API's client will now be accessed as `brawlstats.BrawlAPI` (from `brawlstats.Client`)
11+
- The unofficial API's models will now be accessed as `brawlstats.brawlapi.ModelName`
12+
- The official API's client will be accessed by `brawlstats.OfficialAPI`
13+
- The official API's models will be accessed by `brawlstats.officialapi.ModelName`
14+
- `get_leaderboard()` will now require "brawlers" for the ranking type and the actual brawler name passed through the brawler kwarg.
15+
- `get_leaderboard()` `count` argument has been renamed to `limit`
16+
### Fixed
17+
- BrawlAPI `get_leaderboard` parameter documentation fixed
18+
- Arguments passed into functions that require player/club tags now properly get formatted correctly.
19+
420
## [2.3.14] - 9/14/19
521
### Changed
622
- Default timeout from 10 to 30
@@ -215,4 +231,22 @@ BREAKING CHANGES: Brawl Stars dev team changed "Band" to "Club". This update fix
215231

216232
## [2.0.0] - 11/19/18
217233
### Added
218-
- Support for the brand new API at https://brawlapi.cf/api
234+
- Support for the brand new API at https://brawlapi.cf/api
235+
236+
## [1.5.0] - 2/18/18
237+
### Added
238+
- Python 3.5 support!
239+
240+
## [1.2.0] - 2/13/18
241+
### Changed
242+
- Base links for the new API changes
243+
244+
## [1.1.12] - 2/7/18
245+
### Added
246+
- Essential core
247+
248+
249+
250+
## [1.0.0]
251+
### Added
252+
- Request maker

README.rst

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,16 @@ Brawl Stats
2020
:target: https://github.com/SharpBit/brawlstats/blob/master/LICENSE
2121
:alt: MIT License
2222

23-
- This library is a sync and async wrapper the unofficial `Brawl Stars API`_.
23+
- This library is a sync and async wrapper for https://brawlapi.cf (BrawlAPI) and the official Brawl Stars API.
2424
- Python 3.5.3 or later is required.
25-
- To keep up with API changes, discussions, and status, I recommend you join the API's `discord server`_.
2625

2726
Features
2827
~~~~~~~~
2928

30-
- Covers all of the API endpoints.
29+
- Covers all of the endpoints of both APIs.
3130
- Use the same client for sync and async usage.
32-
- Helper functions to access other game files such as maps and audio files.
31+
- Choose which API you want to use!
32+
- Access game constants such as maps, brawlers, and images.
3333
- Easy to use with an object oriented design.
3434

3535
Installation
@@ -54,40 +54,40 @@ Documentation is being hosted on `Read the Docs`_.
5454

5555
Examples
5656
~~~~~~~~
57-
Examples are in the `examples folder`_.
58-
57+
Examples are in the `examples folder`_. Within each folder, you will find:
5958
- ``sync.py`` shows you basic sync usage
6059
- ``async.py`` shows you basic async usage
61-
- ``discord_cog.py`` shows an example Discord Bot cog using `discord.py rewrite`_
60+
- ``discord_cog.py`` shows an example Discord Bot cog using `discord.py`_
6261

6362
Misc
6463
~~~~
6564

66-
- If you are currently using this wrapper, feel free to star this repository :)
65+
- If you are currently using this wrapper, please star this repository :)
6766
- If you come across an issue in the wrapper, please `create an issue`_. Do **not** PM me on Discord for help.
68-
- If you need an API Key, create one using the `dashboard`_.
67+
- If you need an API Key for BrawlAPI, create one using the `dashboard`_. If you need one for the official API, visit https://developer.brawlstars.com
68+
- To receive help for using the BrawlAPI, I recommend you join BrawlAPI's `discord server`_.
6969

7070
Contributing
7171
~~~~~~~~~~~~
7272
Special thanks to this project's contributors ❤️
7373

7474
- `4JR`_
75-
- `OrangutanGaming`_
76-
- `Stitch`_
7775
- `kjkui`_
7876
- `Kyber`_
7977
- `Pollen`_
78+
- `OrangutanGaming`_
79+
- `Stitch`_
8080

8181
If you want to contribute, whether it be a bug fix or new feature, make sure to follow the `contributing guidelines`_.
8282

83-
.. _Brawl Stars API: https://api.brawlapi.cf/v1
8483
.. _create an issue: https://github.com/SharpBit/brawlstats/issues
8584
.. _discord server: https://discord.me/BrawlAPI
8685
.. _Read the Docs: https://brawlstats.rtfd.io/
8786
.. _examples folder: https://github.com/SharpBit/brawlstats/tree/master/examples
88-
.. _discord.py rewrite: https://github.com/rapptz/discord.py/tree/rewrite
87+
.. _discord.py: https://github.com/rapptz/discord.py
8988
.. _contributing guidelines: https://github.com/SharpBit/brawlstats/blob/master/CONTRIBUTING.md
9089
.. _dashboard: https://brawlapi.cf/dashboard
90+
9191
.. _4JR: https://github.com/fourjr
9292
.. _OrangutanGaming: https://github.com/OrangutanGaming
9393
.. _Stitch: https://github.com/Soumil07

brawlstats/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
from .core import Client
1+
from .brawlapi import Client as BrawlAPI
2+
from .officialapi import Client as OfficialAPI
23
from .errors import *
3-
from .models import *
44

55

66
############
77
# METADATA #
88
############
99

1010

11-
__version__ = 'v2.3.14'
11+
__version__ = 'v3.0.0'
1212
__title__ = 'brawlstats'
1313
__license__ = 'MIT'
1414
__author__ = 'SharpBit'

brawlstats/brawlapi/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from .core import Client
2+
from .models import *
Lines changed: 81 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,16 @@
1010
from cachetools import TTLCache
1111
from datetime import datetime
1212

13-
from .errors import NotFoundError, Unauthorized, ServerError, RateLimitError, MaintenanceError, UnexpectedError
13+
from ..errors import NotFoundError, Unauthorized, ServerError, Forbidden, RateLimitError, MaintenanceError, UnexpectedError
1414
from .models import Player, Club, PartialClub, Events, Leaderboard, Constants, MiscData, BattleLog
15-
from .utils import API, bstag
15+
from .utils import API, bstag, typecasted
1616

1717
log = logging.getLogger(__name__)
1818

1919

2020
class Client:
2121
"""
22-
This is a sync/async client class that lets you access the API.
22+
This is a sync/async client class that lets you access the unofficial BrawlAPI.
2323
2424
Parameters
2525
------------
@@ -59,7 +59,7 @@ def __init__(self, token, session=None, timeout=30, is_async=False, **options):
5959
self.timeout = timeout
6060
self.prevent_ratelimit = options.get('prevent_ratelimit', False)
6161
self.lock = asyncio.Lock() if options.get('prevent_ratelimit') is True else None
62-
self.api = API(options.get('base_url'))
62+
self.api = API(options.get('base_url'), version=1)
6363

6464
self.debug = options.get('debug', False)
6565
self.cache = TTLCache(540, 180) # 3 requests/sec
@@ -72,7 +72,7 @@ def __init__(self, token, session=None, timeout=30, is_async=False, **options):
7272
}
7373

7474
def __repr__(self):
75-
return '<BrawlStats-Client async={} timeout={}>'.format(self.is_async, self.timeout)
75+
return '<BrawlAPI-Client async={} timeout={} debug={}>'.format(self.is_async, self.timeout, self.debug)
7676

7777
def close(self):
7878
return self.session.close()
@@ -98,6 +98,8 @@ def _raise_for_status(self, resp, text, url):
9898
return (data, resp)
9999
if code == 401:
100100
raise Unauthorized(url, code)
101+
if code == 403:
102+
raise Forbidden(url, code, data['message'])
101103
if code in (400, 404):
102104
raise NotFoundError(url, code)
103105
if code == 429:
@@ -162,13 +164,17 @@ async def _aget_model(self, url, model, key=None):
162164
else:
163165
data, resp = await self._arequest(url)
164166

165-
if model == Constants:
166-
if key and not data.get(key):
167-
raise KeyError('No such key for Brawl Stars constants "{}"'.format(key))
168-
if key and data.get(key):
169-
return model(self, resp, data.get(key))
170-
if model in (PartialClub, BattleLog) and (isinstance(data, list) if model == PartialClub else 1):
167+
# Club search
168+
if model == PartialClub and isinstance(data, list):
171169
return [model(self, resp, data) for club in data]
170+
171+
if model == Constants:
172+
if key:
173+
if data.get(key):
174+
return model(self, resp, data.get(key))
175+
else:
176+
raise KeyError('No such Constants key "{}"'.format(key))
177+
172178
return model(self, resp, data)
173179

174180
def _get_model(self, url, model, key=None):
@@ -177,15 +183,21 @@ def _get_model(self, url, model, key=None):
177183
data, resp = self._request(url)
178184
if self.prevent_ratelimit:
179185
time.sleep(1 / self.ratelimit[0])
180-
if model == Constants:
181-
if key and not data.get(key):
182-
raise KeyError('No such key for Brawl Stars constants "{}"'.format(key))
183-
if key and data.get(key):
184-
return model(self, resp, data.get(key))
185-
if model in (PartialClub, BattleLog) and (isinstance(data, list) if model == PartialClub else 1):
186+
187+
# Club search
188+
if model == PartialClub and isinstance(data, list):
186189
return [model(self, resp, data) for club in data]
190+
191+
if model == Constants:
192+
if key:
193+
if data.get(key):
194+
return model(self, resp, data.get(key))
195+
else:
196+
raise KeyError('No such Constants key "{}"'.format(key))
197+
187198
return model(self, resp, data)
188199

200+
@typecasted
189201
def get_player(self, tag: bstag):
190202
"""Get a player's stats.
191203
@@ -203,6 +215,7 @@ def get_player(self, tag: bstag):
203215

204216
get_profile = get_player
205217

218+
@typecasted
206219
def get_club(self, tag: bstag):
207220
"""Get a club's stats.
208221
@@ -218,28 +231,39 @@ def get_club(self, tag: bstag):
218231

219232
return self._get_model(url, model=Club)
220233

221-
def get_leaderboard(self, lb_type: str, count: int=200, region='global'):
234+
def get_leaderboard(self, lb_type: str, limit: int=200, region='global', brawler=None):
222235
"""Get the top count players/clubs/brawlers.
223236
224237
Parameters
225238
----------
226-
type: str
227-
The type of leaderboard. Must be "players", "clubs", or the brawler leaderboard you are trying to access.
239+
lb_type: str
240+
The type of leaderboard. Must be "players", "clubs", "brawlers".
228241
Anything else will return a ValueError.
229-
count: Optional[int] = 200
242+
limit: Optional[int] = 200
230243
The number of top players or clubs to fetch.
231244
If count > 200, it will return a ValueError.
245+
region: Optional[str] = "global"
246+
The region to retrieve from. Must be a 2 letter country code or "global"
247+
brawler: Optional[str|int] = None
248+
The brawler name or ID.
232249
233250
Returns Leaderboard
234251
"""
235-
lb_type = lb_type.lower()
236-
if type(count) != int:
252+
if brawler:
253+
brawler = brawler.lower()
254+
if brawler not in self.api.BRAWLERS and lb_type not in ['players', 'clubs', 'brawlers']:
255+
raise ValueError("Please enter 'players', 'clubs' or 'brawlers'.")
256+
if brawler in self.api.BRAWLERS.keys():
257+
brawler = self.api.BRAWLERS[brawler]
258+
259+
if type(limit) != int:
237260
raise ValueError("Make sure 'count' is an int")
238-
if lb_type not in self.api.BRAWLERS + ['players', 'clubs'] or not 0 < count <= 200:
239-
raise ValueError("Please enter 'players', 'clubs' or a brawler or make sure 'count' is between 1 and 200.")
240-
url = '{}/{}?count={}&region={}'.format(self.api.LEADERBOARD, lb_type, count, region)
241-
if lb_type in self.api.BRAWLERS:
242-
url = '{}/players?count={}&brawlers={}&region={}'.format(self.api.LEADERBOARD, count, lb_type, region)
261+
if not 0 < limit <= 200:
262+
raise ValueError('Make sure limit is between 1 and 200.')
263+
264+
url = '{}/{}?count={}&region={}'.format(self.api.LEADERBOARD, lb_type, limit, region)
265+
if lb_type == 'brawlers':
266+
url = '{}/players?count={}&brawlers={}&region={}'.format(self.api.LEADERBOARD, limit, lb_type, region)
243267

244268
return self._get_model(url, model=Leaderboard)
245269

@@ -249,25 +273,21 @@ def get_events(self):
249273
Returns Events"""
250274
return self._get_model(self.api.EVENTS, model=Events)
251275

252-
def get_constants(self, key=None):
253-
"""Gets Brawl Stars constants extracted from the app.
276+
@typecasted
277+
def get_battle_logs(self, tag: bstag):
278+
"""Get a player's battle logs.
254279
255280
Parameters
256281
----------
257-
key: Optional[str] = None
258-
Any key to get specific data.
259-
260-
Returns Constants
261-
"""
262-
return self._get_model(self.api.CONSTANTS, model=Constants, key=key)
263-
264-
def get_misc(self):
265-
"""Gets misc data such as shop and season info.
282+
tag: str
283+
A valid player tag.
284+
Valid characters: 0289PYLQGRJCUV
266285
267-
Returns MiscData
286+
Returns BattleLog
268287
"""
288+
url = '{}?tag={}'.format(self.api.BATTLELOG, tag)
269289

270-
return self._get_model(self.api.MISC, model=MiscData)
290+
return self._get_model(url, model=BattleLog)
271291

272292
def search_club(self, club_name: str):
273293
"""Searches for bands of the provided club name.
@@ -282,6 +302,26 @@ def search_club(self, club_name: str):
282302
url = self.api.CLUB_SEARCH + '?name=' + club_name
283303
return self._get_model(url, model=PartialClub)
284304

305+
def get_constants(self, key=None):
306+
"""Gets Brawl Stars constants extracted from the app.
307+
308+
Parameters
309+
----------
310+
key: Optional[str] = None
311+
Any key to get specific data.
312+
313+
Returns Constants
314+
"""
315+
return self._get_model(self.api.CONSTANTS, model=Constants, key=key)
316+
317+
def get_misc(self):
318+
"""Gets misc data such as shop and season info.
319+
320+
Returns MiscData
321+
"""
322+
323+
return self._get_model(self.api.MISC, model=MiscData)
324+
285325
def get_datetime(self, timestamp: str, unix=True):
286326
"""Converts a %Y%m%dT%H%M%S.%fZ to a UNIX timestamp
287327
or a datetime.datetime object
@@ -301,18 +341,3 @@ def get_datetime(self, timestamp: str, unix=True):
301341
return int(time.timestamp())
302342
else:
303343
return time
304-
305-
def get_battle_logs(self, tag: bstag):
306-
"""Get a player's battle logs.
307-
308-
Parameters
309-
----------
310-
tag: str
311-
A valid player tag.
312-
Valid characters: 0289PYLQGRJCUV
313-
314-
Returns List\[BattleLog, ..., BattleLog\]
315-
"""
316-
url = '{}?tag={}'.format(self.api.BATTLELOG, tag)
317-
318-
return self._get_model(url, model=BattleLog)

0 commit comments

Comments
 (0)