Hive Developer Portal
Get Post Details
This tutorial fetches the contents of a single post and explains all data related to that post.
Full, runnable src of Get Post Details can be downloaded as part of: tutorials/ruby (or download just this tutorial: devportal-master-tutorials-ruby-05_get_post_details.zip).
Intro
This tutorial fetches the contents of a single post and explains all data related to that post.
We will also describe the most commonly used fields from the response object.
Also see:
Sections
- Making the api call - Use
get_content
to a specific post- Example api call - make the call in code
- Example api call using script - using our tutorial script
- Example Output - output from a successful call
- Post Fields - General use of the method to determine …
parent_author
- if the content is a root post or replylast_update
andcreated
- if the content has been modifiedcashout_time
- if the content has reached payoutbeneficiaries
- reward routes other accountsactive_votes
- all votes appliedjson_metadata
- things liketags
andapp
- To Run - Running the example.
Making the api call
To request a specific post we use the get_content
method:
api = Radiator::Api.new
api.get_content(author, permlink) do |content|
# .
# ... your code here
# .
end
Example api call
If we want to get the post “announcing-the-launch-of-hive-blockchain” by user @hiveio
api.get_content("hiveio", "announcing-the-launch-of-hive-blockchain") do |content| ...
Example api call using script
And to do the same with our tutorial script
ruby get_post_details.rb https://hive.blog/communityfork/@hiveio/announcing-the-launch-of-hive-blockchain
Example Output
From the example we get the following output from our script
Post by hiveio
title: Announcing the Launch of Hive Blockchain
permlink: announcing-the-launch-of-hive-blockchain
category: communityfork
body_length: 10337 (1738 words)
posted at: 2020-03-17T23:30:54, active at: 2020-10-12T05:25:03
children: 978
net_rshares: 0
vote_rshares: 0
payout:
max_accepted_payout: 0.000 HBD
percent_hbd: 100.00 %
payout at: 2020-03-24T23:30:54 (358.0 days ago)
author_rewards: 0.000 HBD
curator_payout_value: 0.000 HBD
total_payout_value: 0.000 HBD
promoted: 0.000 HBD
total_vote_weight: 0
reward_weight: 100.00 %
net_votes: 1337, upvotes: 997, downvotes: 3, unvotes: 0, total: 1000, top voter: blocktrades
allow_replies: true
allow_votes: true
allow_curation_rewards: true
author_reputation: 83492228581467
tags: communityfork, hive, steemcommunity, steem
app: steempeak/2020.03.6
Post fields
Most console applications that use the get_content
method are probably looking for the body
field. But there are many other fields to look at. Let’s break them down by use:
parent_author
In our script (get_post_details.rb
), we use the ruby statement:
content.parent_author.empty?
With the above idiom, your application can determine if the content is a root post or reply. If it’s empty, then you’re working with a root post, otherwise, it’s a reply.
Once you know you’re dealing with a reply, other fields can be useful for additional details. For instance, root_author
, root_permlink
, and root_title
can be used to figure out what the original post details are, even if the reply is deeply nested.
last_update
and created
In our script, we use the ruby statement:
content.last_update == content.created
With the above idiom, your application can determine if the content has been modified since it was originally posted. If they are the same, then there has been no modification.
cashout_time
In our script, we use the ruby statement:
(cashout = Time.parse(content.cashout_time + 'Z') - Time.now.utc) > 0
With the above idiom, you can use cashout_time
to determine if the content has reached payout. If cashout_time
is in the future, the content has not been paid yet. You can determine the possible future payout by inspecting pending_payout_value
.
You will note that we must parse the string found in content.cashout_time
by appending Z
(Zulu Time, aka UTC) in order for Time.parse
to get the right timezone.
Even before payout, you can determine what the max_accepted_payout
is. Most often, this is set to one of two values by the author:
1000000.000 HBD
- Accepted Payout0.000 HBD
- Declined Payout
In addition to max_accepted_payout
, the author may specify how much of the author reward should be in HIVE Power or liquid rewards. The most common settings are:
10000
- Maximum Liquid Reward0
- HIVE Power Only
Once the payout time has arrived, it’s possible to determine the split between author and curation by inspecting at author_rewards
and curator_payout_value
.
beneficiaries
In our script, we use the ruby statement:
content.beneficiaries.any?
Some content will have a beneficiaries
array. This is used to determine reward routes any account, up to eight. Payouts are in HIVE Power and are expressed as a reward percentage of the author reward.
To display a list of who the beneficiaries are, use the following ruby code, as seen in the example:
content.beneficiaries.each do |beneficiary|
puts "\t\t#{beneficiary.account}: #{'%.2f %' % (beneficiary.weight / 100.0)}"
end
Note, if you just want an array of beneficiary account names, this will work in a pinch:
accounts = content.beneficiaries.map do |beneficiary|
beneficiary.account
end
active_votes
In our script, we use the ruby statements:
votes = content.active_votes
upvotes = votes.select { |v| v.percent > 0 }.size
downvotes = votes.select { |v| v.percent < 0 }.size
unvotes = votes.select { |v| v.percent == 0 }.size
top_voter = votes.sort_by { |v| v.rshares.to_i }.last.voter
The above idiom splits all vote types and identifies the top voter. This is because the active_votes
field is an array that shows all votes applied to the content, including upvotes, downvotes, and unvotes (where a vote previously cast is revoked).
json_metadata
In our script, we use the ruby statements:
metadata = JSON[content.json_metadata || '{}'] rescue {}
tags = metadata['tags'] || []
app = metadata['app']
As you can see from the above example, json_metadata
starts out as a string of JSON that can be parsed to determine things like tags
and app
. Other data may be present, depending on the application that created the content.
Note, we’re using rescue
in case the json_metadata
string contains invalid JSON because there is no validation performed on this field by the blockchain when content is broadcasted.
id
- Unique identifier that is mostly an implementation detail (best to ignore). To uniquely identify content, it's best to useauthor/permlink
.author
- The author account name of the content.permlink
- Permanent link of the content, must be unique in the scope of theauthor
.category
- The main category/tag this content belongs to.root_author
- Used in conbination withroot_permlink
.root_permlink
- In conbination withroot_author
, distinctly references discussion this content is part of.parent_author
- Parent author, in case this content is a comment (reply).parent_permlink
- Parent permanent link, this will be the same ascategory
for posts and will contain thepermlink
of the content being replied to in the case of a comment.title
- Title of the content.body
- Body of the content.json_metadata
- JSON metadata that holds extra information about the content. Note: The format for this field is not guaranteed to be valid JSON.last_update
- The date and time of the last update to this content.created
- The date and time this content was created.active
- The last time this content was "touched" by voting or reply.last_payout
- Time of last payout.depth
- Used to track max nested depth.children
- Used to track the total number of children, grandchildren, etc. ...net_rshares
- Reward is proportional to liniar rshares, this is the sum of all votes (positive and negative reward sum)abs_rshares
- This was used to track the total absolute weight of votes for the purpose of calculatingcashout_time
.vote_rshares
- Total positive rshares from all votes. Used to calculate delta weights. Needed to handle vote changing and removal.children_abs_rshares
- This was used to calculate cashout time of a discussion.cashout_time
- 7 days from thecreated
date.max_cashout_time
- Unused.total_vote_weight
- The total weight of voting rewards, used to calculate pro-rata share of curation payouts.reward_weight
- Weight/percent of reward.total_payout_value
- Tracks the total payout this content has received over time, measured in the debt asset.curator_payout_value
- Tracks the curator payout this content has received over time, measured in the debt asset.author_rewards
- Tracks the author payout this content has received over time, measured in the debt asset.net_votes
- Net positive votesroot_comment
- ID of the original content.max_accepted_payout
- Value of the maximum payout this content will receive.percent_hbd
- The percent of Hive Dollars to key, unkept amounts will be received as HIVE Power.allow_replies
- Allows content to disable replies.allow_votes
- Allows content to receive votes.allow_curation_rewards
- Allows curators of this content receive rewards.beneficiaries
- The list of up to 8 beneficiary accounts for this content as well as the percentage of the author reward they will receive in HIVE Power.url
- The end of the url to this content.root_title
- Title of the original content (useful in replies).pending_payout_value
- Pending payout amount if 7 days has not yet elapsed.total_pending_payout_value
- Total pending payout amount if 7 days has not yet elapsed.active_votes
- The entire voting list array, including upvotes, downvotes, and unvotes; used to calculatenet_votes
.replies
- Unused.author_reputation
- Author's reputation.promoted
- If post is promoted, how much has been spent on promotion.body_length
- Total content length.reblogged_by
- Unused.
Final code:
require 'rubygems'
require 'bundler/setup'
Bundler.require
url = ARGV[0]
slug = url.split('@').last
author, permlink = slug.split('/')
api = Radiator::Api.new
api.get_content(author, permlink) do |content|
if content.author.empty?
puts "Could not find content with url: #{url}"
exit
end
if content.parent_author.empty?
puts "Post by #{content.author}"
else
print "Reply by #{content.author} to #{content.parent_author}/#{content.parent_permlink}"
if content.parent_author == content.root_author && content.parent_permlink == content.root_permlink
# This is a reply direcly to the root post, no need to specify.
print "\n"
else
# This is a reply to a reply, so we can specify the root post info.
puts " in #{content.root_author}/#{content.root_permlink} (#{content.root_title})"
end
# Used to track max nested depth of replies.
puts "\tdepth: #{content.depth}"
end
%w(title permlink category).each do |key|
puts "\t#{key}: #{content[key]}"
end
puts "\tbody_length: #{content.body.size} (#{content.body.split(/\W+/).size} words)"
# The date and time this content was created.
print "\tposted at: #{content.created}"
if content.last_update == content.created
# The date and time of the last update to this content.
print ", updated at: #{content.last_update}"
end
# The last time this content was "touched" by voting or reply.
puts ", active at: #{content.active}"
# Used to track the total number of children, grandchildren, etc. ...
puts "\tchildren: #{content.children}"
# Reward is proportional to liniar rshares, this is the sum of all votes
# (positive and negative reward sum)
puts "\tnet_rshares: #{content.net_rshares}"
# Total positive rshares from all votes. Used to calculate delta weights.
# Needed to handle vote changing and removal.
puts "\tvote_rshares: #{content.vote_rshares}"
puts "\tpayout:"
# Value of the maximum payout this content will receive.
puts "\t\tmax_accepted_payout: #{content.max_accepted_payout}"
# The percent of Hive Dollars to keep, unkept amounts will be received as
# HIVE Power.
puts "\t\tpercent_hbd: #{'%.2f %%' % (content.percent_hbd / 100.0)}"
# 7 days from the created date.
if (cashout = Time.parse(content.cashout_time + 'Z') - Time.now.utc) > 0
cashout = cashout / 86400 # convert from seconds to days
puts "\t\tpays in: #{('%.1f' % (cashout))} days"
# Pending payout amount if 7 days has not yet elapsed.
puts "\t\tpending_payout_value: #{content.pending_payout_value}"
# Total pending payout amount if 7 days has not yet elapsed.
puts "\t\ttotal_pending_payout_value: #{content.total_pending_payout_value}"
else
# The date and time of the last update to this content.
print "\t\tpayout at: #{content.last_payout}"
payout_elapsed = Time.now.utc - Time.parse(content.last_payout + 'Z')
payout_elapsed = payout_elapsed / 86400 # convert from seconds to days
puts " (#{('%.1f' % payout_elapsed)} days ago)"
# Tracks the author payout this content has received over time, measured in
# the debt asset.
puts "\t\tauthor_rewards: #{('%.3f HBD' % (content.author_rewards / 1000.0))}"
# Tracks the curator payout this content has received over time, measured in
# the debt asset.
puts "\t\tcurator_payout_value: #{content.curator_payout_value}"
# Tracks the total payout this content has received over time, measured in
# the debt asset.
puts "\t\ttotal_payout_value: #{content.total_payout_value}"
end
# If post is promoted, how much has been spent on promotion.
puts "\tpromoted: #{content.promoted}"
# The list of up to 8 beneficiary accounts for this content as well as the
# percentage of the author reward they will receive in HIVE Power.
if content.beneficiaries.any?
puts "\tbeneficiaries:"
content.beneficiaries.each do |beneficiary|
puts "\t\t#{beneficiary.account}: #{'%.2f %%' % (beneficiary.weight / 100.0)}"
end
end
# The total weight of voting rewards, used to calculate pro-rata share of
# curation payouts.
puts "\ttotal_vote_weight: #{content.total_vote_weight}"
# Weight/percent of reward.
puts "\treward_weight: #{'%.2f %%' % (content.reward_weight / 100.0)}"
# Net positive votes
print "\tnet_votes: #{content.net_votes}"
# The entire voting list array, including upvotes, downvotes, and unvotes;
# used to calculate net_votes.
votes = content.active_votes
upvotes = votes.select { |v| v.percent > 0 }.size
downvotes = votes.select { |v| v.percent < 0 }.size
unvotes = votes.select { |v| v.percent == 0 }.size
top_voter = votes.sort_by { |v| v.rshares.to_i }.last.voter
print ", upvotes: #{upvotes}"
print ", downvotes: #{downvotes}"
print ", unvotes: #{unvotes}"
print ", total: #{votes.size}"
puts ", top voter: #{top_voter}"
# Allows content to disable replies.
puts "\tallow_replies: #{content.allow_replies}"
# Allows content to receive votes.
puts "\tallow_votes: #{content.allow_votes}"
# Allows curators of this content receive rewards.
puts "\tallow_curation_rewards: #{content.allow_curation_rewards}"
# Author's reputation.
puts "\tauthor_reputation: #{content.author_reputation}"
# JSON metadata that holds extra information about the content. Note: The
# format for this field is not guaranteed to be valid JSON, so we use rescue
# here to deal with this possibility.
metadata = JSON[content.json_metadata || '{}'] rescue {}
tags = metadata['tags'] || []
app = metadata['app']
puts "\ttags: #{tags.join(', ')}" if tags.any?
puts "\tapp: #{app}" if !!app
end
To Run
First, set up your workstation using the steps provided in Getting Started. Then you can create and execute the script (or clone from this repository):
*<content-url>
git clone https://gitlab.syncad.com/hive/devportal.git
cd devportal/tutorials/ruby/05_get_post_details
bundle install
ruby get_post_details.rb <content-url>