4th Street Bar Hive-Bar

Hive-Bar Powered by Hive beta-fdb5b5b

Community post

Howto write a perl package to interact with steemit

steemit-perl.png

What Will I Learn?

  • How to create a simple modern perl project
  • Use Mojolicious to create simple object-oriented classes
  • Make web requests with to jsonrpc services
  • Interact with the steem api

Requirements

For this tutorial you will require:

Difficulty

Intermediate

Tutorial Contents

We will start with the basic setup and work out way up to interacting with the steemit api.

Basic project setup

First we create the basic folder structure we will require later on:

  • Bin
    • This will contain out executables
  • Lib
    • Here we will have the module which implements the functionality
  • Etc
    • Other required data will go in here

OK, so first we have to create out package. We will call it Steemit.pm and it goes into the lib folder of course

creating the library

vi lib/Steemit.pm
package Steemit;                                                                                                         
                                                                                                                         
use Modern::Perl '2017';                                                                                                 
use Mojo::Base -base;                                                                                                    
use Mojo::UserAgent;                                                                                                     
use Mojo::JSON qw(decode_json encode_json);                                                                              
                                                                                                                         
has url     => 'steemd.minnowsupportproject.org';                                                                                                             
has ua      =>  sub { Mojo::UserAgent->new };                                                                            
                                                                                                                                                                                                                                                  
1; 

We have here the initial package definition followed by the usage of some libraries. We use Modern::Perl since it will enable the latest perl feature set and language constructs. Further it will automatically use strictures and warnings which is just a sane thing to do.

As base for our project we are going to use Mojolicious Framework . This wills ave us a lot of time wringing object oriented code and has many helpers for accessing web resources and working with json based requests.

What have we donw so far?

  • We then defined our initial two attributes of our class.
  • A url where we will send our requests to
  • A user agent class that we will use to make the http requests

handling dependencies

We will use cpanminus for our perl based dependencies. It has a convenient way of using a cpanfile for installing everything we use. We will put it in the /etc folder of our project:

vi etc/cpanfile

requires 'Modern::Perl', undef;                                                                                          
requires 'Mojo::Base',   undef;                                                                                          
requires 'Test::More',   undef; 

We can denote here all the required modules and while it has more features that will warrant a own guide in the future we take the easy route and not “undef” as version and require all modules to have the latest version of them available.

We can then install the dependencies with

cpanm --v --installdeps ./etc/

adding a test

As good practice we will include a test file.

vi t/steemit.t
#!/usr/bin/env perl
use Modern::Perl '2017';
use Test::More;

use FindBin;
use lib "$FindBin::Bin/../lib";

use_ok('Steemit');
my $steem = Steemit->new;

isa_ok( $steem, 'Steemit', 'constructor will return a Steemit object');

done_testing;

We use Test::More to implement our tests. The use_ok statement will check if we can basically load the package. Then we create an instance of the class. Finally we check if the constructor of our cals indeed returns a valid object.

the client executable

We also want to use the library we are building. So we finally will create a script that will execute the functionalities.

vi bin/steemit_client.pl 
#/usr/bin/env perl
use Modern::Perl '2017';

use FindBin;
use lib "$FindBin::Bin/../lib";

use Steemit;

my $steem = Steemit->new;

say "Initialized Steemit client with url ".$steem->url;

It will again use our package, create a package and call our assessor to print out the url of the endpoint we are going to use.

This concludes our initial project setup. For reference everything can be found in this git branch But what has it to do with the steem api? Good question and we come to this now.

Building the Steemit integration

We first have to build a method to make our requests. For this we add the following to our lib/Steemit.pm

sub _request {                                                                                                           
   my( $self, $api, $method, @params ) = @_;                                                                             
   my $result = decode_json $self->ua->get( $self->url, json => {                                                        
      jsonrpc => '2.0',                                                                                                  
      method  => 'call',                                                                                                 
      params  => [$api,$method,[@params]],                                                                               
      id      => int rand 100,                                                                                           
   })->result->body;                                                                                                     
                                                                                                                         
   return $result->{result} if $result->{result};                                                                        
   if( my $error = $result->{error} ){                                                                                   
      die $error->{message};                                                                                             
   }                                                                                                                     
   #ok no error no result                                                                                                
   require Data::Dumper;                                                                                                 
   die "unexpected api result: ".Data::Dumper::Dumper( $result );                                                        
}                                                                                                                        

Now let's explain this a little bit. Steemit uses a jsonrpc api so we send json there and get json back. This is a simplistic view but covers the basics.

$self->ua->get will send a get request via the Mojo::UserAgent package. It already supports to send a json request body. We will always call the generic “call” method which needs the following parameters:

  • First an api section that we will call, Steemit supports here
    • Login_api
      • authentication
    • network_node_api
      • Methods about the network connectivity and state
    • network_broadcast_api
      • Sending messages ( voting, commenting……)
    • database_api
      • Read access to the steem blockchain
      • Within this tutorial we will focus only in the database_api since we can use it without hassling around with cryptography, security and so on.
  • Then the actual method we want to use. This specifies the actual functionality we want to use
  • An array of parameters that the method requires.

We use decode_json to unmarshal the response and translate json to perl data structures.

After this we handle our result. Either there is a “result” field defined, then we can return it. Otherwise we should have an “error” with a message attached. In case something is really off we will throw an exception with the whole result we have.

So now that we have the general method to make requests we will add a method to make one.

sub get_accounts {                                                                                                       
   my( $self, @params ) = @_;                                                                                            
   return $self->_request('database_api','get_accounts',@params);                                                        
}  

Then we can call it in our bin/steemit_client.pl . There can now just ad the simple lines:

use Data::Dumper;                                                                                                        
say Dumper( $steem->get_accounts(['utopian-io'])); 

This will return us a lot of information about the account of “utopian-io”:


$VAR1 = [
          {
            'can_vote' => bless( do{\(my $o = 1)}, 'JSON::PP::Boolean' ),
            'other_history' => [],
            'posting_rewards' => 3114052,
            'last_account_recovery' => '1970-01-01T00:00:00',
            'sbd_balance' => '0.000 SBD',
            'lifetime_market_bandwidth' => 0,
            'active_challenged' => bless( do{\(my $o = 0)}, 'JSON::PP::Boolean' ),
            'proxy' => '',
            'mined' => $VAR1->[0]{'active_challenged'},
            'last_account_update' => '2017-12-24T13:03:30',
            'savings_sbd_seconds' => '0',
            'average_market_bandwidth' => 0,
            'last_market_bandwidth_update' => '1970-01-01T00:00:00',
            'memo_key' => 'STM5uKByZ9z1QhbGG7AwjMuZzhTvSukJDhUcLhCZbwQkg1H46hUyd',
            'vesting_balance' => '0.000 STEEM',
            'savings_sbd_last_interest_payment' => '1970-01-01T00:00:00',
            'savings_sbd_seconds_last_update' => '1970-01-01T00:00:00',
            'curation_rewards' => 50833941,

This can already be used to access a lot of information on accounts. The example can be accesed via this git link

In a later example we will explore the steemit api further and learn how to expand this example to make something more useful out of it. So stay tuned .



Posted on Utopian.io - Rewarding Open Source Contributors

24 upvotes $9.32

Replies (14)

Review before signing

Posting as . Signing with . Keychain permission: Posting. Hive Keychain will ask you to approve this action next.


  
Technical details

Operation fingerprint: