138 lines
4.4 KiB
Python
138 lines
4.4 KiB
Python
|
|
import os
|
|
import errno
|
|
import requests
|
|
import json
|
|
import datetime
|
|
import tempfile
|
|
|
|
import config
|
|
import blizzard
|
|
import spec
|
|
|
|
|
|
def log(*data):
|
|
print(datetime.datetime.now(), '|', *data)
|
|
|
|
|
|
class Updater(object):
|
|
|
|
def __init__(self):
|
|
self.http = requests.Session()
|
|
self.credentials = blizzard.get_credentials(config.pwd)
|
|
self.last_modified = None
|
|
|
|
|
|
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 api_call(self, region: str, path: str, namespace: str, locale: str, access_token: str, headers=None) -> requests.Response:
|
|
url = f"{blizzard.get_api_host(region)}{path}"
|
|
qs = {'namespace': f"{namespace}-{region}", 'local': locale, 'access_token': access_token}
|
|
|
|
api = self.http.get(url, params=qs, headers=headers) #, headers={'Authorization': f"Authorization: Token {access_token}"})
|
|
api.raise_for_status()
|
|
return api
|
|
|
|
|
|
def create_dst(self, dst: str):
|
|
try:
|
|
os.makedirs(os.path.dirname(dst))
|
|
except OSError as e:
|
|
if e.errno != errno.EEXIST:
|
|
raise
|
|
|
|
|
|
def save_raw(self, region: str, path: str, namespace: str, locale: str, raw: requests.Response):
|
|
dst = f"{config.raw}/{region}/{locale}/{path.replace('/', '_')}.{namespace}.json"
|
|
|
|
self.create_dst(dst)
|
|
|
|
with open(dst, 'w+') as f:
|
|
f.write(json.dumps(raw.json(), indent=2))
|
|
|
|
|
|
def get_last_modified(self, region: str, path: str, namespace: str, locale: str):
|
|
key = f"{region}.{locale}.{path}.{namespace}"
|
|
|
|
# cached!
|
|
if self.last_modified is not None and key in self.last_modified:
|
|
return self.last_modified[key]
|
|
|
|
# not cached, check db again
|
|
db = os.path.join(config.pwd, 'last-modified.db')
|
|
|
|
with open(db, 'r') as f:
|
|
data = json.load(f)
|
|
self.last_modified = data
|
|
|
|
try:
|
|
return data[key]
|
|
except KeyError:
|
|
return None
|
|
|
|
|
|
def save_last_modified(self, region: str, path: str, namespace: str, locale: str, modified: str):
|
|
key = f"{region}.{locale}.{path}.{namespace}"
|
|
db = os.path.join(config.pwd, 'last-modified.db')
|
|
|
|
# never trust the cache, open, modify and save updated data for safety
|
|
with open(db, 'r') as f:
|
|
data = json.load(f)
|
|
|
|
data[key] = modified
|
|
# update cache with new data
|
|
self.last_modified = data
|
|
|
|
# safe write: tmp write and then mv
|
|
fd, tmp_path = tempfile.mkstemp(dir=config.pwd)
|
|
try:
|
|
with os.fdopen(fd, 'w') as tmp:
|
|
json.dump(data, tmp, indent=2)
|
|
finally:
|
|
os.replace(tmp_path, db)
|
|
|
|
|
|
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
|
|
|
|
# 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:
|
|
last_modified = self.get_last_modified(region['code'], api['path'], namespace, locale)
|
|
headers = {'If-Modified-Since': last_modified} if last_modified is not None else None
|
|
|
|
response = self.api_call(region['code'], api['path'], namespace, locale, access_token, headers=headers)
|
|
|
|
if response.status_code == 200:
|
|
self.save_last_modified(region['code'], api['path'], namespace, locale, response.headers['Last-Modified'])
|
|
self.save_raw(region['code'], api['path'], namespace, locale, response)
|
|
|
|
except requests.exceptions.HTTPError as e:
|
|
log(e)
|
|
continue
|
|
|
|
|
|
if __name__ == "__main__":
|
|
updater = Updater()
|
|
updater.iterate_index()
|
|
#updater.iterate_links()
|