PK[Kvncr_api/__init__.py""" Clash Royale wrapper for cr-api.com """ __version__ = "0.5" from .client import Client from .exceptions import APITimeoutError, APIClientResponseError, APIError from .models.clan import ClanPKYKNCª cr_api/client.py""" cr-api client for Clash Royale. """ import asyncio import logging import aiohttp from .exceptions import APIError, APIClientResponseError, APITimeoutError from .models import Clan, Tag, Player, Constants logger = logging.getLogger('__name__') logger.setLevel(logging.DEBUG) ch = logging.StreamHandler() ch.setLevel(logging.ERROR) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') ch.setFormatter(formatter) logger.addHandler(ch) class APIURL: """ API URL """ clan = 'http://api.cr-api.com/clan/{}' top_clans = 'http://api.cr-api.com/top/clans' profile = 'http://api.cr-api.com/profile/{}' constants = 'http://api.cr-api.com/constants' class Client: """ API Client. """ def __init__(self): pass async def fetch(self, url): """Fetch URL. :param url: URL :return: Response in JSON """ try: async with aiohttp.ClientSession() as session: async with session.get(url) as resp: data = await resp.json() if resp.status != 200: logger.error( "API Error | HTTP status {status} | {errmsg} | url: {url}".format( status=resp.status, errmsg=data.get('error'), url=url ) ) raise APIError except asyncio.TimeoutError: raise APITimeoutError except aiohttp.client_exceptions.ClientResponseError: raise APIClientResponseError return data async def get_clan(self, clan_tag): """Fetch a single clan.""" url = APIURL.clan.format(clan_tag) data = await self.fetch(url) if isinstance(data, list): data = data[0] return Clan(data=data, url=url) async def get_clans(self, clan_tags, include_members=True): """Fetch multiple clans. :param clan_tags: List of clan tags :param include_members: Include members or not. URL Format: http://api.cr-api.com/clan/28VVQPV9,Y8GYCGV/?members=0 """ url = '{api_url}/{tag_list}/{members}'.format( api_url=APIURL.clan, tag_list=','.join(clan_tags), members='' if include_members else '?members=0' ) data = await self.fetch(url) return data async def get_top_clans(self): """Fetch top clans.""" data = await self.fetch(APIURL.top_clans) return data async def get_profile(self, tag: str) -> Player: """Get player profile by tag. :param tag: :return: """ ptag = Tag(tag).tag url = APIURL.profile.format(ptag) data = await self.fetch(url) return Player(data=data, url=url) async def get_profiles(self, tags): """Fetch multiple players from profile API.""" ptags = [Tag(tag).tag for tag in tags] url = APIURL.profile.format(','.join(ptags)) data = await self.fetch(url) return [Player(data=d, url=url) for d in data] async def get_constants(self, key=None): """Fetch contants. :param key: Optional field. """ url = APIURL.constants data = await self.fetch(url) return Constants(data=data, url=url) PKYKBԞcr_api/exceptions.py""" Wrapper exceptions """ class APIError(Exception): pass class APITimeoutError(APIError): pass class APIClientResponseError(APIError): passPKcYKebiicr_api/models/__init__.pyfrom .player import Player from .clan import Clan from .core import Tag from .constants import Constants PKXKXa a cr_api/models/base.py# -*- coding: utf-8 -*- """ Clash Royale models. """ from json import dumps, loads from logging import getLogger __timeformat__ = '%Y-%m-%dT%H:%M:%SZ' __logs__ = getLogger(__package__) class BaseModel(object): """Clash Royale base model.""" def __init__(self, data=None, url=None): if url is not None: self._uniq = url self._data = data self._update_attributes(data) def _update_attributes(self, data): pass def __getattr__(self, attribute): """Proxy acess to stored JSON.""" if attribute not in self._data: raise AttributeError(attribute) value = self._data.get(attribute) setattr(self, attribute, value) return value def as_dict(self): """Return the attributes for this object as a dictionary. This is equivalent to calling: json.loads(obj.as_json()) :returns: this object’s attributes seriaized as a dictionary :rtype: dict """ return self._data def as_json(self): """Return the json data for this object. This is equivalent to calling: json.dumps(obj.as_dict()) :returns: this object’s attributes as a JSON string :rtype: str """ return dumps(self._data) @classmethod def _get_attribute(cls, data, attribute, fallback=None): """Return the attribute from the JSON data. :param dict data: dictionary used to put together the model :param str attribute: key of the attribute :param any fallback: return value if original return value is falsy :returns: value paried with key in dict, fallback """ if data is None or not isinstance(data, dict): return None result = data.get(attribute) if result is None: return fallback return result @classmethod def __class_attribute(cls, data, attribute, cl, *args, **kwargs): """Return the attribute from the JSON data and instantiate the class. "param dict data: dictionary used to put together the model or None "param str attribute: key of the attribute :param class cl: class that will be instantiated :returns: instantiated class or None :rtype: object or None """ value = cls._get_attribute(data, attribute) if value: return cl( value, *args, **kwargs ) return value def __repr__(self): repr_string = self._repr() return repr_string @classmethod def from_dict(cls, json_dict): """Return an instanc of this class formed from ``json_dict``.""" return cls(json_dict) @classmethod def from_json(cls, json): """Return an instane of this class formed from ``json``.""" return cls(loads(json)) def __eq__(self, other): return self._uniq == other._uniq def __ne__(self, other): return self._uniq != other._uniq def _repr(self): return "{}({})".format(self.__class__, self.__dict__) PKaYKT*cr_api/models/clan.py""" Clan """ from .base import BaseModel from .core import Badge, Region, Arena class ClanChest(BaseModel): def _update_attributes(self, data): # crowns self.crowns = self._get_attribute(data, 'clanChestCrowns') # crown percent self.crowns_percent = self._get_attribute(data, 'clanChestCrownsPercent') # crowns required self.crowns_required = self._get_attribute(data, 'clanChestCrownsRequired') class ClanMember(BaseModel): """Member model in clan.""" def _update_attributes(self, data): # - name self.name = self._get_attribute(data, 'name') # - arena self.arena = Arena(data=self._get_attribute(data, 'arena')) # - experience level self.experience_level = self._get_attribute(data, 'expLevel') # - trophies self.trophies = self._get_attribute(data, 'trophies') # - score: alias to trophies self.score = self._get_attribute(data, 'score') # - donations for the week self.donations = self._get_attribute(data, 'donations') # - current rank self.current_rank = self._get_attribute(data, 'currentRank') # - previous rank self.previous_rank = self._get_attribute(data, 'previousRank') # - clan chest crowns self.clan_chestcrowns = self._get_attribute(data, 'clanChestCrowns') # - player tag self.tag = self._get_attribute(data, 'tag') # - role: enum self.role = self._get_attribute(data, 'role') # - role name self.role_name = self._get_attribute(data, 'role_name') # - clan name self.clan_name = self._get_attribute(data, 'clan_name') # - clan name self.clan_tag = self._get_attribute(data, 'clan_tag') @property def rank_delta(self): """Difference in rank. Return None if previous rank is 0 """ if self.previous_rank == 0: return None else: return self.current_rank - self.previous_rank @property def league(self): """League ID from Arena ID.""" return max(0, self.arena.arean_id - 11) @property def league_icon_url(self): """League Icon URL.""" return ( 'http://smlbiobot.github.io/img/leagues/' 'league{}.png' ).format(self.league) class Clan(BaseModel): """Clash Royale Clan data.""" def _update_attributes(self, data): # - Name of clan self.name = self._get_attribute(data, 'name') # - badge self.badge = Badge(data=self._get_attribute(data, 'badge')) # - type of the clan: enum self.type = self._get_attribute(data, 'type') # - type name self.type_name = self._get_attribute(data, 'typeName') # - number of memebers in clan self.member_count = self._get_attribute(data, 'memberCount') # - required trophies to join self.required_score = self._get_attribute(data, 'requiredScore') # - total donations for the week self.donations = self._get_attribute(data, 'donations') # - current rank # TODO: not sure what this is self.current_rank = self._get_attribute(data, 'currentRank') # - clan description self.description = self._get_attribute(data, 'description') # - clan tag self.tag = self._get_attribute(data, 'tag') # - region self.region = Region(data=self._get_attribute(data, 'region')) # - members members = self._get_attribute(data, 'members') clan_dict = { "clan_name": self.name, "clan_tag": self.tag } self.members = [] if members is not None: for m in members: m.update(clan_dict) self.members.append(ClanMember(data=m)) @property def member_tags(self): """List of member tags.""" return [m.tag for m in self.members] PKhYKNOZ  cr_api/models/constants.pyfrom .base import BaseModel from .core import Region, Badge, Arena, Rarity, Card, ChestCycle class Constants(BaseModel): """API contants.""" def _update_attributes(self, data): self.arenas = [Arena(data=d) for d in self._get_attribute(data, "arenas")] self.badges = self._get_attribute(data, "badges") self.chest_cycle = ChestCycle(data=self._get_attribute(data, "chestCycle")) self.regions = [Region(data=d) for d in self._get_attribute(data, "countryCodes")] self.country_codes = self.regions self.rarities = [Rarity(data=d) for d in self._get_attribute(data, "rarities")] self.cards = [Card(data=d) for d in self._get_attribute(data, "cards")] def get_region_named(self, name): """Return region by name.""" for region in self.regions: if region.name == name: return region return None def get_chest_by_index(self, index): """Return chest by index.""" return self.chest_cycle.order[index] PKuhYKFg"cr_api/models/core.py""" Core models. Shared by multiple models. """ from .base import BaseModel class Card(BaseModel): """Card.""" def _update_attributes(self, data): self.name = self._get_attribute(data, 'name') self.rarity = self._get_attribute(data, 'rarity') self.level = self._get_attribute(data, 'level') self.count = self._get_attribute(data, 'count') self.required_for_upgrade = self._get_attribute(data, 'requiredForUpgrade') self.card_id = self._get_attribute(data, 'card_id') self.key = self._get_attribute(data, 'key') self.card_key = self._get_attribute(data, 'card_key') self.elixir = self._get_attribute(data, 'elixir') self.type = self._get_attribute(data, 'type') self.arena = self._get_attribute(data, 'arena') self.description = self._get_attribute(data, 'description') self.decklink = self._get_attribute(data, 'decklink') self.left_to_upgrade = self._get_attribute(data, 'leftToUpgrade') class Deck(BaseModel): """Deck with cards.""" def _update_attributes(self, data): self.cards = [Card(data=c) for c in data] class Badge(BaseModel): def _update_attributes(self, data): # - url self.url = self._get_attribute(data, 'url', 'http://smlbiobot.github.io/img/emblems/NoClan.png') # - filename self.filename = self._get_attribute(data, 'filename') # - key self.key = self._get_attribute(data, 'key') class Region(BaseModel): def _update_attributes(self, data): # - region is a country is_country = self._get_attribute(data, 'isCountry') self.is_country = is_country == True or is_country == 'true' # - name self.name = self._get_attribute(data, 'name') class Arena(BaseModel): def _update_attributes(self, data): self.image_url = self._get_attribute(data, 'imageURL') self.arena = self._get_attribute(data, 'arena') self.arena_id = self._get_attribute(data, 'arenaID') self.name = self._get_attribute(data, 'name') self.trophy_limit = self._get_attribute(data, 'trophyLimit') class Rarity(BaseModel): """Rarity.""" def _update_attributes(self, data): self.balance_multiplier = self._get_attribute(data, 'balance_multiplier') self.chance_weight = self._get_attribute(data, 'chance_weight') self.clone_relative_level = self._get_attribute(data, 'clone_relative_level') self.donate_capacity = self._get_attribute(data, 'donate_capacity') self.donate_reward = self._get_attribute(data, 'donate_reward') self.donate_xp = self._get_attribute(data, 'donate_xp') self.mirror_relative_level = self._get_attribute(data, 'mirror_relative_level') self.name = self._get_attribute(data, 'name') self.power_level_multiplier = self._get_attribute(data, 'power_level_multiplier') self.refund_gems = self._get_attribute(data, 'refund_gems') self.relative_level = self._get_attribute(data, 'relative_level') self.sort_capacity = self._get_attribute(data, 'sort_capacity') self.upgrade_cost = self._get_attribute(data, 'upgrade_cost') self.upgrade_exp = self._get_attribute(data, 'upgrade_exp') self.upgrade_material_count = self._get_attribute(data, 'upgrade_material_count') class ChestCycle(BaseModel): """Chest cycle.""" def _update_attributes(self, data): self.order = self._get_attribute(data, 'order') class Tag: """SuperCell tags.""" TAG_CHARACTERS = "0289PYLQGRJCUV" def __init__(self, tag: str): """Init. Remove # if found. Convert to uppercase. Convert Os to 0s if found. """ if tag.startswith('#'): tag = tag[1:] tag = tag.replace('O', '0') tag = tag.upper() self._tag = tag @property def tag(self): """Return tag as str.""" return self._tag @property def valid(self): """Return true if tag is valid.""" for c in self.tag: if c not in self.TAG_CHARACTERS: return False return True @property def invalid_chars(self): """Return list of invalid characters.""" invalids = [] for c in self.tag: if c not in self.TAG_CHARACTERS: invalids.append(c) return invalids @property def invalid_error_msg(self): """Error message to show if invalid.""" return ( 'The tag you have entered is not valid. \n' 'List of invalid characters in your tag: {}\n' 'List of valid characters for tags: {}'.format( ', '.join(self.invalid_chars), ', '.join(self.TAG_CHARACTERS) )) PKMXKAa$$cr_api/models/player.py""" Player profile """ from .base import BaseModel from .core import Deck, Badge, Arena class PlayerClan(BaseModel): """Clan model inside a profile.""" def _update_attributes(self, data): self.tag = self._get_attribute(data, 'tag') self.name = self._get_attribute(data, 'name', 'No Clan') self.role = self._get_attribute(data, 'role', 'N/A') self.badge = Badge(data=self._get_attribute(data, 'badge')) class PlayerExperience(BaseModel): """Player experience.""" def _update_attributes(self, data): self.level = self._get_attribute(data, 'level') self.xp = self._get_attribute(data, 'xp') self.xp_required_for_level_up = self._get_attribute(data, 'xpRequiredForLevelUp') self.xp_to_level_up = self._get_attribute(data, 'xpToLevelUp') class PlayerStats(BaseModel): """Player stats.""" def _update_attributes(self, data): self.legendary_trophies = self._get_attribute(data, 'legendaryTrophies') self.tournament_cards_won = self._get_attribute(data, 'tournamentCardsWon') self.max_trophies = self._get_attribute(data, 'maxTrophies') self.three_crown_wins = self._get_attribute(data, 'threeCrownWins') self.cards_found = self._get_attribute(data, 'cardsFound') self.favorite_card = self._get_attribute(data, 'favoriteCard') self.total_donatons = self._get_attribute(data, 'totalDonations') self.challenge_max_wins = self._get_attribute(data, 'challengeMaxWins') self.challenge_cards_won = self._get_attribute(data, 'challengeCardsWon') self.level = self._get_attribute(data, 'level') class PlayerGames(BaseModel): """Player game stats.""" def _update_attributes(self, data): self.total = self._get_attribute(data, 'total') self.tournament_games = self._get_attribute(data, 'tournamentGames') self.wins = self._get_attribute(data, 'wins') self.losses = self._get_attribute(data, 'losses') self.draws = self._get_attribute(data, 'draws') self.current_win_streak = self._get_attribute(data, 'currentWinStreak') class PlayerChestCycle(BaseModel): """Player chest cycle""" def _update_attributes(self, data): self.position = self._get_attribute(data, 'position') self.super_magical_pos = self._get_attribute(data, 'superMagicalPos') self.legendary_pos = self._get_attribute(data, 'legendaryPos') self.epic_pos = self._get_attribute(data, 'epicPos') class PlayerShopOffers(BaseModel): """Shop offers.""" def _update_attributes(self, data): self.legendary = self._get_attribute(data, 'legendary') self.epic = self._get_attribute(data, 'epic') self.arena = self._get_attribute(data, 'arena') class Player(BaseModel): """A player profile in Clash Royale.""" def _update_attributes(self, data): #: Unique player tag. self.tag = self._get_attribute(data, 'tag') #: In-game name, aka username self.name = self._get_attribute(data, 'name') #: Current trophies self.trophies = self._get_attribute(data, 'trophies') #: name change option self.name_changed = self._get_attribute(data, 'nameChanged') #: global rank self.global_rank = self._get_attribute(data, 'globalRank') #: ---------- #: Clan # self.clan = self._get_attribute(player, 'clan') self.clan = PlayerClan(data=self._get_attribute(data, 'clan')) #: Not in clan self.not_in_clan = self.clan is None #: Clan name self.clan_name = 'No Clan' #: Clan tag self.clan_tag = None #: Clan role self.clan_role = 'N/A' self.clan_name = self.clan.name self.clan_tag = self.clan.tag self.clan_role = self.clan.role self.badge = Badge(data=self._get_attribute(data, 'badge')) #: Arena self.arena = Arena(data=self._get_attribute(data, 'arena')) #: Experience self.experience = PlayerExperience(data=self._get_attribute(data, 'experience')) #: Stats self.stats = PlayerStats(data=self._get_attribute(data, 'stats')) #: Games self.games = PlayerGames(data=self._get_attribute(data, 'games')) #: Chests self.chest_cycle = PlayerChestCycle(data=self._get_attribute(data, 'chestCycle')) #: Shop offers self.shop_offers = PlayerShopOffers(data=self._get_attribute(data, 'shopOffers')) #: Deck self.deck = Deck(data=self._get_attribute(data, 'currentDeck')) PKаIK` :: cr_api-0.5.dist-info/LICENSE.txtMIT License Copyright (c) 2017 SML Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK!H١Wdcr_api-0.5.dist-info/WHEEL HM K-*ϳR03rOK-J,/RH,Q0343 /, (-JLR()*M ILR(4KM̫#DPK!Hcr_api-0.5.dist-info/METADATA=0DR5ғDD͂6lS^Pu޼0>QLuF荆:>08U=Qá…Fl #`"(N&/ L[9hvfzV*b3dEjSZ-E eA(%GxteePK!Hrp/ecr_api-0.5.dist-info/RECORDuǒ`}_ tdXQ9 ^ԌUsO;}E[Lo-P5^jVd)RyM;Ym"R" !>RTI;8i,hG։GqBX2惌 ,$5J gXsK>\8~*<!.NOs$zM2rh$13ey3`ώ!x`G&0}Ŭʓ |z#qnԍqyӢ:h_IśZ[_h2]кtp:dX{)h$ٌQrkJAO5qh(yCw<]Z=7]rε,Â%jLOEHTP!&7-_<J[ԟOjۀoぐB` .reN?g\&hK9^:=IMcҒ<:>ج{ 6t*1Й?Oџa[yIX10ʜ;mE ӸP^;1BwI&gM6-Qsڵ0I>V;tOB\J\C܂3=}PK[Kvncr_api/__init__.pyPKYKNCª cr_api/client.pyPKYKBԞcr_api/exceptions.pyPKcYKebiivcr_api/models/__init__.pyPKXKXa a cr_api/models/base.pyPKaYKT*cr_api/models/clan.pyPKhYKNOZ  ,cr_api/models/constants.pyPKuhYKFg"0cr_api/models/core.pyPKMXKAa$$Dcr_api/models/player.pyPKаIK` :: pVcr_api-0.5.dist-info/LICENSE.txtPK!H١WdZcr_api-0.5.dist-info/WHEELPK!Hw[cr_api-0.5.dist-info/METADATAPK!Hrp/eU\cr_api-0.5.dist-info/RECORDPK ^