--- /dev/null
+package Y::AoC::UA;
+
+use v5.36;
+
+use Mojo::Base -signatures;
+use Mojo::UserAgent;
+use Mojo::DOM;
+use Y::AoC qw(white);
+
+our $user_agent = 'kas@yenya.net https://www.fi.muni.cz/~kas/git/aoc.git';
+our $cache_dir = '/home/kas/aoc/cache';
+our $cookie;
+
+sub request($url, $args) {
+ chomp($cookie //= Mojo::File->new("$cache_dir/cookie")->slurp);
+
+ my ($cache, $cachefile);
+ if ($args->{cache_to}) {
+ $cachefile = $cache_dir . '/' . $args->{cache_to};
+ $cache = Mojo::File->new("$cachefile");
+
+ if ($args->{max_age}) {
+ $cache->remove
+ if $cache->stat
+ && time - $cache->stat->mtime
+ > $args->{max_age};
+ }
+
+ if ($cache->stat) {
+ say "\n", white('cached'),
+ " response from $cachefile";
+ return Mojo::DOM->new($cache->slurp);
+ }
+ }
+
+ my $res;
+ my %hdrs = (
+ Cookie => $cookie,
+ 'User-Agent' => $user_agent,
+ );
+ my $ua = Mojo::UserAgent->new;
+ if ($args->{form}) {
+ $res = $ua->post($url => \%hdrs => form => $args->{form})
+ ->result;
+ } else {
+ $res = $ua->get($url => \%hdrs)->result;
+ }
+
+ if (!$res->is_success) {
+ say $res->message;
+ say "body:\n", $res->body;
+ die red("http request failed");
+ }
+
+ if ($cache) {
+ $cache->spurt($res->body);
+ # say "Stored response to $cachefile";
+ }
+ return $args->{body} ? $res->body : $res->dom;
+}
+
+sub cache_del($file) {
+ my $f = Mojo::File->new("$cache_dir/$file");
+ return if !$f->stat;
+ $f->move_to("$cache_dir/old-$file");
+}
+
+sub is_cached($file) { Mojo::File->new("$cache_dir/$file")->stat; }
+
+1;
+__END__
+package Y::AoC::UA;
+
+sub new($class, $year, $day) {
+ eval 'require
+}
+
+sub get($ua) {
+1;