How To Use Python To Interact With Steemconnect's Access Tokens, Refresh Tokens And Perform Voting

View this thread on: d.buzz | hive.blog | peakd.com | ecency.com
·@steempytutorials·
0.000 HBD
How To Use Python To Interact With Steemconnect's Access Tokens, Refresh Tokens And Perform Voting
<center>![banner.png](https://cdn.steemitimages.com/DQmaqmkNGUPGei1xJdTmHcqEotS9ac6oaigWg9AN2Zv4Eoz/banner.png)</center>

This tutorial will explain how Steemconnect and Python can be used to build a back-end application that is used to perform voting.

---

#### Repository
https://github.com/steemit/steemconnect
https://github.com/emre/steemconnect-python-client

#### What will I learn

- Storing access tokens
- Retrieving access token en refreshing
- Voting and response
- Error handling

#### Requirements

- Python3.6
- MySQL
- Steemconnect

#### Difficulty

- intermediate

---

### Tutorial

#### Preface
Python is a great language to build back-end applications that work great in conjunction with a front-end that is for example build with a framework like WordPress. This tutorial will look into how voting can be done by fetching `access_tokens` from a MySQL database. There are 3 files that are not set up to do anything in particular but include all the functions and database table structure to do so. [Github link](https://github.com/amosbastian/steempy-tutorials/tree/master/part_30).

#### Install Steemconnect for Python
A splendid Steemconnect written for Python by @emrebeyler.
```
pip3 install steemconnect
```

#### Setting up your Steemconnect app
This tutorial requires access to a Steemconnect app. It is somewhat a continuation on the Steemconnect & WordPress tutorials [1](https://steemit.com/utopian-io/@steempytutorials/integrate-steemconnect-v2-user-authentication-into-any-wordpress-website) and [2](https://steemit.com/utopian-io/@steempytutorials/integrate-steemconnect-v2-user-authorisation-into-any-wordpress-website). However, they are not required for this tutorial.

All Steemconnect actions are performed via the `Client` object. It takes the `client_id` and `client_secret` from your Steemconnect app. In addition it requires the `access_token` for the account that actions will be performed for. A separate `Client` is needed for each account, or the `access_token` has to be changed.

```
c = Client(
        client_id=CLIENT_ID,
        client_secret=CLIENT_SECRET,
        access_token=access_token,
    )
```

The `client_id` and `client_secret` are declared outside of the function. This is also true for the database variables. They have to be configured for the code to work.


#### Storing access tokens
For the storing of the access tokens a MySQL database is used that has the following properties. This tutorial assumes that there is already a solution in place to obtain the initial access tokens and will look into how they can be used afterwards.

```
CREATE TABLE `steem_authorization` (
  `id` int(11) NOT NULL,
  `access_token` text NOT NULL,
  `user_login` varchar(20) NOT NULL,
  `expires_in` datetime NOT NULL,
  `refresh_token` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

ALTER TABLE `steem_authorization`
  ADD PRIMARY KEY (`id`),
  ADD UNIQUE KEY `user_login` (`user_login`);

ALTER TABLE `steem_authorization`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
```

There are 4 main attributes. The `access_token` itself, the `user_login`, which STEEM account is belongs to, `expires_in`, this is calculated when the access_token is created. And the `refresh_token`, this is given when offline access is requested and is used to refresh the `access token` when it expires.

#### Retrieving access tokens and refreshing
The query is relatively simple as there should only be 1 `access_token` per user in the database. If this is not the case the updating and or registering for the `access_token` is not dealt with properly. `get_data()` fetches all the rows and return them.

```
def get_user_auth(self, voter):
    query = ("SELECT `access_token`,`refresh_token`,`expires_in` FROM " +
             f"`steem_authorization` WHERE `user_login` = '{voter}'")
    return self.get_data(query)
```
<br>
The access token is fetched from the database with its relevant data. The next step is to verify that the `access_token` is still valid, if not it must be renewed. **Note: This is only possible if the user gave authorisation for offline access.**
```
result = self.db.get_user_auth(voter)
access_token, refresh_token, expire_on = result[0]
dt = datetime.now()
```
<br>
The current date is referenced against the date when the token expires. The `access_token` is then refreshed by calling the `refresh_access_token()` function inside Client. The new variables are then stored in the database.
```
# Verify access_token
if dt > expire_on:
    result = c.refresh_access_token(
                refresh_token,
                "login,vote"  # scopes
    )

# Retrieve new variables and store in DB
access_token = result['access_token']
refresh_token = result['refresh_token']
expires_in = result['expires_in']
self.db.update_authentication_tokens(
    voter,
    access_token,
    refresh_token,
    expires_in,
    self.timestamp,
)
```
<br>
The current timestamp is used to calculate the new date when the new `access_token` expires.
```
def update_authentication_tokens(self, voter, access_token, refresh_token,
                                 expires_in, timestamp):
    dt = datetime.strptime(timestamp, '%Y-%m-%dT%H:%M:%S')
    expires_in = dt + timedelta(seconds=expires_in)

    query = ("UPDATE `steem_authorization` SET `access_token` = " +
             f"'{access_token}', `expires_in` = '{expires_in}', " +
             f"`refresh_token` = '{refresh_token}' WHERE " +
             f"`user_login` = '{voter}';")
    self.post_data(query, 'steem_authorization')
```


#### Voting and response
Voting is relatively simple as it just requires the parameters that make up the vote.

```
# Perform vote
vote = Vote(voter, author, permlink, weight)
```
<br>
The vote will return a response, if it succeeds or fails.
```
result = c.broadcast([vote.to_operation_structure()])
```
<br>
A successful example:
```
{
	'result': {
		'id': 'b0e79cc31de3d248d7a90406edadf9dc3e979e14',
		'block_num': 24673233,
		'trx_num': 21,
		'expired': False,
		'ref_block_num': 31679,
		'ref_block_prefix': 3139963426,
		'expiration': '2018-08-01T01:57:57',
		'operations': [
			['vote', {
				'voter': 'juliank',
				'author': 'axeman',
				'permlink': 'girls-n-cars-greta-vs-mb',
				'weight': 1000
			}]
		],
		'extensions': [],
		'signatures': ['1f7b83bba9177a8f5c980e6185f7546134554ae6229363a1ef2986bdb29ba60d0b5b7e3cf6975e8d3d955a3274b2b8cbf526fd42b1b2f88ed0167c45296a50d15a']
	}
}
```
An example of an error:
```
{
	'error': 'server_error',
	'error_description': 'itr->vote_percent != o.weight: You have already voted in a similar way.'
}
```

#### Error handling
Error handling is done by just looking if there is an `error` key in the result.

```
if 'error' in result:
   message = result['error_description']
   print(message)
else:
   message = 'Succes'
   print(message)
```
<br>
It is recommend to capture the `error` message and store this inside the database for manual review. This way the application can continue.

```
self.db.add_to_error_log(
    voter, author, permlink, weight,
    message, self.timestamp,

def add_to_error_log(self, voter, author, permlink, weight, message,
                     timestamp):
    query = (
        'INSERT INTO `error_log` (`id`, `voter`, `author`, ' +
        '`permlink`, `weight`, `error`, `timestamp`) VALUES ' +
        f'(NULL, "{voter}", "{author}", "{permlink}", ' +
        f'"{weight}", "{message}", "{timestamp}");')
    self.post_data(query, 'error_log')
```
---

The files are available on [Github](https://github.com/amosbastian/steempy-tutorials/tree/master/part_30).

This tutorial was written by @juliank.
👍 , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,