--- /dev/null
+#!/usr/bin/perl -w
+
+use v5.16;
+use Mojo::UserAgent -signatures;
+use Mojo::JSON qw(from_json);
+use Mojo::File qw(curfile);
+use Mojo::Util qw(getopt dumper);
+use POSIX;
+
+my $board_id = 652041;
+my $my_name = 'Yenya';
+my $day = (localtime(time))[3];
+my $mode = 'r';
+my $cached = curfile->sibling('cache.json');
+getopt
+ 'n|name=s' => \$my_name,
+ 'd|day=i' => \$day,
+ '1|first-task' => sub { $mode = '1' },
+ '2|second-task' => sub { $mode = '2' },
+ 'r|relative' => sub { $mode = 'r' },
+ 'b|board=i' => \$board_id
+or die "Usage: $0 [-n username] [-1|-2|-r] [-b board_id]\n";
+
+if (!$cached->stat || time - $cached->stat->mtime > 900) {
+ warn "Downloading cache.json\n";
+ chomp(my $cookie = curfile->sibling('cookie')->slurp);
+ my $res = Mojo::UserAgent->new->get(
+ "https://adventofcode.com/2021/leaderboard/private/view/$board_id.json",
+ { Cookie => $cookie }
+ )->result;
+
+ $cached->spurt($res->body);
+}
+
+my $json = from_json $cached->slurp;
+
+
+my %times;
+my $my_time;
+for my $name (keys %{ $json->{members} }) {
+ my $member = $json->{members}->{$name};
+ my $level = $member->{completion_day_level}->{$day}
+ or next;
+ my $time;
+ if ($mode eq 'r') {
+ next if !$level->{2};
+ $time = $level->{2}->{get_star_ts} - $level->{1}->{get_star_ts};
+ } elsif ($mode eq '1') {
+ next if !$level->{1};
+ $time = $level->{1}->{get_star_ts};
+ } elsif ($mode eq '2') {
+ next if !$level->{2};
+ $time = $level->{2}->{get_star_ts};
+ }
+ my $id_or_name = $member->{name} // 'Anonymous user #' . $member->{id};
+ $times{ $id_or_name } = $time;
+ $my_time = $time
+ if $id_or_name eq $my_name;
+}
+
+for my $name (sort { $times{$a} <=> $times{$b} } keys %times) {
+ $my_time //= $times{$name};
+ printf("%6d %6d %s\n", $times{$name}, $times{$name} - $my_time, $name);
+}