Steemit-WsClient

 view release on metacpan or  search on metacpan

lib/Steemit/WsClient.pm  view on Meta::CPAN

our $VERSION = '0.11';


=head1 SYNOPSIS


    use Steemit::WsClient;

    my $foo = Steemit::WsClient->new();
    my $steem = Steemit::WsClient->new( url => 'https://some.steemit.d.node.address');

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

    #get the last 99 discussions with the tag utopian-io
    #truncate the body since we dont care here
    my $discussions = $steem->get_discussions_by_created({
          tag => 'utopian-io',
          limit => 99,
          truncate_body => 100,
    });

    #extract the author names out of the result
    my @author_names = map { $_->{author} } @$discussions;
    say "last 99 authors: ".join(", ", @author_names);

    #load the author details
    my $authors = $steem->get_accounts( [@author_names] );
    #say Dumper $authors->[0];

    #calculate the reputation average
    my $reputation_sum = 0;
    for my $author ( @$authors ){
       $reputation_sum += int( $author->{reputation} / 1000_000_000 );
    }

    say "Average reputation of the last 99 utopian authors: ". ( int( $reputation_sum / scalar(@$authors) )  / 100 );


=head1 DEPENDENCIES

you will need some packages.
openssl support for https
libgmp-dev for large integer aritmetic needd for the eliptical curve calculations

   libssl-dev zlib1g-dev libgmp-dev


=head1 SUBROUTINES/METHODS

=cut

use Modern::Perl;
use Mojo::Base -base;
use Mojo::UserAgent;
use Mojo::JSON qw(decode_json encode_json);
use Data::Dumper;

has url                => 'https://api.steemit.com/';
has ua                 => sub { Mojo::UserAgent->new };
has posting_key        => undef;
has plain_posting_key  => \&_transform_private_key;


=head2 all database api methods of the steemit api

L<https://github.com/steemit/steem/blob/master/libraries/app/database_api.cpp>

      get_miner_queue
      lookup_account_names
      get_discussions
      get_discussions_by_blog
      get_witness_schedule
      get_open_orders
      get_trending_tags
      lookup_witness_accounts
      get_discussions_by_children
      get_accounts
      get_savings_withdraw_to
      get_potential_signatures
      get_required_signatures
      get_order_book
      get_key_references
      get_tags_used_by_author
      get_account_bandwidth
      get_replies_by_last_update
      get_dynamic_global_properties
      get_block
      get_witnesses
      get_transaction_hex
      get_comment_discussions_by_payout
      get_discussions_by_votes
      get_witness_by_account
      verify_authority
      get_config
      get_account_votes
      get_discussions_by_promoted
      get_conversion_requests
      get_account_history
      get_escrow
      get_discussions_by_comments
      get_feed_history
      get_hardfork_version
      set_block_applied_callback
      get_discussions_by_author_before_date
      get_discussions_by_hot
      get_discussions_by_payout
      get_discussions_by_trending
      get_recovery_request
      get_reward_fund
      get_chain_properties
      get_witnesses_by_vote
      get_account_references
      get_post_discussions_by_payout
      get_active_witnesses
      get_ops_in_block
      get_discussions_by_created
      get_discussions_by_active
      get_account_count
      get_owner_history
      get_next_scheduled_hardfork
      get_savings_withdraw_from

lib/Steemit/WsClient.pm  view on Meta::CPAN

   return $self->_broadcast_transaction($operation);
}

=head2 delete_comment

   $steem->delete_comment(
      author => $author,
      permlink => $permlink
   )

you need the permlink
author will be filled with the user of your posting key if missing

=cut

sub delete_comment {
   my( $self, %params ) = @_;

   my $permlink = $params{permlink} or die "permlink missing for comment";

   my $author   = $params{author} // $self->get_key_references([$self->public_posting_key])->[0][0];

   my $operation = [
      delete_comment => {
            "author"          => $author,
            "permlink"        => $permlink,
      }
   ];
   return $self->_broadcast_transaction($operation);
}


sub _broadcast_transaction {
   my( $self, @operations ) = @_;

   my $properties = $self->get_dynamic_global_properties();

   my $block_number  = $properties->{last_irreversible_block_num};
   my $block_details = $self->get_block( $block_number );

   my $ref_block_id  = $block_details->{previous},

   my $time          = $properties->{time};
   #my $expiration = "2018-02-24T17:00:51";#TODO dynamic date
   my ($year,$month,$day, $hour,$min,$sec) = split /\D/, $time;
   require Date::Calc;
   my $epoch = Date::Calc::Date_to_Time($year,$month,$day, $hour,$min,$sec);
   ($year,$month,$day, $hour,$min,$sec) = Date::Calc::Time_to_Date($epoch + 600 );
   my $expiration = "$year-$month-$day".'T'."$hour:$min:$sec";

   my $transaction = {
      ref_block_num => ( $block_number - 1 )& 0xffff,
      ref_block_prefix => unpack( "xxxxV", pack('H*',$ref_block_id)),
      expiration       => $expiration,
      operations       => [@operations],
      extensions => [],
      signatures => [],
   };
   my $serialized_transaction = $self->_serialize_transaction_message( $transaction );

   my $bin_private_key = $self->plain_posting_key;
   require Steemit::ECDSA;
   my ( $r, $s, $i ) = Steemit::ECDSA::ecdsa_sign( $serialized_transaction, Math::BigInt->from_bytes( $bin_private_key ) );
   $i += 4;
   $i += 27;

   my $signature = join('', map { unpack 'H*', $_ } ( pack("C", $i ), map { $_->as_bytes} ($r,$s )) );
   unless( Steemit::ECDSA::is_signature_canonical_canonical( pack "H*", $signature ) ){
      die "signature $signature is not canonical";
   }

   $transaction->{signatures} = [ $signature ];


   $self->_request('network_broadcast_api','broadcast_transaction_synchronous',$transaction);
}

sub public_posting_key {
   my( $self ) = @_;
   unless( $self->{public_posting_key} ){
      require Steemit::ECDSA;
      my $bin_pubkey = Steemit::ECDSA::get_compressed_public_key( Math::BigInt->from_bytes( $self->plain_posting_key ) );
      #TODO use the STM from dynamic lookup in get_config or somewhere
      require Crypt::RIPEMD160;
      my $rip = Crypt::RIPEMD160->new;
      $rip->reset;
      $rip->add($bin_pubkey);
      my $checksum = $rip->digest;
      $rip->reset;
      $rip->add('');
      $self->{public_posting_key} = "STM".Steemit::Base58::encode_base58($bin_pubkey.substr($checksum,0,4));
   }

   return $self->{public_posting_key}
}


sub _transform_private_key {
   my( $self ) = @_;
   die "posting_key missing" unless( $self->posting_key );

   my $base58 = $self->posting_key;

   require Steemit::Base58;
   my $binary = Steemit::Base58::decode_base58( $base58 );


   my $version            = substr( $binary, 0, 1 );
   my $binary_private_key = substr( $binary, 1, -4);
   my $checksum           = substr( $binary, -4);
   die "invalid version in wif ( 0x80 needed ) " unless $version eq  pack "H*", '80';

   require Digest::SHA;
   my $generated_checksum = substr( Digest::SHA::sha256( Digest::SHA::sha256( $version.$binary_private_key )), 0, 4 );

   die "invalid checksum " unless $generated_checksum eq $checksum;

   return $binary_private_key;
}

sub _serialize_transaction_message  {
   my ($self,$transaction) = @_;

   my $serialized_transaction;

   $serialized_transaction .= pack 'v', $transaction->{ref_block_num};

   $serialized_transaction .= pack 'V', $transaction->{ref_block_prefix};

   require Date::Calc;
   #2016-08-08T12:24:17
   my @dates = split /\D/, $transaction->{expiration} ;
   my $epoch = Date::Calc::Date_to_Time( @dates);

   $serialized_transaction .= pack 'L', $epoch;

   $serialized_transaction .= pack "C", scalar( @{ $transaction->{operations} });

   require Steemit::OperationSerializer;
   my $op_ser = Steemit::OperationSerializer->new;

   for my $operation ( @{ $transaction->{operations} } ) {

      my ($operation_name,$operations_parameters) = @$operation;
      $serialized_transaction .= $op_ser->serialize_operation(
         $operation_name,
         $operations_parameters,
      );
   }

   #extentions in case we realy need them at some point we will have to implement this is a less nive way ;)
   die "extentions not supported" if $transaction->{extensions} and $transaction->{extensions}[0];
   $serialized_transaction .= pack 'H*', '00';

   return pack( 'H*', ( '0' x 64 )).$serialized_transaction;
}






=head1 REPOSITORY

L<https://github.com/snkoehn/perlSteemit>


=head1 AUTHOR

snkoehn, C<< <snkoehn at cpan.org> >>

=head1 BUGS

Please report any bugs or feature requests to C<bug-steemit at rt.cpan.org>, or through
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Steemit::WsClient>.  I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.




( run in 2.033 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )