Skip to content

Instantly share code, notes, and snippets.

@inertia186
Last active February 26, 2018 21:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save inertia186/acc37590f160842afb5528eb2c63a636 to your computer and use it in GitHub Desktop.
Save inertia186/acc37590f160842afb5528eb2c63a636 to your computer and use it in GitHub Desktop.
Mr. Krabs (`mrkrabs.rb`) is a self-voting bot. The goal is to self-vote using certain rules. See: https://steemit.com/radiator/@inertia/mrkrabsrb-self-voting-bot
Mr. Krabs is a self-voting bot that uses rules you set like posts having 60 votes or 6 days old.
accounts.txt
mrkrabs.log
.rvmrc
  • Title: mrkrabs.rb - Self Voting Bot
  • Tags: radiator ruby steem steemdev curation
  • Notes:

Mr. Krabs is a reimplementation of Dr. Phil, but instead of voting for new articles, it self-votes for posts and comments you write, after certain rules are met. By default, it votes for posts that reach 60 votes or is 6 days old. The default for comments is 6 votes or 6 days old.

Features

  • YAML config.
    • voting_rules
      • vote_weight a static percentage (like 100.00 %)
      • history_limit used to set how far back to look in account history.
      • enable_comments option to vote for post replies (default false).
      • min_post_age allows you to only upvote once a post has reached a certain age
      • min_comment_age allows you to only upvote once a comment has reached a certain age
      • min_post_votes allows you to only upvote once a post has reached a certain number of votes
      • min_comment_votes allows you to only upvote once a comment has reached a certain number of votes
      • poll_time how often to check for new content (in seconds)
    • accounts List of accounts to vote on, e.g: account wif
      • Or use accounts: accounts.txt
        • The format for the file is just: account wif (no leading dash, separated by space)

Overview

The goal is to vote after the rules have been met. Setting 60 votes or 6 hours will have Mr. Krabs vote in either situation, whichever happens first.


Install

To use this Radiator bot:

Linux
$ sudo apt-get update
$ sudo apt-get install ruby-full git openssl libssl1.0.0 libssl-dev
$ sudo apt-get upgrade
$ gem install bundler
macOS
$ gem install bundler

You can try the system version of ruby, but if you have issues with that, use this how-to, and come back to this installation at Step 4:

I've tested it on various versions of ruby. The oldest one I got it to work was:

ruby 2.0.0p645 (2015-04-13 revision 50299) [x86_64-darwin14.4.0]

Setup

First, clone this gist and install the dependencies:

$ git clone https://gist.github.com/acc37590f160842afb5528eb2c63a636.git mrkrabs
$ cd mrkrabs
$ bundle install

Then run it:

$ ruby mrkrabs.rb

Mr. Krabs will now do it's thing. Check here to see an updated version of this bot:

https://gist.github.com/inertia186/acc37590f160842afb5528eb2c63a636


Upgrade

Typically, you can upgrade to the latest version by this command, from the original directory you cloned into:

$ git pull

Usually, this works fine as long as you haven't modified anything. If you get an error, try this:

$ git stash --all
$ git pull --rebase
$ git stash pop

If you're still having problems, I suggest starting a new clone.


Troubleshooting

Problem: What does this error mean?
mrkrabs.yml:1: syntax error, unexpected ':', expecting end-of-input
Solution: You ran ruby mrkrabs.yml but you should run ruby mrkrabs.rb.

Problem: Everything looks ok, but every time Mr. Krabs tries to vote, I get this error:
Unable to vote with <account>.  Invalid version
Solution: You're trying to vote with an invalid key.

Make sure the .yml file voter items have the account name, followed by a space, followed by the account's WIF posting key. Also make sure you have removed the example accounts (social and bad.account are just for testing).

Problem: The node I'm using is down.

Is there a list of nodes?

Solution: Yes, see:

https://steem-third-party.github.io/steem-servers/


*Image Source: [ekarasz.deviantart.com](https://ekarasz.deviantart.com/art/Mr-Krabs-603720464)*

See my previous Ruby How To posts in: /f/ruby

Get in touch!

If you're using Mr. Krabs, I'd love to hear from you. Drop me a line and tell me what you think! I'm @inertia on STEEM and SteemSpeak.

License

I don't believe in intellectual "property". If you do, consider Mr. Krabs as licensed under a Creative Commons CC0 License.

source 'https://rubygems.org'
gem 'radiator'
gem 'awesome_print'
GEM
remote: https://rubygems.org/
specs:
awesome_print (1.8.0)
bitcoin-ruby (0.0.15)
connection_pool (2.2.1)
ffi (1.9.21)
hashie (3.5.7)
json (2.1.0)
little-plugger (1.1.4)
logging (2.2.2)
little-plugger (~> 1.1)
multi_json (~> 1.10)
multi_json (1.13.1)
net-http-persistent (3.0.0)
connection_pool (~> 2.2)
radiator (0.3.15)
awesome_print (~> 1.7, >= 1.7.0)
bitcoin-ruby (~> 0.0, >= 0.0.11)
ffi (~> 1.9, >= 1.9.18)
hashie (~> 3.5, >= 3.5.5)
json (~> 2.0, >= 2.0.2)
logging (~> 2.2, >= 2.2.0)
net-http-persistent (>= 2.5.2)
PLATFORMS
ruby
DEPENDENCIES
awesome_print
radiator
BUNDLED WITH
1.16.1
:voting_rules:
:vote_weight: 100.0 %
:history_limit: 2000
:enable_comments: false
:min_post_age: 518400 # 6 days
:min_comment_age: 518400
:min_post_votes: 60
:min_comment_votes: 6
:poll_time: 600 # polls accounts every 10 minutes
:accounts:
- social 5JrvPrQeBBvCRdjv29iDvkwn3EQYZ9jqfAHzrCyUvfbEbRkrYFC
- bad.account 5XXXBadWifXXXdjv29iDvkwn3EQYZ9jqfAHzrCyUvfbEbRkrYFC
:chain_options:
:chain: golos
:url: https://ws.golos.io
# Mr. Krabs (mrkrabs) watches account posts/comments to self-vote with rules.
#
# See: https://steemit.com/radiator/@inertia/mrkrabs-rb-self-voting-bot
require 'rubygems'
require 'bundler/setup'
require 'yaml'
# require 'irb'
Bundler.require
defined? Thread.report_on_exception and Thread.report_on_exception = true
MAX_AGE = 60 * 60 * 24 * 6.5
@config_path = __FILE__.sub(/\.rb$/, '.yml')
unless File.exist? @config_path
puts "Unable to find: #{@config_path}"
exit
end
def parse_accounts(accounts)
case accounts
when String
raise "Not found: #{accounts}" unless File.exist? accounts
hash = {}
File.open(accounts, 'r').each do |line|
key, value = line.split(' ')
hash[key] = value if !!key && !!hash
end
hash
when Array
a = accounts.map{ |v| v.split(' ')}.flatten.each_slice(2)
return a.to_h if a.respond_to? :to_h
hash = {}
accounts.each_with_index do |e|
key, val = e.split(' ')
hash[key] = val
end
hash
else; raise "Unsupported accounts: #{accounts}"
end
end
@config = YAML.load_file(@config_path)
rules = @config[:voting_rules]
@voting_rules = {
vote_weight: (((rules[:vote_weight] || '100.0 %').to_f) * 100).to_i,
history_limit: rules[:history_limit].to_i,
enable_comments: rules[:enable_comments],
min_post_age: rules[:min_post_age].to_i,
min_comment_age: rules[:min_comment_age].to_i,
min_post_votes: rules[:min_post_votes].to_i,
min_comment_votes: rules[:min_comment_votes].to_i,
poll_time: rules[:poll_time].to_i,
}
@voting_rules = Struct.new(*@voting_rules.keys).new(*@voting_rules.values)
@accounts = parse_accounts(@config[:accounts])
@options = @config[:chain_options]
@options[:logger] = Logger.new(__FILE__.sub(/\.rb$/, '.log'))
@api = nil
def vote(account_name, wif, content)
slug = "#{content.author}/#{content.permlink}"
puts "Found content, ready for self-vote: #{slug}"
tx = Radiator::Transaction.new(wif: wif)
tx.operations << {
type: :vote,
voter: account_name,
author: content.author,
permlink: content.permlink,
weight: @voting_rules.vote_weight
}
ap tx.process(true)
end
def content_type(op)
op.parent_author == '' ? :post : :comment
end
def content_age(trx)
content_created = Time.parse(trx.timestamp + 'Z')
Time.now - content_created
end
def skip?(trx, account_name)
type, op = trx.op
return true unless type == 'comment'
return true if content_age(trx) > MAX_AGE
return true unless op.author == account_name
false
end
def can_vote?(content, trx, account_name)
return false if content.author == '' # content proably deleted
active_votes = content.active_votes
vote_size = active_votes.size
content_age = content_age(trx)
return false if active_votes.map(&:voter).include? account_name
case content_type(content)
when :post
return true if vote_size >= @voting_rules.min_post_votes
return content_age >= @voting_rules.min_post_age
when :comment
return false if !@voting_rules.enable_comments
return true if vote_size >= @voting_rules.min_comment_votes
return content_age >= @voting_rules.min_comment_age
end
false
end
puts "Now polling every #{@voting_rules.poll_time / 60} minutes ..."
loop do
@api ||= Radiator::Api.new(@options)
begin
@accounts.each do |account_name, wif|
puts "Looking for content by #{account_name} ..."
@api.get_account_history(account_name, -1, @voting_rules.history_limit) do |history, error|
ap(error) and break unless error.nil?
history.each do |idx, trx|
type, op = trx.op
next if skip?(trx, account_name)
@api.get_content(op.author, op.permlink) do |content, error|
ap(error) and break unless error.nil?
if can_vote?(content, trx, account_name)
vote(account_name, wif, content)
end
end
end
end
end
sleep @voting_rules.poll_time
rescue => e
@api.shutdown
@api = nil
ap({error: "Unable to poll on current node. Retrying in 5 seconds. Error: #{e}", backtrace: e.backtrace})
sleep 5
end
end
:voting_rules:
:vote_weight: 100.0 %
:history_limit: 10000
:enable_comments: false
:min_post_age: 518400 # 6 days
:min_comment_age: 518400
:min_post_votes: 60
:min_comment_votes: 6
:poll_time: 600 # polls accounts every 10 minutes
:accounts:
- social 5JrvPrQeBBvCRdjv29iDvkwn3EQYZ9jqfAHzrCyUvfbEbRkrYFC
- bad.account 5XXXBadWifXXXdjv29iDvkwn3EQYZ9jqfAHzrCyUvfbEbRkrYFC
:chain_options:
:chain: steem
:url: https://api.steemit.com
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment