Howto write a perl package to interact with steemit
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:
- a linux operating system.
- If you dont know how to get one you can refer to my previous guide on How to set up a debain vm in VirtualBox
- A perlbrew installation
- Also here you can reference my previous guide on How to set up a modern perl environment
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.
- Login_api
- 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
Hey @hoffmann I am @utopian-io. I have just upvoted you!
Achievements
Suggestions
Get Noticed!
Community-Driven Witness!
I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!
Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x
Thank you for the contribution. It has been approved.
You can contact us on Discord.
[utopian-moderator]
thank you so mutch :D
i guess the third time is the charm
This is great! I'm working with Python and steempy but I'd much rather be working with Perl.
Is there a cpan version of this, by any chance? If not please accept my vote for one (I'd upvote this post, but it was 9 days ago). In fact I could help with it, if you're interested.
hi,
in fact i want to make it a cpan module in the future but first i want to line out the details on how it works.
i guess in about 4-5 posts from now it should become a cpan module ;)
Excellent! Looking forward to it, just followed you.
Hmm, the "use Modern::Perl '2017';" line is failing with this error:
I'm using an Ubuntu 16.04.3 (LTS) VM. Just did "sudo apt update" and "sudo apt upgrade" but the newest version of Perl available is 5.22. Don't want to upgrade to a newer Ubuntu version that's not LTS (long-term support).
Aha! I see that your link to the Perlbrew tutorial should resolve my issue. Great, thanks!