1010from cachetools import TTLCache
1111from 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
1414from .models import Player , Club , PartialClub , Events , Leaderboard , Constants , MiscData , BattleLog
15- from .utils import API , bstag
15+ from .utils import API , bstag , typecasted
1616
1717log = logging .getLogger (__name__ )
1818
1919
2020class 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={}®ion={}' .format (self .api .LEADERBOARD , lb_type , count , region )
241- if lb_type in self .api .BRAWLERS :
242- url = '{}/players?count={}&brawlers={}®ion={}' .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={}®ion={}' .format (self .api .LEADERBOARD , lb_type , limit , region )
265+ if lb_type == 'brawlers' :
266+ url = '{}/players?count={}&brawlers={}®ion={}' .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