Skip to content

Instantly share code, notes, and snippets.

@lillesvin
Created June 16, 2022 22:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lillesvin/b686f6605f84cbaa424098a9401dfbe8 to your computer and use it in GitHub Desktop.
Save lillesvin/b686f6605f84cbaa424098a9401dfbe8 to your computer and use it in GitHub Desktop.
iZBOT 2 Leaderboard Viewer
=begin
Leaderboard scraper for iZBOT 2 (but can probably be adapted
for other games using Steam leaderboards.
WARNING: USE AT YOUR OWN RISK! THIS SCRAPES STEAM FOR THE
LEADERBOARD DATA, WHICH IS PROBABLY AGAINST THE STEAM TOS.
SETUP: First of all, you'll need Ruby (probably version 2.7
or above), and then you'll need 'oga' (`gem install oga`).
USAGE: Run `ruby izbot2-lb.rb --help` for that.
=end
require 'oga'
require 'net/http'
require 'optparse'
options = {
ranks: 5,
debug: false,
level: nil
}
OptionParser.new do |parser|
parser.banner = "Usage: #{__FILE__} [options] <level no.>|all"
parser.on("-r", "--ranks INT", "Number of ranks/places to get (default: #{options[:ranks]})") do |n|
options[:ranks] = n.to_i
end
parser.on("-d", "--debug", "Show debug information (default: #{options[:debug]})") do |d|
options[:debug] = true
end
parser.on("-h", "--help", "Show this help and exit") do |h|
puts parser
exit
end
end.parse!
options[:level] = ARGV.shift
if options[:level].nil?
puts "Please supply a level no. or 'all'."
exit
end
$DEBUG = options[:debug]
def debug(str)
puts str if $DEBUG
end
class Izbot2Leaderboards
def initialize(ranks)
@base_url = "https://steamcommunity.com/stats/1452790/leaderboards/"
@first_level = "https://steamcommunity.com/stats/1452790/leaderboards/8014483/"
@ranks = ranks
debug("Base URL: #{@base_url}")
debug("First level: #{@first_level}")
debug("Ranks: #{@ranks}")
@levels = []
body = Net::HTTP.get(URI.parse(@first_level))
doc = Oga.parse_html(body)
doc.xpath('//select[@id="lbID"]/option').each do |level|
level_no = level.text.gsub(/Room\s([0-9]+)\b.*$/, "\\1").to_i
@levels[level_no] = level["value"]
end
end
def get_level(level_no)
if level_no == "all"
debug("Getting all levels...")
(1..80).each do |level|
format_board(get_level_board(level))
puts
end
return
end
debug("Getting level %s (id: %s)" % [level_no, @levels[level_no.to_i]])
format_board(get_level_board(level_no.to_i))
end
def get_level_board(level_no)
if @levels[level_no].nil?
return nil
end
body = Net::HTTP.get(URI.parse(@base_url + @levels[level_no]))
{
level: level_no,
entries: extract_entries(body)
}
end
def extract_entries(body)
e = []
entries = Oga.parse_html(body).xpath('//div[@class="lbentry"]')[0...@ranks]
entries.each do |entry|
e << extract_entry(entry)
end
e
end
def extract_entry(entry)
rank = entry.xpath('div[@class="relativeRank"]/div[@class="rR"]').text
player = entry.xpath('div[@class="player"]/div[@class="playerLink"]/a[@class="playerName"]').text
score = entry.xpath('div[@class="score"]').text.strip
{
rank: rank,
player: player,
score: score
}
end
def format_board(board)
return if board.nil?
puts "Level %s" % board[:level]
first = nil
board[:entries].each do |entry|
if board[:entries].first == entry
first = entry
to_first = nil
else
to_first = time_diff(first[:score], entry[:score])
end
puts "%3s %11s (%10s) %s" % [entry[:rank], entry[:score], to_first, entry[:player]]
end
end
def time_diff(first, second)
fmin, fsec = first.split(/:/)
smin, ssec = second.split(/:/)
d = ((smin.to_i * 60) + ssec.to_f) - ((fmin.to_i * 60) + fsec.to_f)
if d > 0
n = "+"
elsif d == 0
n = " "
else
n = "-"
end
"%s%02d:%06.3f" % [n, d / 60, d % 60]
end
end
lb = Izbot2Leaderboards.new(options[:ranks])
lb.get_level(options[:level])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment