So I started writing out my first Steemit Bot. It is as simple as it can get. Wakes up every 65 minutes and looks at best bet from hot posts api.steemjs.com/getState?path=/hot.
public static final String API_URI = "https://api.steemjs.com/getState?path=/hot";
@Test
public void testLoadHotPosts() throws IOException {
String hotPosts = Utils.getHotPosts(API_URI);
ObjectMapper objectMapper = new ObjectMapper();
HotPostsReply hotPostsReply = objectMapper.readValue(hotPosts.toString(), HotPostsReply.class);
assertNotNull(hotPostsReply);
assertNotNull(hotPostsReply.getCurrentRoute());
assertNotNull(hotPostsReply.getProps());
assertNotNull(hotPostsReply.getProps().getCurrentSupply());
assertNotNull(hotPostsReply.getProps().getMaxVirtualBandwidth());
assertNotNull(hotPostsReply.getContent());
assertFalse(hotPostsReply.getContent().isEmpty());
}
@Value("${account}")
private String account;
@Value("${posting.key}")
private String postingKey;
@Value("${active.key}")
private String activeKey;
@Value("${phrases}")
private String phrases;
@Value("${comment.tag}")
private String commentTag;
@Value("${hour.range}")
private static int hourRange;
@Value("${hotpost.url}")
private String hotPostsURL;
/**
* Checks if already voted this post
*
* @param steemJ
* @param author
* @param permlink
* @return
* @throws SteemResponseException
* @throws SteemCommunicationException
*/
private boolean alreadyVoted(SteemJ steemJ, AccountName author, Permlink permlink) throws SteemResponseException, SteemCommunicationException {
return steemJ.getActiveVotes(author, permlink)
.stream()
.anyMatch(a -> a.getVoter().getName().equals(account));
}
/**
* Calculate the bandwidth ratio
* A ratio greater than 1 means the account has bandwidth
*
* @param steemJ
* @param accountName
* @return
* @throws SteemCommunicationException
* @throws SteemResponseException
*/
public static boolean isBandwidthAvailable(SteemJ steemJ, String accountName) throws Exception {
final ExtendedAccount theAccount = getAccountInfo(steemJ, accountName);
final BigDecimal account_vesting_shares = BigDecimal.valueOf(theAccount.getVestingShares().getAmount() + theAccount.getReceivedVestingShares().getAmount());
final BigDecimal account_average_bandwidth = BigDecimal.valueOf(theAccount.getAverageBandwidth());
final BigDecimal total_vesting_shares = BigDecimal.valueOf(steemJ.getDynamicGlobalProperties().getTotalVestingShares().getAmount());
final BigDecimal market_average_bandwidth = new BigDecimal(steemJ.getDynamicGlobalProperties().getMaxVirtualBandwidth());
return account_vesting_shares.multiply(market_average_bandwidth).subtract(account_average_bandwidth.multiply(total_vesting_shares)).doubleValue() > 0;
}
/**
* A scheduled method to wake up the bot every 65 minutes
*
* @throws Exception
*/
@Scheduled(fixedDelay = 1000 * 60 * 65)
public void commentAndVote() throws Exception {
SteemJ steemJ = getSteemJInstance(account, postingKey, activeKey);
words = Arrays.asList(phrases.split(","));
final HotPostsReply hotPostsReply = objectMapper.readValue(getHotPosts("https://api.steemjs.com/getState?path=/hot").toString(), HotPostsReply.class);
hotPostsReply.getContent()
.values()
.stream()
.sorted(getComparatorByTime())
.filter(c -> postsLessThanThirtyMinutes(c))
.sorted((c1, c2) -> maximumSharesByVotesRatio(c1, c2))
.findFirst()
.ifPresent(post -> commentAndVote(steemJ, post));
}
/**
* Gets the difference between net shares/net vots for two posts. Helpful for sorting
*
* @param c1
* @param c2
* @return
*/
private int maximumSharesByVotesRatio(ContentEntry c1, ContentEntry c2) {
return Double.valueOf((double) (c1.getNetRshares() / c1.getNetVotes() - c2.getNetRshares() / c2.getNetVotes()) / 1000d).intValue();
}
/**
* Checks if the post was created within last 30 minutes
*
* @param c
* @return
*/
private boolean postsLessThanThirtyMinutes(ContentEntry c) {
return (new java.util.Date().getTime() - c.getCreated().getDateTimeAsDate().getTime()) / 60000 < 30;
}
/**
* Time comparison for ordering posts
*
* @return
*/
private Comparator<ContentEntry> getComparatorByTime() {
return (c1, c2) -> c2.getCreated().getDateTimeAsDate().compareTo(c1.getCreated().getDateTimeAsDate());
}
/**
* Vote & Comment Scheduling.
* We want to vote exactly after 30 mins have elapsed after the post was created
*
* @param steemJ
* @param post
*/
private void commentAndVote(SteemJ steemJ, ContentEntry post) {
try {
if (isBandwidthAvailable(steemJ, account) && !alreadyVoted(steemJ, post.getAuthor(), post.getPermlink())) {
final long delay = 30 - (new Date().getTime() - post.getCreated().getDateTimeAsDate().getTime()) / 60000;
service.schedule(() -> action(steemJ, post), delay, TimeUnit.MINUTES);
System.out.println(((new java.util.Date().getTime() - post.getCreated().getDateTimeAsDate().getTime()) / 60000) + ":" + delay);
} else {
logger.log(Level.ALL, "Bandwidth Not Available or already voted in similar way...");
}
} catch (Throwable e) {
logger.log(Level.SEVERE, e.getMessage(), e);
}
}
/**
* Actual voting and commenting happens here
*
* @param steemJ
* @param post
*/
private void action(SteemJ steemJ, ContentEntry post) {
try {
steemJ.vote(post.getAuthor(), post.getPermlink(), (short) 100);
steemJ.createComment(post.getAuthor(), post.getPermlink(), words.get(randInt(0, words.size() - 1)) + "!", new String[]{commentTag});
} catch (Throwable e) {
logger.log(Level.SEVERE, e.getMessage(), e);
}
}
Thanks to @dez1337 for developing SteemJ. Very helpful library indeed.
Image Courtesy: