How to Find Flag Wars with Ruby
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
Good tool you made. Keep it up!
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.