├── .gitignore ├── LICENSE ├── README.md ├── pfaw ├── __init__.py ├── constants.py ├── core.py ├── objects.py └── utils.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | 103 | #ds store 104 | .DS_Store 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Nicolas Kenner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Python Fortnite API Wrapper 2 | 3 | [![Discord](https://img.shields.io/discord/430802154022895616.svg?logo=discord)](https://discord.gg/AEfWXP9) 4 | [![PyPI](https://img.shields.io/pypi/v/fortnite.svg)](https://pypi.org/project/fortnite/) 5 | 6 | ## Installation 7 | ```bash 8 | pip install fortnite 9 | ``` 10 | 11 | ## Usage 12 | 13 | ### Setup 14 | [Obtaining fortnite and launcher tokens](https://gist.github.com/Douile/67daa69b59255bcdc390025053dbe295) 15 | Looking for an asynchronous client? Check out [JakeMakesStuff/aiofortnite](https://github.com/JakeMakesStuff/aiofortnite) 16 | ```python 17 | from pfaw import Fortnite, Platform, Mode 18 | 19 | fortnite = Fortnite(fortnite_token='FORTNITE_TOKEN', launcher_token='LAUNCHER_TOKEN', 20 | password='PASSWORD', email='EMAIL') 21 | ``` 22 | 23 | ### Player 24 | Return an object containing the attributes name and id. 25 | ```python 26 | player = fortnite.player(username='Smitty Werbenjagermanjensen') 27 | 28 | print(player.name) 29 | print(player.id) 30 | 31 | # prints: 32 | # Smitty Werbenjagermanjensen 33 | # 9c9212603304472d831c03d0978d2bc1 34 | ``` 35 | 36 | ### Battle Royale Stats 37 | Creates an object containing various stats for a given player. 38 | ```python 39 | stats = fortnite.battle_royale_stats(username='Smitty Werbenjagermanjensen', platform=Platform.pc) 40 | 41 | print(f'Solo Wins: {stats.solo.wins}') 42 | print(f'Duo Wins: {stats.duo.wins}') 43 | print(f'Squad Wins: {stats.squad.wins}') 44 | print(f'Lifetime Wins: {stats.all.wins}') 45 | 46 | 47 | # prints: 48 | # Solo Wins: 1051 49 | # Duo Wins: 1005 50 | # Squad Wins: 210 51 | # Lifetime Wins: 2266 52 | ``` 53 | 54 | ### Server Status 55 | Check the status of the Fortnite servers. Return True if up or False if down. 56 | ```python 57 | status = fortnite.server_status() 58 | 59 | if status: 60 | print('Servers are UP!') 61 | else: 62 | print('Servers are DOWN.') 63 | ``` 64 | 65 | ### Friends 66 | Return a list of player IDs 67 | ```python 68 | smittys_pals = fortnite.friends(username='Smitty Werbenjagermanjensen') 69 | 70 | for friend in smittys_pals: 71 | print(friend) 72 | ``` 73 | 74 | ### News 75 | Return an object containing the attributes common, br, and login. 76 | ```python 77 | news = fortnite.news() 78 | 79 | for br_news in news.br: 80 | print(br_news.image) 81 | print(br_news.title) 82 | print(br_news.body) 83 | ``` 84 | 85 | ### Store 86 | ```python 87 | store = fortnite.store() 88 | 89 | print(store.refresh_interval_hrs) 90 | print(store.daily_purchase_hrs) 91 | print(store.expiration) 92 | 93 | for front in store.storefronts: 94 | print(front.name) 95 | 96 | for entry in front.catalog_entries: 97 | print(entry.offer_id) 98 | print(entry.dev_name) 99 | print(entry.offer_type) 100 | print(entry.title) 101 | print(entry.description) 102 | print(entry.refundable) 103 | 104 | for price in entry.prices: 105 | print(price.currency_type) 106 | print(price.regular_price) 107 | print(price.final_price) 108 | print(price.sale_expiration) 109 | print(price.base_price) 110 | ``` 111 | 112 | ### Leaderboard 113 | ```Python 114 | leaderboard = fortnite.leaderboard(count=10, platform=Platform.pc, mode=Mode.solo) 115 | 116 | for player in leaderboard: 117 | print(f'{player.id} - {player.name} - {player.rank} - {player.value}') 118 | 119 | # prints: 120 | # 385c4d9ab7e3498db533ff4d2d9f4c5b - twitch_bogdanakh - 1 - 909 121 | # 155234bbadaa4e8199a7b2d413722290 - TwitchTV.lavak3_ - 2 - 805 122 | # c083d2200d654b25a87c0c48cb76c902 - Agares29_Twitch - 3 - 781 123 | # 0041d08bedc548d9a2230c4a28550594 - Myboosting.com2 - 4 - 736 124 | # 6f5c77adef1c4e47bc33f1f0c8b4b263 - Twitch_DutchHawk - 5 - 728 125 | # e94c3e05284443398803285171550b45 - twitchtvLIKANDOO - 6 - 675 126 | # 13b3c77420da4101a213e1f646b316a9 - Twitch APEXENITH - 7 - 675 127 | # b94176db4c254f9099fb2bd8e8ae0f94 - VaxitylolMIXERtv - 8 - 624 128 | # cfd16ec54126497ca57485c1ee1987dc - SypherPK - 9 - 608 129 | # a9467569462d4149bc438550c03a45c9 - RuralKTmixer.com - 10 - 607 130 | ``` 131 | 132 | ## Contributors 133 | A thank you to those who have helped out with this project. 134 | 135 | - Tom ([@Douile](https://github.com/Douile)) 136 | -------------------------------------------------------------------------------- /pfaw/__init__.py: -------------------------------------------------------------------------------- 1 | from .core import Fortnite 2 | from .constants import Platform, Mode 3 | -------------------------------------------------------------------------------- /pfaw/constants.py: -------------------------------------------------------------------------------- 1 | token = 'https://account-public-service-prod03.ol.epicgames.com/account/api/oauth/token' 2 | exchange = 'https://account-public-service-prod03.ol.epicgames.com/account/api/oauth/exchange' 3 | player = 'https://persona-public-service-prod06.ol.epicgames.com/persona/api/public/account/lookup?q={}' 4 | battle_royale = 'https://fortnite-public-service-prod11.ol.epicgames.com/fortnite/api/stats/accountId/{}/bulk/window/alltime' 5 | status = 'https://lightswitch-public-service-prod06.ol.epicgames.com/lightswitch/api/service/bulk/status?serviceId=Fortnite' 6 | friends = 'https://friends-public-service-prod06.ol.epicgames.com/friends/api/public/friends/{}' 7 | store = 'https://fortnite-public-service-prod11.ol.epicgames.com/fortnite/api/storefront/v2/catalog?rvn={}' 8 | news = 'https://fortnitecontent-website-prod07.ol.epicgames.com/content/api/pages/fortnite-game' 9 | patch_notes = 'https://www.epicgames.com/fortnite/api/blog/getPosts' 10 | blog = 'https://www.epicgames.com/fortnite/{}/news/{}' 11 | leaderboard = 'https://fortnite-public-service-prod11.ol.epicgames.com/fortnite/api/leaderboards/type/global/stat/br_placetop1_{}_m0{}/window/weekly' 12 | account = 'https://account-public-service-prod03.ol.epicgames.com/account/api/public/account?accountId={}' 13 | 14 | 15 | class Platform: 16 | pc = 'pc' 17 | ps4 = 'ps4' 18 | xb1 = 'xb1' 19 | 20 | 21 | class Mode: 22 | solo = '_p2' 23 | duo = '_p10' 24 | squad = '_p9' 25 | -------------------------------------------------------------------------------- /pfaw/core.py: -------------------------------------------------------------------------------- 1 | from . import constants, objects, utils 2 | from datetime import datetime, timedelta 3 | import requests 4 | import threading 5 | 6 | 7 | class Fortnite: 8 | def __init__(self, fortnite_token, launcher_token, password, email): 9 | password_response = requests.post(constants.token, headers={'Authorization': 'basic {}'.format(launcher_token)}, 10 | data={'grant_type': 'password', 'username': '{}'.format(email), 11 | 'password': '{}'.format(password), 'includePerms': True}).json() 12 | access_token = password_response.get('access_token') 13 | 14 | exchange_response = requests.get(constants.exchange, 15 | headers={'Authorization': 'bearer {}'.format(access_token)}).json() 16 | code = exchange_response.get('code') 17 | 18 | token_response = requests.post(constants.token, headers={'Authorization': 'basic {}'.format(fortnite_token)}, 19 | data={'grant_type': 'exchange_code', 'exchange_code': '{}'.format(code), 20 | 'includePerms': True, 'token_type': 'egl'}).json() 21 | 22 | access_token = token_response.get('access_token') 23 | refresh_token = token_response.get('refresh_token') 24 | expires_at = utils.convert_iso_time(token_response.get('expires_at')) 25 | self.session = Session(access_token, refresh_token, expires_at, fortnite_token) 26 | 27 | def player(self, username): 28 | """Return object containing player name and id""" 29 | response = self.session.get(constants.player.format(username)) 30 | return objects.Player(response) 31 | 32 | def battle_royale_stats(self, username, platform): 33 | """Return object containing Battle Royale stats""" 34 | player_id = self.player(username).id 35 | response = self.session.get(constants.battle_royale.format(player_id)) 36 | return objects.BattleRoyale(response=response, platform=platform) 37 | 38 | def friends(self, username): 39 | """Return list of player ids. This method only works for the authenticated account.""" 40 | player_id = self.player(username).id 41 | response = self.session.get(constants.friends.format(player_id)) 42 | return [friend.get('accountId') for friend in response] 43 | 44 | def store(self, rw=-1): 45 | """Return current store items. This method only works for the authenticated account.""" 46 | response = self.session.get(constants.store.format(rw)) 47 | return objects.Store(response) 48 | 49 | def leaderboard(self, count=50, platform=constants.Platform.pc, mode=constants.Mode.solo): 50 | """Return list of leaderboard objects. Object attributes include id, name, rank, and value.""" 51 | params = {'ownertype': '1', 'itemsPerPage': count} 52 | leaderboard_response = self.session.post(endpoint=constants.leaderboard.format(platform, mode), params=params) 53 | for entry in leaderboard_response.get('entries'): 54 | for key, value in entry.items(): 55 | if key == 'accountId': 56 | entry[key] = value.replace('-', '') 57 | account_ids = '&accountId='.join([entry.get('accountId') for entry in leaderboard_response.get('entries')]) 58 | account_response = self.session.get(endpoint=constants.account.format(account_ids)) 59 | players = [] 60 | for player in leaderboard_response.get('entries'): 61 | for account in account_response: 62 | if player.get('accountId') == account.get('id'): 63 | players.append({'id': player.get('accountId'), 'name': account.get('displayName'), 64 | 'value': player.get('value'), 'rank': player.get('rank')}) 65 | return [objects.Leaderboard(player) for player in players] 66 | 67 | @staticmethod 68 | def news(): 69 | """Get the current news on fortnite.""" 70 | response = Session.get_noauth(endpoint=constants.news, headers={'Accept-Language': 'en'}) 71 | return objects.News(response=response) 72 | 73 | @staticmethod 74 | def server_status(): 75 | """Check the status of the Fortnite servers. Returns True if up and False if down.""" 76 | response = Session.get_noauth(endpoint=constants.status) 77 | if response[0]['status'] == 'UP': 78 | return True 79 | else: 80 | return False 81 | 82 | @staticmethod 83 | def patch_notes(posts_per_page=5, offset=0, locale='en-US', category='patch notes'): 84 | """Get a list of recent patch notes for fortnite. Can return other blogs from epicgames.com""" 85 | params = {'category': category, 'postsPerPage': posts_per_page, 'offset': offset, 'locale': locale} 86 | response = Session.get_noauth(endpoint=constants.patch_notes, params=params) 87 | return objects.PatchNotes(status=response.status_code, response=response) 88 | 89 | 90 | class Session: 91 | def __init__(self, access_token, refresh_token, expires_at, fortnite_token): 92 | self.refresh_token = refresh_token 93 | self.expires_at = expires_at 94 | self.fortnite_token = fortnite_token 95 | self.session = requests.Session() 96 | self.session.headers.update({'Authorization': 'bearer {}'.format(access_token)}) 97 | 98 | # Check if the access token needs to be updated 99 | def check_token(): 100 | threading.Timer(20.0, check_token).start() 101 | now = datetime.utcnow() 102 | if self.expires_at < (now - timedelta(seconds=60)): 103 | print('Token Refresh') 104 | self.refresh() 105 | 106 | check_token() 107 | 108 | def refresh(self): 109 | response = requests.post(constants.token, headers={'Authorization': 'basic {}'.format(self.fortnite_token)}, 110 | data={'grant_type': 'refresh_token', 'refresh_token': '{}'.format(self.refresh_token), 111 | 'includePerms': True}).json() 112 | access_token = response.get('access_token') 113 | self.session.headers.update({'Authorization': 'bearer {}'.format(access_token)}) 114 | self.refresh_token = response.get('refresh_token') 115 | self.expires_at = utils.convert_iso_time(response.get('expires_at')) 116 | 117 | def get(self, endpoint, params=None, headers=None): 118 | response = self.session.get(endpoint, params=params, headers=headers) 119 | if response.status_code != 200: 120 | response.raise_for_status() 121 | return response.json() 122 | 123 | def post(self, endpoint, params=None, headers=None): 124 | response = self.session.post(endpoint, params=params, headers=headers) 125 | if response.status_code != 200: 126 | response.raise_for_status() 127 | return response.json() 128 | 129 | @staticmethod 130 | def get_noauth(endpoint, params=None, headers=None): 131 | response = requests.get(endpoint, params=params, headers=headers) 132 | if response.status_code != 200: 133 | response.raise_for_status() 134 | return response.json() 135 | 136 | @staticmethod 137 | def post_noauth(endpoint, params=None, headers=None): 138 | response = requests.post(endpoint, params=params, headers=headers) 139 | if response.status_code != 200: 140 | response.raise_for_status() 141 | return response.json() 142 | -------------------------------------------------------------------------------- /pfaw/objects.py: -------------------------------------------------------------------------------- 1 | from . import constants, utils 2 | 3 | 4 | class Base: 5 | def __init__(self, response): 6 | self.response = response 7 | 8 | 9 | class Player(Base): 10 | def __init__(self, response): 11 | super().__init__(response) 12 | self.name = self.response.get('displayName') 13 | self.id = self.response.get('id') 14 | 15 | 16 | class BattleRoyale(Base): 17 | def __init__(self, response, platform): 18 | super().__init__(response) 19 | self.solo = BattleRoyaleStats(response=self.response, platform=platform, mode='_p2') 20 | self.duo = BattleRoyaleStats(response=self.response, platform=platform, mode='_p10') 21 | self.squad = BattleRoyaleStats(response=self.response, platform=platform, mode='_p9') 22 | self.all = BattleRoyaleStats(response=self.response, platform=platform, mode='_p') 23 | 24 | 25 | class BattleRoyaleStats(Base): 26 | def __init__(self, response, platform, mode): 27 | super().__init__(response) 28 | self.score = 0 29 | self.matches = 0 30 | self.time = 0 31 | self.kills = 0 32 | self.wins = 0 33 | self.top3 = 0 34 | self.top5 = 0 35 | self.top6 = 0 36 | self.top10 = 0 37 | self.top12 = 0 38 | self.top25 = 0 39 | for stat in self.response: 40 | if platform in stat.get('name') and mode in stat.get('name'): 41 | if 'score_' in stat.get('name'): 42 | self.score += stat.get('value') 43 | elif 'matchesplayed_' in stat.get('name'): 44 | self.matches += stat.get('value') 45 | elif 'minutesplayed_' in stat.get('name'): 46 | self.time += stat.get('value') 47 | elif 'kills_' in stat.get('name'): 48 | self.kills += stat.get('value') 49 | elif 'placetop1_' in stat.get('name'): 50 | self.wins += stat.get('value') 51 | elif 'placetop3_' in stat.get('name'): 52 | self.top3 += stat.get('value') 53 | elif 'placetop5_' in stat.get('name'): 54 | self.top5 += stat.get('value') 55 | elif 'placetop6_' in stat.get('name'): 56 | self.top6 += stat.get('value') 57 | elif 'placetop10_' in stat.get('name'): 58 | self.top10 += stat.get('value') 59 | elif 'placetop12_' in stat.get('name'): 60 | self.top12 += stat.get('value') 61 | elif 'placetop25_' in stat.get('name'): 62 | self.top25 += stat.get('value') 63 | 64 | 65 | class Store(Base): 66 | def __init__(self, response): 67 | super().__init__(response) 68 | self.refresh_interval_hrs = self.response.get('refreshIntervalHrs') 69 | self.daily_purchase_hrs = self.response.get('dailyPurchaseHrs') 70 | self.expiration = self.response.get('expiration') 71 | self.storefronts = self.storefront_list() 72 | 73 | def storefront_list(self): 74 | return [StoreFront(response) for response in self.response.get('storefronts')] 75 | 76 | 77 | class StoreFront(Base): 78 | def __init__(self, response): 79 | super().__init__(response) 80 | self.name = self.response.get('name') 81 | self.catalog_entries = self.catalog_entry_list() 82 | 83 | def catalog_entry_list(self): 84 | return [CatalogEntry(response) for response in self.response.get('catalogEntries')] 85 | 86 | 87 | class CatalogEntry(Base): 88 | def __init__(self, response): 89 | super().__init__(response) 90 | self.offer_id = self.response.get('offerId') 91 | self.dev_name = self.response.get('devName') 92 | self.offer_type = self.response.get('offerType') 93 | self.prices = self.price_list() 94 | self.title = self.response.get('title') 95 | self.description = self.response.get('description') 96 | self.refundable = self.response.get('refundable') 97 | 98 | def price_list(self): 99 | return [Price(response) for response in self.response.get('prices')] 100 | 101 | 102 | class Price(Base): 103 | def __init__(self, response): 104 | super().__init__(response) 105 | self.currency_type = self.response.get('currencyType') 106 | self.regular_price = self.response.get('regularPrice') 107 | self.final_price = self.response.get('finalPrice') 108 | self.sale_expiration = self.response.get('saleExpiration') 109 | self.base_price = self.response.get('basePrice') 110 | 111 | 112 | class News(Base): 113 | def __init__(self, response): 114 | super().__init__(response) 115 | 116 | common = response.get('athenamessage').get('overrideablemessage') 117 | if common.get('message') is not None: 118 | self.common = [NewsMessage(common.get('message'))] 119 | elif common.get('messages') is not None: 120 | self.common = self.message_list(common) 121 | else: 122 | self.common = None 123 | 124 | br = response.get('battleroyalenews').get('news') 125 | if br.get('message') is not None: 126 | self.br = [NewsMessage(br.get('message'))] 127 | elif br.get('messages') is not None: 128 | self.br = self.message_list(br) 129 | else: 130 | self.br = None 131 | 132 | login = response.get('loginmessage').get('loginmessage') 133 | if login.get('message') is not None: 134 | self.login = [NewsMessage(login.get('message'))] 135 | elif login.get('messages') is not None: 136 | self.login = self.message_list(login) 137 | else: 138 | self.login = None 139 | 140 | def message_list(self, messages): 141 | return [NewsMessage(response) for response in messages.get('messages')] 142 | 143 | 144 | class NewsMessage(Base): 145 | def __init__(self, response): 146 | super().__init__(response) 147 | self.image = self.response.get('image') 148 | self.title = self.response.get('title') 149 | self.body = self.response.get('body') 150 | 151 | 152 | class PatchNotes(Base): 153 | def __init__(self, status, response): 154 | super().__init__(response) 155 | self.status = status 156 | 157 | self.post_count = response.get('postCount') 158 | self.increment_count = response.get('incrementCount') 159 | self.total_blogs = response.get('blogTotal') 160 | self.totals = CategoryTotals(response.get('categoryTotals')) 161 | self.blogs = self.blog_list() 162 | 163 | def blog_list(self): 164 | return [Blog(response) for response in self.response.get('blogList')] 165 | 166 | 167 | class CategoryTotals: 168 | def __init__(self, data): 169 | self.community = data.get('community') 170 | self.events = data.get('events') 171 | self.patch_notes = data.get('patch_notes') 172 | self.announcements = data.get('announcements') 173 | self.all = data.get('all') 174 | 175 | 176 | class Blog: 177 | def __init__(self, data): 178 | self.trending = data.get('trending') 179 | self.no_top_image = data.get('noTopImage') 180 | self.image = data.get('image') 181 | self.author = data.get('author') 182 | self.share_image = data.get('shareImage') 183 | self.title = data.get('title') 184 | self.html_content = data.get('content') 185 | self.trending_image = data.get('trendingImage') 186 | self.category = data.get('cat') 187 | self.html_short = data.get('short') 188 | self.featured = data.get('featured') 189 | self.date = data.get('date') 190 | if self.date is not None: 191 | self.date = utils.convert_iso_time(self.date) 192 | self.id = data.get('_id') 193 | self.slug = data.get('slug') 194 | self.locale = data.get('locale') 195 | self.categories = data.get('category') 196 | self.tags = data.get('tags') 197 | if self.slug is not None and self.locale is not None: 198 | self.url = constants.blog.format(self.locale, self.slug) 199 | else: 200 | self.url = None 201 | 202 | 203 | class Leaderboard(Base): 204 | def __init__(self, response): 205 | super().__init__(response) 206 | self.id = response.get('id') 207 | self.name = response.get('name') 208 | self.rank = response.get('rank') 209 | self.value = response.get('value') 210 | -------------------------------------------------------------------------------- /pfaw/utils.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | 4 | def convert_iso_time(isotime): 5 | """Will convert an isotime (string) to datetime.datetime""" 6 | return datetime.strptime(isotime, '%Y-%m-%dT%H:%M:%S.%fZ') 7 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | from codecs import open 3 | from os import path 4 | 5 | here = path.abspath(path.dirname(__file__)) 6 | 7 | with open(path.join(here, 'README.md'), encoding='utf-8') as f: 8 | long_description = f.read() 9 | 10 | setup( 11 | name='fortnite', 12 | version='0.0.8', 13 | description='The Python Fortnite API Wrapper', 14 | long_description=long_description, 15 | long_description_content_type='text/markdown', 16 | url='https://github.com/nicolaskenner/python-fortnite-api-wrapper', 17 | author='Nicolas Kenner', 18 | author_email='nick@nicolaskenner.com', 19 | classifiers=[ 20 | 'Development Status :: 3 - Alpha', 21 | 'Intended Audience :: Developers', 22 | 'License :: OSI Approved :: MIT License', 23 | 'Programming Language :: Python :: 3' 24 | ], 25 | packages=find_packages(exclude=['contrib', 'docs', 'tests']), 26 | install_requires=['requests >= 2.20.0'] 27 | ) --------------------------------------------------------------------------------