How to Find Flag Wars with Ruby

in #radiator7 years ago

Sometimes an author posts something that the community finds controversial. For these situations, we have the "flag" option. But just flagging a post is not enough to get classified as a flag war by this script.

For the purpose of this script, a flag war is defined as:

  • Post is approaching cashout.
  • Post has a non-zero payout to the author.
  • Post has a flag.
  • Post has a comment by the person who issued the flag.
    • Both flagged and commented from someone who has reputation greater than 25.

So we're looking for a combination of flags and comments. When someone flags a post, they also have to put their reputation at risk by writing a comment. Only then will this script find them.

It's a double-edged sword. This script is agnostic as to why the post is being flagged. It doesn't have a condition for who is flagging. We might see results for:

  • Plagiarism - The author has misrepresented authorship. The community has noticed evidence and taken action but there's still a payout. For this situation, this script result is useful in highlighting the fact that this post is still getting a payout when it shouldn't.
  • Payout Disagreement - Certain people just disagree with the payout. For this situation, this script result is useful in highlighting the fact that this post isn't getting a payout when perhaps it should.
  • Circlejerks - Trivial threads where nobody's really being serious about anything of substance (hopefully these are mostly filtered by the payout amounts).

If you're not into the command line, I've also updated my Ganymede project and added this functionality. To try this logic out yourself, browse to one of these ...

... then click the Flag War tab.


As always, we use Radiator with bundler. You can get bundler with this command:

$ gem install bundler

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]

First, make a project folder:

$ mkdir radiator
$ cd radiator

Create a file named Gemfile containing:

source 'https://rubygems.org'
gem 'radiator', github: 'inertia186/radiator'

Then run the command:

$ bundle install

Create a file named flagwar.rb containing:

require 'rubygems'
require 'bundler/setup'

Bundler.require

@api = Radiator::Api.new

@downvoter_names = []
@downvoter_accounts = nil

def base_value(raw)
  raw.split(' ').first.to_i
end

def downvoter_accounts
  if @downvoter_names.any? && @downvoter_accounts.nil?
    response = @api.get_accounts(@downvoter_names.uniq)
    @downvoter_accounts = response.result
  end
  
  @downvoter_accounts
end

def commented_any?(author)
  downvoter_accounts.map do |account|
    # Author has made at least one post (thus exposed to flag).
    if account.name == author && account.post_count > 0
      # Author reputation is above 25, meaning their comments are not being
      # consistently flagged.
      if to_rep(account.reputation) > 25
        author
      end
    end
  end.reject(&:nil?).include? author
end

def commented_on?(options = {})
  return false unless commented_any? options[:author]
  
  response = @api.get_content_replies(options[:parent_author], options[:parent_permlink])
  commented = response.result.map(&:author).include? options[:author]
  
  commented
end

def to_rep(raw)
  raw = raw.to_i
  neg = raw < 0
  level = Math.log10(raw.abs)
  level = [level - 9, 0].max
  level = (neg ? -1 : 1) * level
  level = (level * 9) + 25
  level.to_i
end

options = {
  limit: 100
}

tags = ARGV
by_cashout = []

if tags.none?
  response = @api.get_discussions_by_cashout(options)
  by_cashout += response.result
else
  tags.each do |tag|
    options[:tag] = tag
    response = @api.get_discussions_by_cashout(options)
    result_size = response.result.size
    by_cashout += response.result
  end
end

by_cashout = by_cashout.uniq

# prefetch all of the downvoter accounts.
by_cashout.each do |comment|
  downvotes = comment.active_votes.each do |vote|
    if vote.percent < 0
      @downvoter_names << vote.voter
    end
  end
end

discussions = by_cashout.map do |comment|
  next unless comment.children > 0 # nobody bothered to comment, don't care
  next if base_value(comment.max_accepted_payout) == 0 # payout declined, don't care
  next if (pending_payout_value = base_value(comment.pending_payout_value)) < 0.001 # no author payout, don't care
  next if (base_total_pending_payout_value = base_value(comment.total_pending_payout_value)) < 0.001 # no payout, don't care
  
  votes = comment.active_votes
  upvotes = votes.map do |vote|
    vote if vote.percent > 0
  end.reject(&:nil?)
  downvotes = votes.map do |vote|
    vote if vote.percent < 0
  end.reject(&:nil?)
  unvotes = votes.map do |vote|
    vote if vote.percent == 0
  end.reject(&:nil?)
  
  next if upvotes.none? # no upvotes, don't care
  next if downvotes.none? # no downvotes, don't care

  # Looking up downvotes that qualify.
  qualified_downvotes = votes.map do |vote|
    vote if vote.percent < 0 && commented_on?(author: vote.voter, parent_author: comment.author, parent_permlink: comment.permlink)
  end.reject(&:nil?)
  
  next if qualified_downvotes.none? # no qualified downvotes, don't care

  {
    amount: base_total_pending_payout_value,
    url: comment.url,
    from: comment.author,
    slug: comment.url.split('@').last,
    timestamp: Time.parse(comment.created + 'Z'),
    votes: comment.active_votes.size,
    upvotes: upvotes.size,
    downvotes: downvotes.size,
    unvotes: unvotes.size,
    title: comment.title,
    content: comment.body,
    author_reputation: to_rep(comment.author_reputation)
  }
end.reject(&:nil?)

if discussions.any?
  puts "Flagwar detected:"
  discussions.each do |discussion|
    puts "https://steemit.com#{discussion[:url]}"
  end
else
  puts "Flagwar not detected."
end

Then run it:

$ ruby flagwar.rb

The expected output will be something like this:

Flagwar detected:
https://steemit.com/spam/@berniesanders/harassment-by-matrixdweller
https://steemit.com/nature/@favorit/flowers-that-grow-in-my-garden-amazing-flower-85

ruby

See my previous Ruby How To posts in: #radiator #ruby

Sort:  

What is this operatong system? (gem install ...)

Pretty much any modern platform is supported. Depending on your platform, I recommend starting here:

https://rubygems.org/pages/download

Let me know if you get anywhere.