
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.
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 and 2. 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)
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()
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,
)
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)
The vote will return a response, if it succeeds or fails.
result = c.broadcast([vote.to_operation_structure()])
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)
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.
This tutorial was written by @juliank.
Thank you for your contribution.
Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.
To view those questions and the relevant answers related to your post, click here.
Chat with us on Discord.
[utopian-moderator]Need help? Write a ticket on https://support.utopian.io/.
Exactly what I needed to play with right now. Thanks a lot for this.
However, can we get steemconnect and js version of this (if there is)? That will be also interesting to read (maybe even more interesting).
You can look around, maybe somebody else hopped on that bandwagon. I barely know js myself so I cannot help you with that. However, the idea stays the same, just different syntax.
Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!Hey @steempytutorials
Want to chat? Join us on Discord https://discord.gg/h52nFrV.
Vote for Utopian Witness!