154 lines
4.9 KiB
Python
154 lines
4.9 KiB
Python
import datetime
|
|
import time
|
|
import boto3
|
|
import json
|
|
import os
|
|
import requests
|
|
|
|
|
|
local_region = os.environ['AWS_REGION']
|
|
|
|
dynamo_client = boto3.client('dynamodb', region_name=local_region)
|
|
tables = {
|
|
'retail': {
|
|
'recent': 'wow-token-price-recent',
|
|
'current': 'wow-token-price',
|
|
'timestream': 'wow-token-price-history'
|
|
},
|
|
'classic': {
|
|
'recent': 'wow-token-classic-price-recent',
|
|
'current': 'wow-token-classic-price',
|
|
'timestream': 'wow-token-classic-price-history'
|
|
}
|
|
}
|
|
|
|
|
|
# a lambda handler to handle eventbridge triggered vents
|
|
def lambda_handler(event, context):
|
|
flavors = ['retail', 'classic']
|
|
regions = ['us', 'eu', 'kr', 'tw']
|
|
timestamp = int(datetime.datetime.utcnow().timestamp())
|
|
for flavor in flavors:
|
|
flavor_handler(flavor, regions, timestamp)
|
|
|
|
|
|
def flavor_handler(flavor: str, regions: list, timestamp: int) -> None:
|
|
for region in regions:
|
|
if flavor == 'retail':
|
|
namespace = 'dynamic'
|
|
else:
|
|
namespace = 'dynamic-classic'
|
|
live_price = get_token_price_from_blizzard(region, namespace)
|
|
print(f"{flavor} {region} live price {live_price}")
|
|
print(f'Updating {region.upper()}...')
|
|
update_token_price(flavor, region, timestamp, live_price)
|
|
|
|
|
|
def update_token_price(flavor: str, region: str, current_time_epoch: int, live_price: int) -> None:
|
|
stored_price = get_stored_price(flavor, region)
|
|
print(f'Current live price {live_price}')
|
|
print(f'Current stored price {stored_price}')
|
|
if stored_price != live_price:
|
|
# update the stored price and the recent price
|
|
print(f"Stored price differs from the live price, updating databases")
|
|
update_stored_token_price(flavor, region, live_price, current_time_epoch)
|
|
update_recent_token_price(flavor, region, live_price, current_time_epoch)
|
|
else:
|
|
print(f"Price hasn't changed for {flavor} {region.upper()}")
|
|
|
|
|
|
# update the current price records in DynamoDB
|
|
def update_stored_token_price(flavor: str, region: str, price: int, current_time_epoch: int) -> None:
|
|
dynamo_client.update_item(
|
|
TableName=tables[flavor]['current'],
|
|
Key={
|
|
'region': {
|
|
'S': region
|
|
}
|
|
},
|
|
UpdateExpression='SET price = :p, current_time = :t',
|
|
ExpressionAttributeValues={
|
|
':p': {
|
|
'S': str(price)
|
|
},
|
|
':t': {
|
|
'S': str(current_time_epoch)
|
|
}
|
|
}
|
|
# ReturnValues="UPDATED_NEW"
|
|
)
|
|
print(f'Updated {flavor} {region.upper()} price to {price}')
|
|
|
|
# add a record to the recent token price table in DynamoDB
|
|
def update_recent_token_price(flavor: str, region: str, price: int, current_time_epoch: int) -> None:
|
|
dynamo_client.put_item(
|
|
TableName=tables[flavor]['recent'],
|
|
Item={
|
|
'region': {
|
|
'S': region
|
|
},
|
|
'price': {
|
|
'S': str(price)
|
|
},
|
|
'timestamp': {
|
|
'N': str(current_time_epoch)
|
|
},
|
|
'expire': {
|
|
'N': str(current_time_epoch + (60 * 60 * 24 * 31 * 12))
|
|
}
|
|
}
|
|
# ReturnValues="UPDATED_NEW"
|
|
)
|
|
print(f'Added {region.upper()} price {price} to {tables[flavor]["recent"]} table')
|
|
|
|
# get the current stored token price from dynamodb
|
|
def get_stored_price(flavor: str, region: str) -> int:
|
|
response = dynamo_client.get_item(
|
|
TableName=tables[flavor]['current'],
|
|
Key={
|
|
'region': {
|
|
'S': region
|
|
}
|
|
}
|
|
)
|
|
return int(response['Item']['price']['S'])
|
|
|
|
|
|
def get_combined_live_price(game_flavor: str) -> dict:
|
|
if game_flavor == 'retail':
|
|
namespace = 'dynamic'
|
|
else:
|
|
namespace = 'dynamic-classic'
|
|
|
|
return {
|
|
'us': get_token_price_from_blizzard('us', namespace),
|
|
'eu': get_token_price_from_blizzard('eu', namespace),
|
|
'kr': get_token_price_from_blizzard('kr', namespace),
|
|
'tw': get_token_price_from_blizzard('tw', namespace)
|
|
}
|
|
|
|
|
|
def get_token_price_from_blizzard(region: str, namespace: str) -> int:
|
|
api_endpoint = f'https://{region}.api.blizzard.com/data/wow/token/index'
|
|
params = {'namespace': f'{namespace}-{region}'}
|
|
headers = {'Authorization': f'Bearer {get_oauth_token()}'}
|
|
response = requests.get(api_endpoint, params=params, headers=headers)
|
|
response.raise_for_status()
|
|
return int(response.json()['price'])
|
|
|
|
|
|
def get_oauth_token() -> str:
|
|
url = 'https://us.battle.net/oauth/token'
|
|
payload = {
|
|
'grant_type': 'client_credentials',
|
|
'client_id': os.environ.get('BLIZZARD_CLIENT_ID'),
|
|
'client_secret': os.environ.get('BLIZZARD_CLIENT_SECRET')
|
|
}
|
|
response = requests.post(url, data=payload)
|
|
response.raise_for_status()
|
|
return json.loads(response.text)['access_token']
|
|
|
|
|
|
if __name__ == '__main__':
|
|
lambda_handler(None, None)
|