From 78136e93bf5e13aabceb8834ab3748187ee74eee Mon Sep 17 00:00:00 2001 From: "Jan \"Yenya\" Kasprzak" Date: Fri, 16 Dec 2022 21:09:03 +0100 Subject: [PATCH] New fancy infrastructure: downloader, inotify watcher, submitter --- 2022/get | 63 ++++++++++++++++++++++++++++ 2022/run | 35 ++++++++++++++++ bashrc | 3 ++ lib/Y/AoC.pm | 26 ++++++++++++ lib/Y/AoC/Task.pm | 102 ++++++++++++++++++++++++++++++++++++++++++++++ lib/Y/AoC/UA.pm | 79 +++++++++++++++++++++++++++++++++++ 6 files changed, 308 insertions(+) create mode 100755 2022/get create mode 100755 2022/run create mode 100644 bashrc create mode 100644 lib/Y/AoC.pm create mode 100644 lib/Y/AoC/Task.pm create mode 100644 lib/Y/AoC/UA.pm diff --git a/2022/get b/2022/get new file mode 100755 index 0000000..760e024 --- /dev/null +++ b/2022/get @@ -0,0 +1,63 @@ +#!/usr/bin/perl -w + +use v5.36; +use strict; + +use Y::AoC qw(grey); +use Y::AoC::UA; + +use Mojo::UserAgent -signatures; +use Mojo::Util qw(getopt); +use Time::ParseDate; + +my $start = '6:00:02'; + +getopt + 'd|day=i' => \my $day, + 'y|year=i' => \my $year; + +$year //= Y::AoC::year; +my $sleep; + +if (!$day) { + my $now = time; + my @now = localtime($now); + if ($now[4] == 11 && $now[2] >= 5 && $now[2] < 7) { + $day = $now[3]; + if ($now[2] == 5 && $now[1] >= 50) { + my $then = parsedate($start); + $sleep = $then - $now; + } + } +} + +die "Use -d day command-line switch.\n" + if !$day; + +my $task = 2*$day-1; +my $url = "https://adventofcode.com/$year/day/$day/input"; +my $dest = $task.'in.txt'; + +if ($sleep) { + say "Sleeping for $sleep s till $start before downloading\n", + "$url to $dest"; + sleep $sleep; +} +say "Downloading $url to $dest"; + +my $data = Y::AoC::UA::request($url, { body => 1 }); +Mojo::File->new($dest)->spurt($data); +say grey('=================================================='); +print $data; +say grey('=================================================='); + +say "lines words chars"; +system 'wc', $dest; + +$url =~ s/\/input\z//; +$dest = $task.'test.txt'; + +my $tst = Y::AoC::UA::request($url, { cache_to => "task-$year-$day.html" }) + ->find('main > article > pre > code')->first->all_text; +Mojo::File->new($dest)->spurt($tst); +say "\n", grey("also downloaded $dest"), "\n"; diff --git a/2022/run b/2022/run new file mode 100755 index 0000000..40a45a6 --- /dev/null +++ b/2022/run @@ -0,0 +1,35 @@ +#!/usr/bin/perl + +use v5.36; +use warnings; +use strict; + +use Time::HiRes qw(sleep); +use Linux::Inotify2; +use POSIX qw(strftime); +use Mojo::File; + +use Y::AoC qw(grey red); + +my $cmd = shift; +my $backup = "backup/$cmd"; +$cmd = "./$cmd" if $cmd !~ /\//; + +my $last_backup; +while (1) { + my $b = "$backup-".strftime("%H-%M-%S", localtime(time)); + system 'cp', $cmd, $b; + say grey("\nrunning $cmd @ARGV... ============================"); + system $cmd, @ARGV; + if ($?) { + say grey("FAILED: $?"); + } else { + say grey("finished OK"); + } + + my $inotify = Linux::Inotify2->new; + $inotify->watch($cmd, IN_MODIFY); + say grey("\nWaiting for modification of $cmd ..."); + $inotify->read; + sleep 0.1; +} diff --git a/bashrc b/bashrc new file mode 100644 index 0000000..bca07d4 --- /dev/null +++ b/bashrc @@ -0,0 +1,3 @@ +export PERL5LIB="/home/kas/aoc/lib:$PERL5LIB" +export PS1="AoC\$ " +ulimit -v 35500000 diff --git a/lib/Y/AoC.pm b/lib/Y/AoC.pm new file mode 100644 index 0000000..8fbb606 --- /dev/null +++ b/lib/Y/AoC.pm @@ -0,0 +1,26 @@ +package Y::AoC; + +use v5.36; + +use Exporter qw(import); +our @EXPORT_OK = qw(red grey white yellow day year); + +use FindBin qw($Bin); +use Term::ANSIColor; + +sub red { colored(['bright_red on_black'], @_); } +sub white { colored(['bright_white on_black'], @_); } +sub grey { colored(['white on_black'], @_); } +sub yellow { colored(['bright_yellow on_black'], @_); } + +sub day { + my ($num) = $0 =~ /.*\D(\d+)/; + int ((1+$num)/2); +} + +sub year { + my ($num) = $Bin =~ /.*(\d{4})/; + $num; +} + +1; diff --git a/lib/Y/AoC/Task.pm b/lib/Y/AoC/Task.pm new file mode 100644 index 0000000..bab6d93 --- /dev/null +++ b/lib/Y/AoC/Task.pm @@ -0,0 +1,102 @@ +package Y::AoC::Task; + +use v5.36; + +use Exporter ('import'); +our @EXPORT = qw(t asay bsay); +use Y::AoC qw(red white grey yellow day year); + +our $printed_err; +$SIG{__DIE__} = sub($msg) { + $msg =~ s/\A(.*?)( at \S+ )(line \d+)/red($1).$2.white($3)/e + if -t STDERR && !$printed_err++; + say STDERR $msg; + die "\n"; +}; + +$SIG{__WARN__} = sub($msg) { + $msg =~ s/\A(.*?)( at \S+ )(line \d+)/red($1).$2.white($3)/e + if -t STDERR && !$printed_err++; + say STDERR $msg; +}; + +our $in_test; +sub t($subtest = ()) { + $subtest //= ''; + $ARGV[0] =~ s/in\.txt/test$subtest.txt/; + $in_test = 1; +} + +sub asay(@msg) { + try_submit(1, @msg); +} + +sub bsay(@msg) { + try_submit(2, @msg); +} + +sub try_submit($part, @msg) { + my $msg = join($, // '', @msg); + my $ans; + $msg =~ s/(\w+)\z/white($ans = $1)/e; + + say $msg; + + return if $in_test; + + my $day = day; + my $year = year; + + my $url = "https://adventofcode.com/$year/day/$day/answer"; + my $cachefile = "ans-$year-$day-$part-$ans.html"; + local $| = 1; + print "\nSubmit $url\nlevel=", white($part), ' answer=', + white($ans), ' ? [Enter]/[Ctrl-C]: '; + + scalar ; + eval '{ + local $SIG{__DIE__}; + require Y::AoC::UA; + }'; + my $res = Y::AoC::UA::request($url, { + form => { + answer => $ans, + level => $part, + }, + cache_to => $cachefile, + }); + + my $art = $res->find('main > article > p')->join("\n"); + if (!$art) { + say $res->to_string; + return; + } + + $art =~ s/'/'/g; + $art =~ s/(?<=That's )not(?= the right answer)/red($&)/e; + my $ok = $art =~ s/(?<=That's the )(right answer)/yellow($&)/e; + $art =~ s/(gold stars?)/yellow($&)/e; + $art =~ s/(silver star)/grey($&)/e; + Y::AoC::UA::cache_del($cachefile) + if $msg =~ s/(?<=Please wait ).*?(?= before trying again)/white($&)/e; + $art =~ s/(?<=your answer is )([^\.;]+)/red($&)/e; + $art =~ s/([^<]+)<\/code>/white($1)/e; + $art =~ s/<[^>]+>//g; + + if ($ok) { + system "cp $0 backup/$0-ok-$ans-$part"; + } + + say $art; +} + +1; +__END__ +package Y::AoC::UA; + +sub new($class, $year, $day) { + eval 'require +} + +sub get($ua) { +1; diff --git a/lib/Y/AoC/UA.pm b/lib/Y/AoC/UA.pm new file mode 100644 index 0000000..ecee1b0 --- /dev/null +++ b/lib/Y/AoC/UA.pm @@ -0,0 +1,79 @@ +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; -- 2.43.0