Allyn H

Making things, writing code, wasting time...

Tag: WebServer

Creating a Python app for Destiny – Part 8: Displaying the Vault contents.

Introduction:

Big news: I’ve managed to launch my own Destiny based website!!! I’ve been working on an inventory management system for a while now, and following on from my previous blog posts I’ve managed to deploy my work to date as a live website.

You can test it out for yourself here https://www.destinyvaultraider.com once you’ve authorised your account, you can click on Vault, Character or Xur (if he’s around).

The aim of this website is to use it as a live learning tool, where I can continue to develop and add new features. For example, I can display the vault and character inventory but I can’t transfer items yet, so this is a feature I hope to add in the future.

If you’d like to review the previous Destiny API posts, check them out here:

  1. Send a request to read Xurs inventory.
  2. Send a HTML formatted email with Xurs inventory.
  3. Log into Destiny and Bungie.net via your PSN account.
  4. Transfer items from your vault to your character and equip them.
  5. Reading a characters inventory and vault contents.
  6. Creating a Python web server with Flask.
  7. Authenticating our app with Bungie.net OAuth.

You can also find me on Twitter here @Allyn_H_

Flow chart:

This section is quite confusing as I’ll be making a request to Bungie, then decoding that request and changing the format of the response a few times before plugging it into my Flask app, so here’s a quick guide as to how I’m creating the Vault route. Excuse the colours, I’m a representing Future War Cult. You can click on the image to make it bigger.

Future War Cult representing!

Flow chart detailing the steps for creating the vault page.reate vault route:

Create vault route:

In the previous section we created an index route, which was used to display the index.html page. Now that we have authorised the user, we can build on this and add a new route to display the users Vault contents.

To create the Vault route, the code looks like this:

@main.route('/vault')
def vault():
    :
    # Do something...
    :
    return render_template('vault.html') 

Right now, ‘vault.html’ is an empty file that extends from our ‘base.html’ – it will only display our navbar. For a quick refresh on the Flask setup, check out this post: Creating a Python app for Destiny – Part 7: Authenticating our app with Bungie.net OAuth.

Right now the “vault.hmtl” template looks like this:

{% extends "base.html" %}
{% block content %}

{% endblock %}

We are extending from the “base.hmtl” template and our content block is empty. Now we can build on this and add some content.

in order to build our Vault page we will need to do a few things:

  1. Send a request to Bungie.net to get the users account details:
    1. We need the users membershipType, destinyMembershipId and characterId.
  2. Send a request to Bungie.net to get the users Vault contents.
  3. Parse through the users vault contents and take out the data we wish to display.
    • Again, we’ve done this above, we just need to tweak the code a bit.
  4. Categorise and display each item.
    • The JSON response from Bungie doesn’t return the items in any order, so we need to categorise them in order to display them properly.

In order to better categorise the vault items, I created a dictionary of each item category. These categories are stored in the item details returned from the manifest,  as item[‘bucket’]. I’ve split these out as below:

invItems = {
    1 : 'Primary Weapons', 
    2 : 'Special Weapons', 
    3 : 'Heavy Weapons', 
    4 : 'Ghost', 
    5 : 'Helmet', 
    6 : 'Gauntlets', 
    7 : 'Chest Armor', 
    8 : 'Leg Armor', 
    9 : 'Class Armor', 
    10 : 'Artifacts', 
    11 : 'Vehicle', 
    12 : 'Sparrow Horn', 
    13 : 'Ships',
    14 : 'Shaders',
    15 : 'Emblems',
    16 : 'Emotes',
    17 : 'Weapon Bundles',
    18 : 'Materials',
    19 : 'Consumables',
    20 : 'Ornaments',
    }

Each of these categories will be used to create a section in our vault page. We will loop through these categories and create the HTML needed to display each item. First though, we need to get the users vault details…

1: Send a request to Bungie.net to get the users account details:

Once the user has authenticated their account, we can request information about their account.

Send a request to Bungie.net to get the users account details

Once the user has been authenticated by Bungie, we can send a request to Bungie to get the account details of the current user. This will return  some important information like the users PSX / Xbox Live username, membershipType, destinyMembershipId, and all of the users character IDs.

Here’s the code to send the “GetCurrentBungieAccount” request:

def GetCurrentBungieAccount(session):
    req_string = 'https://www.bungie.net/Platform/User/GetCurrentBungieAccount/'
    res = session.get(req_string)
    error_state = res.json()['ErrorStatus'].decode('utf-8')
    return res

There’s nothing there we haven’t seen before. The function takes the authenticated session data as a parameter and returns the JSON response. As we can see, there’s a lot of important account data returned  that will enable us to view and interact with the users account:

Lots of important user data returned here.

The response from getcurrentbungieaccount returns a lot of important account data.

This Endpoint will also give character details, such as race, gender and class, details of the users clan affiliation, the users Grimore and more.

2: Send a request to Bungie.net to get the users Vault details:

Request and Response

Sending a request to Bungie.net to get the users Vault details

Now that we have the users membershipType and destinyMembershipId, we can send a request to Bungie for their vault contents.

def getVault(session, membershipType, destinyMembershipId):
    getVault_url = base_url + membershipType + "/MyAccount/Vault/"
    res = session.get(getVault_url, params={'accountId': destinyMembershipId})
    return res

Again we send a request, with the users membershipType and destinyMembershipId added to the URL as parameters, the the function then returns the JSON response from Bungie.

3: Parse the Vault response and return only the required data:

Now that we have a response from Bungie, we need to convert this into a human readable format. The JSON response from the getVault request (in my case) is 35,147 lines of text… That’s a lot of data, also there is no real human readable data in there, it doesn’t return the item names for example – just a itemHash value. So we now need to strip out each item hash and search the manifest for the human readable format of that data. Again, the response after decrypting these items from the Manifest is 54,150 lines of text, so we’ll need to extract only the lines of code we’re interested in. Each item will be condensed down to 10 pieces of useful information (we can build on this at a later stage if needed).

Parsing manifest and categorising the data.

Decoding the itemHash from the manifest and stripping out the required data.

One of the things we can also do, to make life a little easier for us, is to categorise each item by its “bucketName”  – this is a value stored in the manifest that is used to categorise each item, for example “Primary Weapon”, “Consumables” or “Ghost”. The response from getVault is split into 3 buckets, “Weapons,” “Armor” and “Inventory”. This can be seen when you visit your vault in the tower or on any app.  In our case, we’re going to add them all to 1 page, with the ‘bucketName’ (i.e. “Primary Weapon”, “Chest Armor” or “Consumables”) used to categorise each item.

First, lets create a list of blank dictionary objects with the items we want to display for our vault:

array_size = 0
weapon_list = [{
    'itemReferenceHash': 0,
    'itemId': 0,
    'itemName': '',
    'tierTypeName': '',
    'itemTypeName': '',
    'itemLightLevel ': '',
    'stackSize': 1,
    'icon': '',
    'bucket': '',
    'equipped': '',
} for array_size in range(vaultSize)]
array_size = 0

Now that the list is created, lets loop through each inventory item, decode the values from the manifest and store the data we want to display.

Nested JSON dicts and lists...

The JSON response from getVault shows the itemHash we need to decrypt.

We are also going to query each “itemHash”  from the manifest to get the full item details, you can read back  to part 5 for a refresh on what I’m doing here.

If we look at the JSON response from the getVault request, shown in the picture above, both “buckets” and “items” are lists of nested dictionary items , so we need to loop through “buckets” and then each of the “items”, to get to the items “itemHash”. (You can see they’re lists because of the square brackets “[“, denote the start of the list.)

The code for looping through the nested lists is below:

for bucket in vaultResult.json()['Response']['data']['buckets']:
    for item in bucket['items']:
        weapon_list[array_size]['itemReferenceHash'] = item['itemHash']
        inventoryItem = all_data['DestinyInventoryItemDefinition'][item['itemHash']]
        weapon_list[array_size]['itemName'] = inventoryItem['itemName']

Once we have the “itemHash” we can query the Manifest definition “DestinyInventoryItemDefinition” for the item details. We can then store the information we want to keep in our own list of dictionary items.

Stripping out the important data:

The function parseVault takes 3 parameters; the authorised “session”, the “vaultResult” JSON response and the Manifest data in the format of a Python dictionary as “all_data”. Here’s what the full code for stripping each item from the manifest looks like:

def parseVault(session, vaultResult, all_data):
    for bucket in vaultResult.json()['Response']['data']['buckets']:
        for item in bucket['items']:
            weapon_list[array_size]['itemReferenceHash'] = item['itemHash']
            inventoryItem = all_data['DestinyInventoryItemDefinition'][item['itemHash']]
            weapon_list[array_size]['itemName'] = inventoryItem['itemName']
            weapon_list[array_size]['itemLightLevel'] = item.get('primaryStat', {}).get('value', "")
            if ((inventoryItem['itemName'] != "Classified") and (inventoryItem['itemHash'] != 1826822442)):
                bucketHash = all_data['DestinyInventoryBucketDefinition'][inventoryItem['bucketTypeHash']]
                weapon_list[array_size]['itemName'] = inventoryItem['itemName']
                weapon_list[array_size]['tierTypeName'] = inventoryItem['tierTypeName']
                weapon_list[array_size]['itemTypeName'] = inventoryItem['itemTypeName']
                weapon_list[array_size]['icon'] = "https://www.bungie.net/" + inventoryItem['icon']
                weapon_list[array_size]['bucket'] = bucketHash['bucketName']
            # Classified items won't have this information, if not overwritten can cause fails:
            if ((inventoryItem['itemName'] == "Classified") or (inventoryItem['itemHash'] == 1826822442)):
                weapon_list[array_size]['itemName'] = inventoryItem['itemName']
                weapon_list[array_size]['tierTypeName'] = "Classified"
                weapon_list[array_size]['itemTypeName'] = "Classified"
                weapon_list[array_size]['bucket'] = "Classified"
            array_size += 1 
    return weapon_list

Also note, as I’ve hit a few issues recently with Bungie releasing new items – but forgetting to change them from “Classified” in the Manifest, I’ve added a default value for any items with an “itemName” of “Classified” – this will prevent any errors occurring if Bungie add a Classified item at a later stage.

Populating the vault route with our data:

Now that we have stripped the important data out of the manifest, into a list of dictionary’s,  we can pass this list to the ‘vault’ route in the ‘views.py’ file. As a side note, it would have been possible to populate the Vault route without stripping out this data – but it’s much harder to parse through lists of nested dictionary items in the Jinja2 template than in Python. Also, if we were creating a high traffic site, it’s not good practice to send large files of data every time the user refreshes a webpage.

In the case of our Vault route, this could get to about 5MB per page view… Users on mobile data wouldn’t be happy about that, so we need to cut this down.

Creating the Flask Vault route with data taken from the manifest.

Populating the Vault route in our Flask app with the data stripped from the manifest.

Now that we have our important data stripped out and stored in a list of dictionary objects, we need to pass this dictionary to the render_template function of the vault route as a parameter.  The “invItems” dictionary, containing the item categories is also passed as a parameter.

@main.route('/vault')
def vault():
    userSummary = GetCurrentBungieAccount(oauth_session)
    vault = getVault(oauth_session, user.membershipType, user.destinyMembershipId)
    weaponList = parseVault(oauth_session, vault, all_data)
    return render_template('vault.html',
        weaponList = weaponList, 
        invItems = invItems,
        character = userSummary.json()['Response']['destinyAccounts'][0]['userInfo']['displayName'], 
        characterHash = characterHash, 
        charId = userSummary.json()['Response']['destinyAccounts'][0]['characters'][0]['characterId'],
        lightLevel = userSummary.json()['Response']['destinyAccounts'][0]['characters'][0]['powerLevel'],
        emblemImage = userSummary.json()['Response']['destinyAccounts'][0]['characters'][0]['emblemPath'],
        backgroundImage = userSummary.json()['Response']['destinyAccounts'][0]['characters'][0]['backgroundPath'],
        ) 

The other parameters (character, characterHash, charId, lightLevel, emblemImage and backgroundImage) are all taken from the response from getcurrentbungieaccount() and were shown in the last blog post, Part 7: Authenticating our app with Bungie.net OAuth.

Displaying an item in HTML:

To display each item, I created a HTML template file called “itemBlock.html”

The code is quite basic, 2 <div>’s an <img> and 2 <p>’s. The first <div> sets out a Bootstrap column, col-md-3 will allow 4 images to be tiled, per row, before moving to a new row. For the image, I’m passing a lot of parameters but I’m not actually using them yet, as I haven’t found a tidy way to display all of the information. In the <p> paragraph sections, I’m displaying the itemName, itemLightLevel, tierTypeName and itemTypeName.

Here’s the code in the “itemBlock.html”:

<div class="col-md-3 col-sm-4 col-xs-6">
    <div class="thumbnail">
        <img class="img-responsive med-tile" src="{{ dict_item['icon'] }}" title="{{ dict_item['itemName'] }}" tierTypeName="{{ dict_item['tierTypeName'] }}" itemTypeName="{{ dict_item['itemTypeName'] }}" tier="{{ dict_item['tier'] }}" bucket="{{ dict_item['bucket'] }}" itemLightLevel="{{ dict_item['itemLightLevel'] }}" itemReferenceHash="{{ dict_item['itemReferenceHash'] }}">
        <p>{{ dict_item['itemName'] }}: {{ dict_item['itemLightLevel'] }}</p>
        <p>{{ dict_item['tierTypeName'] }} {{ dict_item['itemTypeName'] }}</p>
    </div>
</div>

Here’s what that will look like when displayed:

WOOT Fragment of the Prime!

Here’s what the output from the ‘itemBlock.html’ template file looks like.

Edit vault.html template to display all items:

To display the vault contents in the correct order, we need to step through the invItems list and pick out each item of each category.

Here’s the code in the “itemBlock.html” file:

<div class="inventory-container">
{% for item in invItems -%}
    {% for dict_item in weaponList -%}
 
        {% if invItems[item] in dict_item['bucket'] -%}
            {% include 'itemBlock.html' -%}
        {% endif -%}

    {% endfor -%} 
{% endfor -%}
 
</div>

In the code below, we test to see if the current item in our list is one of the category of items we want to display:

{% if invItems[item] in dict_item['bucket'] -%}
    {% include 'itemBlock.html' -%}
{% endif -%}

If so we include the “itemBlock.html” file to display the item.

Viewing our web site:

As usual, the full set of code can be found on my GitHub page:

https://github.com/AllynH/Destiny_Flask_Webserver

There are a few files / folders, so you’ll need to download it from there.

To run the code you can type:

python app.py

This will start the Flask Web Server, you’ll see some output like this:

* Restarting with stat
Opening Manifest...
Finished!
 * Debugger is active!
* Running on https://127.0.0.1:5000/ (Press CTRL+C to quit)

First, click on the “Authenticate with Bungie” link and follow the instructions to authenticate your account.

Index page view:

Here is the view of the index page.

You should see a screen like this, asking you to review and approve the permissions required by this app.

Authorise your app.

Don’t forget to review what permissions your giving the app.

You should then be redirected back to the index page.

Now that you’ve been authorised and logged into Bungie via our app, we can open the link “view your vault contents”, you’ll see something like this:

Vault view.

Here’s what the vault view currently looks like!

Next steps:

Right now, we’ve created the basic shell of the website / app. This is a good start and we can build onto this, add features, create new pages.

The next steps for me will be to add more functionality to the website:

  • Character inventory view.
  • Xur inventory view.
  • Finish the refresh token flow, so users don’t need to authenticate every 30 mins.
  • Add a user database to securely store the users refresh tokens.
  • Add a transfer item feature.

All of the above is finished (except the transfer item feature), I just need to write up the blog post 🙂

Check out my website:

https://www.destinyvaultraider.com/

Creating a Python app for Destiny – Part 7: Authenticating our app with Bungie.net OAuth

Introduction:

Bungie are moving away from their Cookie based authentication flow and have created a new OAuth 2.0 style flow, this allows for a safer, more standardised approach to authenticating users with Bungie.net. Read the Bungie.net authentication release article here.

Bungie outline some of the benefits of using the new authentication flow:

  • It uses OAuth 2.0 style sign in flow, and does not depend on fragile cookies.
  • It gives the user a chance to review the scope of permissions granted to an application so they can understand what the application can do on their behalf.
  • Users can review all write operations performed by an application, and disable apps they no longer wish to have access.
  • It’s sanctioned by Bungie, and we will smile upon apps that use this mechanism instead of cookies. Also, this is the only mechanism permitted by our terms of service for application developers to make use of APIs that require authentication.

If you’d like to review the previous Destiny API posts, check them out here:

  1. Send a request to read Xurs inventory.
  2. Send a HTML formatted email with Xurs inventory.
  3. Log into Destiny and Bungie.net via your PSN account.
  4. Transfer items from your vault to your character and equip them.
  5. Reading a characters inventory and vault contents.
  6. Creating a Python web server with Flask.

You can also find me on Twitter here @Allyn_H_

Also, many thanks to all the people involved in the Destiny Item Manager development team (too many to name individually) and Vlad from Destiny Trials Report,  for their help debugging some of the issues with the OAuth flow!

Creating an app on Bungie.net:

To create an app on Bungie, you need to visit the developer page here. If you’ve previously created one, you’ll need to update it for the new authentication flow.

Fill in the required details:

  1. Application name: Give your app a recognisable name.
  2. Application status: Set it to private unless you plan to deploy this app.
  3. Website: A place where people can find details of your app.
  4. Redirect URL: You’ll need to set a HTTPS redirect URL for your app in order to complete OAuth authorisation.
Setting Callback URL and permissions.

I’ve added all the permissions here, as I’m also using this to test other features – you may not need all these permissions.

Then click on the save changes button at the bottom.

Hooray, your app is created!

App is created, now let’s get coding!

Now, let’s get to coding up the flow…

Creating the authorisation URL:

When you registered your app on Bungie, you should have been given an authorisation URL with a unique number, like this: https://www.bungie.net/en/Application/Authorize/1234.

We are going to take that URL and add some parameters to it before we make our authorisation request. Here’s what the updated “index.html” route looks like:

AUTH_URL = 'https://www.bungie.net/en/Application/Authorize/1234?'

@app.route('/')
@app.route('/index')
def index():
    state = make_authorization_url()
    state_params = {'state': state}
    url = AUTH_URL + urllib.urlencode(state_params)
    return render_template('index.html', url=url)

Before we look at what the state parameter is doing, here’s how we pass the URL to our HTML “index.html” template, via the render_template method.

 Creating the index page:

I’m going to build on the templates made in the previous blog post, you can review them here. The index.html page is left deliberately bare, there’s a lot of cool stuff we could do here but let’s get our basic program up and running first. In our index.html template file, we can create a hyperlink to authorise our account like this:

{% block page_content %}

<div>
    <h2><a href="{{ url }} " >Authenticate with Bungie</a></h2></br></br>
</div>


{% endblock %}

The value for {{ url }} will be passed in from the render_template method, and the hyper link will be created.

Preventing Cross Site Request Forgery with the state parameter:

As an extra precaution, we are going to add some Cross Site Request Forgery (CSRF) protection, as an added security benefit to the visitors of our site, there is a great post about this, referenced on the Bungie authentication article, you can read the post here.

Here’s how we are going to handle the state parameter of our authorisation request and callback:

  1. Create a state value, a random string of numbers and letters.
  2. Save this state value in the users secure HTTPS session.
  3. Add this parameter to the authorisation URL when we direct the user to the Bungie authorisation URL.
  4. When the user is redirected to back to our site via the callback URL, Bungie will echo the state parameter back to us.
  5. Compare the state parameter from Bungie and the state parameter we stored in the users session.

If the state parameters are the same – we know the user is who they say they are, and nothing dodgy is happening.

Here’s what the state parameter looks like in action:

State parameter in action during app authorisation.

Here we can see the state parameter in the URL.

Here’s what it looks like when Bungie redirects the user to the callback URL:

State parameter in callback.

User is redirected to the callback URL, then redirected to the /index route.

Let’s code this up –  here’s what our index view looks like (deliberately basic):

@app.route('/')
@app.route('/index')
def index():
    state = make_authorization_url()
    state_params = {'state': state}
    url = AUTH_URL + urllib.urlencode(state_params)
    print url
    return render_template('index.html', url=url)

The first thing we do is call the function make_authorization_url() and store the result in the “state” variable.

Inside the make_authorization_url(), we create a unique UUID (Universally Unique Identifier)  32 bit string, using  the Python library “uuid4”.  We store this UUID string in a variable called “state”.

Now that we have generated the state string, we’ll need to store, so we then call a function, save_created_state(state) and pass it the “state” value we just generated. We also return the

Here’s what the code looks like:

 
def make_authorization_url():
    # Generate a random string for the state parameter
    from uuid import uuid4
    state = str(uuid4())
    save_created_state(state)
    return state 

Now that we have generated the “state” value, we need to store this in the session, here’s what the code for save_created_state() looks like:

# Save state parameter used in CSRF protection: 
def save_created_state(state):
    session['state_token'] = state
    pass

Now that we have generated the state parameter, we can generate our authorisation URL and send the user to Bungie to be authenticated.

Handling a callback:

Once we have sent our request for authorisation, our server will listen on the callback URL for a response. The response from Bungie will also echo our CSRF state parameter back to us, so we are sure the response is from Bungie.

The callback route should read the state parameter, test to see if it’s the same parameter we stored in the users session – if it’s not the same state parameter- we send a HTTP 403 “forbidden” response. If the state parameter is the same as the one we sent, we can then store the authorisation code (access_token).

@app.route('/callback/bungie')
def bungie_callback():
    state = session.get('state_token')
    if not is_valid_state(state):
        print "Uh-oh, this request wasn't started by us!"
        abort(403)
    session.pop('state_token', None)
    code = request.args.get('code')
    access_code = code
    token = get_token(code)
    return redirect(url_for('index'))

The method  is_valid_state(state) is used to check that the state echoed back from Bungie is the same as the state value we have stored in our session.

Below you can see we are passing in the returned state value and comparing it to the “saved_state” value taken from the session:

def is_valid_state(state):
    saved_state = session['state_token']
    if state == saved_state:
        print "States match, you are who you say you are!"
    return True
    else:
        return False

If the states match, we return “True” and send our request for the access_token, if the states do not match, we return “False” and throw our 403 error.

The code “session.pop(‘state_token’, None)” removes the state parameter from the session (set’s it to “None”) – we don’t need it anymore.

Getting the Access Token from the Authorisation Code:

Now that the user has authorised their account and we have received the authorisation code from Bungie.net, we can swap this authoirsation code for the access token. This relates to step 4 & 5 of the Bungie.net authorisation flow.

First, we take the authorisation code and add it as the value of a Python dictionary object “post_data” – this dictionary object will be added to the body of the post request and transmitted as a JSON object.

def get_token(code):
    HEADERS = {"X-API-Key":'MY-API'}
    post_data = {'code': code}
    response = requests.post(access_token_url, json=post_data, headers=HEADERS)
    :

Now that we have made a request for the token – lets look at the response:

We've got the access_token!

JSON response containing access_token and refresh_token.

The section we are most interested in (right now) is:

    "accessToken": {
        "readyin": 0, 
        "expires": 3600, 
        "value": "COoJEo ... j8w=="
 }

We now have the access token needed to make any authorised API request! The response contains a bit more information too; the access token is ready in 0 seconds – which means we can use it immediately. The access token will expire in 3600 seconds – 1 hour from now. The access token value, is of course, the really long string.

Now that we have the JSON response, we can save the access token and the refresh token. I’m also saving the time values which tell me when my refresh token is ready and when it expires.

I’m not going to worry about refreshing the token in this blog post, as it takes a bit more work. I will be updating my code and writing a new blog post at a later date.

def get_token(code):
    :
    access_token = response.json()['Response']['accessToken']['value']
    refresh_token = response.json()['Response']['refreshToken']['value']
    refresh_ready = datetime.now() + timedelta(seconds=int(response.json()['Response']['refreshToken']['readyin']))
    refresh_expired = datetime.now() + timedelta(seconds=int(response.json()['Response']['refreshToken']['expires']))
    save_session(token_json)
    userSummary = GetCurrentBungieUser(oauth_session)
    return userSummary.json()['Response']['displayName']

We will use this to create an authorised HTTP session.

Creating our authorised session:

Now that we have our API key and our access token – we can create an authorised session. As before the API-Key is added to the session header “X-API-Key”. We also need to add an “Authorization” header with the value of our access token, here’s how the access token above would look: “Bearer  COoJEo … j8w==” (I’ve shortened it a little here).

Here’s the code to create the session:

def save_session(token_json):
    oauth_session = requests.Session()
    oauth_session.headers["X-API-Key"] = API_KEY
    oauth_session.headers["Authorization"] = 'Bearer ' + str(token_json)
    access_token = "Bearer " + str(token_json)

Making an authorised request:

Now that we are authorised – we can try a simple GET request to the GetCurrentBungieAccount endpoint (as recommended in the Bungie Auth article), this will give us some information on the logged in character, from their Bungie account.

For this – I’ve created a new view template called “vault.html”, for now this is just a straight copy of the “index.html” template. In the future, I’ll populate this with all of the vault contents.

def vault():
    userSummary = GetCurrentBungieAccount(oauth_session)
    return render_template('vault.html', 
        character=userSummary.json()['Response']['user']['displayName'], 
        lightLevel = charSummary.json()['Response']['data']['characterBase']['stats']['STAT_LIGHT']['value'],
        emblemImage = account.json()['Response']['data']['characters'][0]['emblemPath'],
        backgroundImage = account.json()['Response']['data']['characters'][0]['backgroundPath'],
    )

Here’s what the code for GetCurrentBungieAccount looks like, as we’ve seen before, it’s the same Python Requests GET format, we are passing the “oauth_session”, that we saved a moment ago, into the function as a parameter. We are returning the JSON response to the vault() route.

def GetCurrentBungieAccount(session):
    req_string = 'https://www.bungie.net/Platform/User/GetBungieNetUser/'
    res = session.get(req_string)
    return res

I’ve also created a new function to return some character specific data using the getCharacterSummary endpoint, this will give me some data like my characters light level and my characters emblem.

def GetCharacterSummary(session):
    req_string = base_url + membershipType + "/Account/" + destinyMembershipId + "/Character/" + characterId + "/"
    res = session.get(req_string)
    return res

Viewing our web site:

As usual, the full set of code can be found on my GitHub page:

https://github.com/AllynH/Destiny_Flask_Webserver

There are a few files / folders, so you’ll need to download it from there.

To run the code you can type:

python app.py

This will start the Flask Web Server, you’ll see some output like this:

* Restarting with stat
Opening Manifest...
Finished!
 * Debugger is active!
* Running on https://127.0.0.1:5000/ (Press CTRL+C to quit)

First, click on the “Authenticate with Bungie” link and follow the instructions to authenticate your account.

Index page view:

Here is the view of the index page.

You should see a screen like this, asking you to review and approve the permissions required by this app.

Authorise your app.

Don’t forget to review what permissions your giving the app.

You should then be redirected back to the index page.

Now that you’ve been authorised and logged into Bungie via our app, we can open the link “view your vault contents”, you’ll see something like this:

Welcome to your vault - yet to be finished.

Gamertag, light level, Emblem and emblem background display.

Next steps:

Right now, we’ve created the basic shell of the website / app. This is a good start and we can build onto this, add features, create new pages.

The next steps for me will be to add more functionality to the website:

  • Character inventory view.
  • Xur inventory view.
  • Finish the refresh token flow, so users don’t need to authenticate every 30 mins.
  • Add a user database to securely store the users refresh tokens.
  • Add a transfer item feature.

All of the above is finished (except the transfer item feature), I just need to write up the blog post 🙂 I’m looking to deploy the code soon to a real live web site, so stay tuned.

 

Creating a Python app for Destiny – Part 6: Creating a Python web server with Flask

Introduction:

In my previous blog posts, I’ve been working to build a wep app based on Bungies API for Destiny the game. I’ve made great progress with the server side requests and processing the data responses from Bungie, the next steps are to integrate this code into a web server. This will also work out well as I move my code over to the new Bungie OAuth fow.

After a bit of research, I’ve decided to go with the Flask microframework. As their tag line suggests, Flask is a very light weight microframework and appears to be a little easier than Django to setup for smaller projects. In order to learn more about Flask, I’ve been working from Miguel Grinbergs book and his massive online tutorial, both of which I would highly recommend. This blog post is designed as a general overview of Flask, Jinja2, Python as there are many other better thought out learning material on these subjects I’d like to get stuck into the OAuth and adding Destiny features into the code.

If you’d like to review the previous Destiny API posts, check them out here:

  1. Send a request to read Xurs inventory.
  2. Send a HTML formatted email with Xurs inventory.
  3. Log into Destiny and Bungie.net via your PSN account.
  4. Transfer items from your vault to your character and equip them.
  5. Reading a characters inventory and vault contents.

Hello World:

To get started, lets start with the “Hello World!” example on the Flask homepage.

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
 app.run()

What the code is doing:

from flask import Flask
app = Flask(__name__)

We are importing the Flask Class from the flask package. We then create an instance of a Flask object and name it “app”. We pass “__name__” as the first parameter to our Flask object, this is the “import_name” parameter and you can read more about it here.

@app.route("/") 
def hello(): 
    return "Hello World!" 

The @app.route() decorator is used to tell Flask what URL will trigger our function. In this case, a visitor to our homepage will trigger the function hello(). Within the hello() function, we are returning the string “Hello World!”.

if __name__ == "__main__":
    app.run()

When you execute the Python code from the command line, “__name__” will be set to “__main__”, this will execute the code to run our instance of the Flask object, called “app”.

When we execute the code it will print something like this to the screen:

> python app.py
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [25/Jan/2017 12:45:43] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [25/Jan/2017 12:45:43] "GET /favicon.ico HTTP/1.1" 404 -

When we navigate to address in our browser we will see this:

Flask Hello World app

This is what our app looks like in our web browser.

If we view the source of the web page (Right mouse button -> View source), we can see. There is no HTML displayed, just the string “Hello World!”:

Flask Hello World app - page source

No HTML here – just a string.

This is a good start, we have created a web server with 7 lines of code.

Adding features to our server:

Now that we have a very basic web server up and running, lets add some features.

Flask_script Manager:

Flask Manager provides support for writing external scripts in Flask, in our case it will allow us to add parameters for the server directly from the command line. This will allow us to switch between debug mode and production mode, or to change the port and host IP numbers directly from the command line.

Here’s how we create a Manager instance and link it to our “app” object:

from flask_script import Manager
manager = Manager(app)

We can then restart our web server like so:

> python hello.py runserver

Or we could start on a different port like this:

> python hello.py  runserver -p 5001

Which would return:

 * Running on http://127.0.0.1:5001/ (Press CTRL+C to quit)

Flask-Bootstrap:

If you’re not familiar with HTML or web development, Bootstrap is a free web framework for designing websites. Bootstrap contains HTML and CSS templates used to design web pages. It really takes a lot of the pain out of designing websites 🙂

For more info on Python-Bootstrap, see here.

Here’s how we import Bootstrap into our app:

from flask_bootstrap import Bootstrap

bootstrap = Bootstrap(app)

This will allow us to create Bootstrap based designs by adding the following to our base.html template file (I’ll talk about this more in a minute):

{% extends "bootstrap/base.html" %}

HTML with the Jinja2 template engine:

In my previous posts, I’ve used a series of the file.write() commands to take the JSON response, format it as HTML , with a bit of hacking, and write it to a HTML file. This is OK for small scale programs, like the one where I send Xurs inventory as a HTML formatted email. It’s not really suitable for large scale projects, or any sort of project where you’ll be displaying multiple pages of content.

A template file is a text file that contains the response from our server, we can control the flow and contents of the template file by using Jinja2’s inbuilt Control Statements. First, let’s change the above code from returning a string – to returning a rendered Jinja2 template.

from flask import Flask, render_template

@app.route("/") def hello():
    message = "Hello, World!"
    return render_template('index.html', message=message) 

In the above code, instead of just returning the string “Hello, World!”, we are passing the Python string to the Jinja2 engine, which then renders the index.html template using the data our Python sends.

Before we build the index.html template, we can strip out all of the generic HTML code, that is reused for each page, and keep that in a base template file. This base.html template will contain our HTML <head></head> data, our CSS links, and our navigation bar.

Here’s what our base.html will look like:

When Flask-Bootstrap is initialised by our app, the template “bootstrap/base.html” becomes available to us. This “bootstrap/base.html” template contains the general HTML structure of our page and allows us to derive all of our new templates from this.

(For convenience, I’ve stripped out the navigation bar HTML, I’m using the Bootstrap example code for this).

{% extends "bootstrap/base.html" %}

{% block title %}Hello World app{% endblock %}

{% block head %}
{{ super() }}
    <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
    <link rel="icon" href="{{ url_for('static', filename = 'favicon.ico') }}" type="image/x-icon">{% endblock %}
{% block navbar %}

<div class="navbar navbar-inverse" role="navigation">
    :
    :
    :
</div>

{% endblock %}

{% block content %}

<div class="container">
    {% block page_content %}{% endblock %}
</div>

{% endblock %}

Blocks like {% title%}, {% navbar %} or {% content %} are called “convenience” blocks, and are built directly into the Flask-Bootstrap, this means we can amend these blocks, without having to directly replace them. In the case of the {% head %} block, we call Flasks super() function, which allows to to amend data to the block, without overwriting the Bootstrap CSS links in the <head></head> tags.

The block we are most interested in is the {% block content %}, inside here we will find the {% block page_content %}, this will allow any template that calls our base.html to replace the contents of this block – through Template Inheritance.

{% block content %}

<div class="container">
    {% block page_content %}{% endblock %}
</div>

{% endblock %}

Here’s what our index.html will look like:

Now that all of our generic HTML has been defined in the base.html, we can create our index.html, as an extension of the base.html template.

{% extends "base.html" %}
{% block title %}Hello World app{% endblock %}

{% block page_content %}

<div>
<h1>{{ message }}</h1>
</div>

{% endblock %}

The index.html defines the contents of the page_content block, in our case we will print the string stored in the Python variable “message”, inside a set of <div> and <h1> tags:

<h1>{{ message }}</h1>
 

Folder structure:

In this example, we’ll have a base.html file for all of our generic HTML and an index.html, which will be populated by our Python code. This is a basic example, but later I’ll be building several more pages and HTML templates into this project.

Flask folder structure

Here is the folder structure Flask uses for defining template and static files.

Running the web server:

Now that the code is complete we can start our web server and see what our index page looks like.

We can start our web server like so:

> python hello.py runserver
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [25/Jan/2017 12:45:43] "GET / HTTP/1.1" 200 -

Then navigating to http://127.0.0.1:5000/ we can view our active web server:

Web server with HTML output.

Web server shown on the left and HTML source code on the right.

As we can see, we are no longer outputting just a text string, we are outputting a full HTML web page, created from our base.html, index.html and the output of our Python code.

Download the code:

The full set of Python code can also be found on my GitHub page here: https://github.com/AllynH/Flask_webserver

As there are a few files in this project, it’s best to copy it from there.

Next steps:

Now that we have a web server built in Python using Flask we can:

  • Update the code with the new Bungie OAuth login flow.
  • Build in our previous Bungie code.

The new Bungie authentication flow!

Hi there,

As of the 14th of December, Bungie have changed their authentication flow for the Destiny API. Bungie are moving away from the cookie based authentication flow, to the OAuth 2.0 flow.

The code in the previous blog posts will still work, but Bungie will soon be switching over fully to the new authentication system.

This is good news for anyone using the apps based on the Destiny API, as it is much more secure, offers the users more control over how their data is used, and what can controlled from external applications.

You can read about the changes from the official Bungie release here:

The new Bungie OAuth flow.

I’m currently creating the Python code for the new authentication flow. The code is all working but I’m taking a week off to spend Christmas with my family, so stay tuned for the blog post with updated code 🙂

Creating a Web Server on the Galileo: Using the Arduino IDE.

Creating a Web Server using the Arduino IDE example:

The Arduino IDE comes with a prebuilt Web Server example. If you’re not sure what a Web Server is, or what it’s used for – check out my blog post here -> Creating a Web Server on the Galileo

It's pretty sweet actually.

Arduino Example for creating a Web Server.

The above example comes with enough code to:

  • Create a Web Server on the given IP address.
  • Create a blank HTML page.
  • Read the analogue inputs and display their values on the HTML page.

How the Arduino Web Server works:

The Web Server uses the Arduino Ethernet library to answer any HTTP requests made to the Galileo. The Galileo already has an Ethernet connection on board and fully supports the Ethernet library.

Here’s how the library is explained on the Arduino website:

The library allows the Arduino device to connect to the internet. It can serve as either a server accepting incoming connections or a client making outgoing ones. The library supports up to four concurrent connection (incoming or outgoing or a combination).

The Arduino example does not conform to the full HTML page structure, and instead relies on printing text between the opening and closing HTML tags.
This is not the ideal solution for creating a fully functioning HTML page but it is a very quick and easy way to control your Galileo via a web browser. The example is pretty bare-bones, a HTML only website with no CSS styling or server side scripting functionality. Without any JavaScript or PHP, your page needs to be refreshed anytime the data changes.

Creating the Web Server in the Arduino IDE:

We start the Web Server by initialising the Ethernet class, to do this we need to call the Ethernet.begin() function and pass the required MAC and IP addresses to the function.

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,1,177);

void setup() {
//  Ethernet.begin(0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED, 192,168,1,177); 
    Ethernet.begin(mac, ip);
}

Once the Ethernet class has been initialised, we need to tell the server to listen for incoming connections on port 80, this is the default port for HTML requests, you can change this port number, which I will be doing in the Node.js example.

EthernetServer server(80);

void setup() {
  server.begin();
}

When the server is running and a client is available (connected via a web browser), the client.println() function is used to send data to all the connected devices. HTML can be printed directly using the client.println() function.

void loop() {
  client.println("<title> Allyns webserver: </title>");
}

The above code can be used to build a fully functioning Web Server.

Note:

At the time of writing there is a bug in the Arduino Ethernet library, which is discussed and a fix is suggested here.

© 2017 Allyn H

Theme by Anders NorenUp ↑