Created
June 16, 2022 22:03
-
-
Save lillesvin/b686f6605f84cbaa424098a9401dfbe8 to your computer and use it in GitHub Desktop.
iZBOT 2 Leaderboard Viewer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
=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