|
|
|
@ -3,85 +3,76 @@ import os
@@ -3,85 +3,76 @@ import os
|
|
|
|
|
import errno |
|
|
|
|
import requests |
|
|
|
|
import json |
|
|
|
|
import datetime |
|
|
|
|
|
|
|
|
|
""" |
|
|
|
|
https://develop.battle.net/documentation/guides/regionality-and-apis |
|
|
|
|
""" |
|
|
|
|
regions = [ |
|
|
|
|
{'code': 'us', 'locales': ['en_US', 'es_MX', 'pt_BR']}, |
|
|
|
|
{'code': 'eu', 'locales': ['en_GB', 'es_ES', 'fr_FR', 'ru_RU', 'de_DE', 'pt_PT', 'it_IT']}, |
|
|
|
|
{'code': 'kr', 'locales': ['ko_KR']}, |
|
|
|
|
{'code': 'tw', 'locales': ['zh_TW']}, |
|
|
|
|
#{'code': 'cn', 'locales': ['zh_CN']} |
|
|
|
|
] |
|
|
|
|
import config |
|
|
|
|
import blizzard |
|
|
|
|
import spec |
|
|
|
|
|
|
|
|
|
""" |
|
|
|
|
https://develop.battle.net/documentation/world-of-warcraft/game-data-apis |
|
|
|
|
""" |
|
|
|
|
apis = [ |
|
|
|
|
{'path': '/data/wow/achievement-category/index', 'namespaces': ['static']}, |
|
|
|
|
{'path': '/data/wow/achievement/index', 'namespaces': ['static']}, |
|
|
|
|
{'path': '/data/wow/connected-realm/index', 'namespaces': ['dynamic']}, |
|
|
|
|
{'path': '/data/wow/covenant/index', 'namespaces': ['static']}, |
|
|
|
|
{'path': '/data/wow/covenant/soulbind/index', 'namespaces': ['static']}, |
|
|
|
|
{'path': '/data/wow/covenant/conduit/index', 'namespaces': ['static']}, |
|
|
|
|
{'path': '/data/wow/creature-family/index', 'namespaces': ['static']}, |
|
|
|
|
{'path': '/data/wow/creature-type/index', 'namespaces': ['static']}, |
|
|
|
|
{'path': '/data/wow/item-class/index', 'namespaces': ['static']}, |
|
|
|
|
{'path': '/data/wow/item-set/index', 'namespaces': ['static']}, |
|
|
|
|
{'path': '/data/wow/journal-expansion/index', 'namespaces': ['static']}, |
|
|
|
|
{'path': '/data/wow/journal-encounter/index', 'namespaces': ['static']}, |
|
|
|
|
{'path': '/data/wow/journal-instance/index', 'namespaces': ['static']}, |
|
|
|
|
{'path': '/data/wow/modified-crafting/index', 'namespaces': ['static']}, |
|
|
|
|
{'path': '/data/wow/modified-crafting/category/index', 'namespaces': ['static']}, |
|
|
|
|
{'path': '/data/wow/modified-crafting/reagent-slot-type/index', 'namespaces': ['static']}, |
|
|
|
|
{'path': '/data/wow/mount/index', 'namespaces': ['static']}, |
|
|
|
|
{'path': '/data/wow/keystone-affix/index', 'namespaces': ['static']}, |
|
|
|
|
{'path': '/data/wow/mythic-keystone/dungeon/index', 'namespaces': ['dynamic']}, |
|
|
|
|
{'path': '/data/wow/mythic-keystone/index', 'namespaces': ['dynamic']}, |
|
|
|
|
{'path': '/data/wow/mythic-keystone/period/index', 'namespaces': ['dynamic']}, |
|
|
|
|
] |
|
|
|
|
def log(*data): |
|
|
|
|
print(datetime.datetime.now(), '|', *data) |
|
|
|
|
|
|
|
|
|
credentials = open('.credentials').read().strip().split(':') |
|
|
|
|
cache = './cache' |
|
|
|
|
class Updater(object): |
|
|
|
|
def __init__(self): |
|
|
|
|
self.http = requests.Session() |
|
|
|
|
self.credentials = blizzard.get_credentials(config.pwd) |
|
|
|
|
|
|
|
|
|
def getApiHost(region): |
|
|
|
|
return f"{region}.api.blizzard.com" if region != 'cn' else 'gateway.battlenet.com.cn' |
|
|
|
|
def region_oauth(self, region: str) -> dict: |
|
|
|
|
api = self.http.post(f"{blizzard.get_bnet_host(region)}/oauth/token", data={'grant_type': 'client_credentials'}, auth=self.credentials) |
|
|
|
|
oauth = api.json() |
|
|
|
|
#log(region, oauth) |
|
|
|
|
api.raise_for_status() |
|
|
|
|
return oauth |
|
|
|
|
|
|
|
|
|
def getBnetHost(region): |
|
|
|
|
region = region if region not in ['tw', 'kr'] else 'apac' |
|
|
|
|
return f"{region}.battle.net" if region != 'cn' else 'www.battlenet.com.cn' |
|
|
|
|
def api_call(self, url: str) -> requests.Response: |
|
|
|
|
api = self.http.get(url) #, headers={'Authorization': f"Authorization: Token {access_token}"}) |
|
|
|
|
api.raise_for_status() |
|
|
|
|
return api |
|
|
|
|
|
|
|
|
|
s = requests.Session() |
|
|
|
|
def create_dst(self, dst: str): |
|
|
|
|
try: |
|
|
|
|
os.makedirs(os.path.dirname(dst)) |
|
|
|
|
except OSError as e: |
|
|
|
|
if e.errno != errno.EEXIST: |
|
|
|
|
raise |
|
|
|
|
|
|
|
|
|
for region in regions: |
|
|
|
|
try: |
|
|
|
|
r = s.post(f"https://{getBnetHost(region['code'])}/oauth/token", data={'grant_type': 'client_credentials'}, auth=(credentials[0], credentials[1])) |
|
|
|
|
oauth = r.json() |
|
|
|
|
#print(region, oauth) |
|
|
|
|
r.raise_for_status() |
|
|
|
|
except requests.exceptions.HTTPError as e: |
|
|
|
|
print(region, e) |
|
|
|
|
continue |
|
|
|
|
def save(self, dst, content): |
|
|
|
|
with open(dst, 'w+') as f: |
|
|
|
|
f.write(json.dumps(content.json(), indent=4)) |
|
|
|
|
|
|
|
|
|
for api in apis: |
|
|
|
|
for locale in region['locales']: |
|
|
|
|
for namespace in api['namespaces']: |
|
|
|
|
try: |
|
|
|
|
url = f"https://{getApiHost(region['code'])}{api['path']}?namespace={namespace}-{region['code']}&locale={locale}&access_token={oauth['access_token']}" |
|
|
|
|
r = s.get(url) #, headers={'Authorization': f"Authorization: Token {oauth['access_token']}"}) |
|
|
|
|
#print(r.request.headers, r.status_code, r.reason, r.headers, url) |
|
|
|
|
print(region['code'], locale, r.status_code, api['path']) |
|
|
|
|
r.raise_for_status() |
|
|
|
|
def iterate_index(self): |
|
|
|
|
for region in spec.regions: |
|
|
|
|
# access token for region |
|
|
|
|
try: |
|
|
|
|
oauth = self.region_oauth(region['code']) |
|
|
|
|
access_token = oauth['access_token'] |
|
|
|
|
except (requests.exceptions.HTTPError, KeyError) as e: |
|
|
|
|
log(region, type(e), e) |
|
|
|
|
continue |
|
|
|
|
|
|
|
|
|
dst = f"{cache}/{region['code']}/{locale}/{api['path'].replace('/', '_')}.{namespace}.json" |
|
|
|
|
try: |
|
|
|
|
os.makedirs(os.path.dirname(dst)) |
|
|
|
|
except OSError as e: |
|
|
|
|
if e.errno != errno.EEXIST: |
|
|
|
|
raise |
|
|
|
|
|
|
|
|
|
open(dst, 'w+').write(json.dumps(r.json(), indent=4)) |
|
|
|
|
except requests.exceptions.HTTPError as e: |
|
|
|
|
print(e) |
|
|
|
|
# loop every api |
|
|
|
|
for api in spec.apis: |
|
|
|
|
if not 'index' in api or not api['index']: |
|
|
|
|
continue |
|
|
|
|
|
|
|
|
|
for locale in region['locales']: |
|
|
|
|
# retail or classic |
|
|
|
|
for namespace in api['namespaces']: |
|
|
|
|
try: |
|
|
|
|
dst = f"{config.raw}/{region['code']}/{locale}/{api['path'].replace('/', '_')}.{namespace}.json" |
|
|
|
|
|
|
|
|
|
url = f"{blizzard.get_api_host(region['code'])}{api['path']}" |
|
|
|
|
url += f"?namespace={namespace}-{region['code']}&locale={locale}&access_token={access_token}" |
|
|
|
|
response = self.api_call(url) |
|
|
|
|
|
|
|
|
|
self.create_dst(dst) |
|
|
|
|
self.save(dst, response) |
|
|
|
|
|
|
|
|
|
except requests.exceptions.HTTPError as e: |
|
|
|
|
log(e) |
|
|
|
|
continue |
|
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
|
updater = Updater() |
|
|
|
|
updater.iterate_index() |
|
|
|
|
#updater.iterate_links() |
|
|
|
|