How To Use Python To Interact With Steemconnect's Access Tokens, Refresh Tokens And Perform Voting
utopian-io·@steempytutorials·
0.000 HBDHow To Use Python To Interact With Steemconnect's Access Tokens, Refresh Tokens And Perform Voting
<center></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.
👍 photocontests3, torchweed, makerhacks, codebull, photocontests4, tamaxadi, steempytutorials, iauns, ione.s86, ihalf2p, gregjava, yuxi, ceruleanblue, drorion, statsexpert, lucky-robin, abandi, aleister, zuur, emrebeyler, sereze, thememeguy, kamuoyu, yollardannotlar, polbot, fisherck, juliank, finaldestination, ravigoyal1, tonino, artcreator, midun, jacekw.dev, luokai1992, scorer, cristi, rach, portugalcoin, amosbastian, jrawsthorne, rafalski, mcfarhat, rondras, bhuiyansohel, mycryptostalker, adam-saudagar, utopian-io, ace108, sozib.karmokar, fikri02, christophe51, compumatrix, happens, lsn1406, cyberspacegod,