#!/usr/bin/perl use strict; use warnings; use HTTP::Tiny; use DateTime; use JSON; use LWP::UserAgent; my %data = ( 'Arena Berlin (Biontech)' => '&visit_motive_ids=2495719&agenda_ids=457622-457619-457629-457626-457627-457610-457631-457621-457625-457611-457612-457620-457617-457623-457628-457616-457614-457618-457613-457615&insurance_sector=public&practice_ids=158431&destroy_temporary=true&limit=4', 'Messe Berlin (Biontech)' => '&visit_motive_ids=2495719&agenda_ids=457415-457408-457411-457407-457504-457414-457511-457404-457412-457409-457405-457418&insurance_sector=public&practice_ids=158434&destroy_temporary=true&limit=4', 'Flughafen Berlin-Tegel (Biontech)' => '&visit_motive_ids=2495719&agenda_ids=457363-457254-457331-457358-457349-457256-457263-457343&insurance_sector=public&practice_ids=158436&destroy_temporary=true&limit=4', 'Flughafen Berlin-Tegel (Moderna)' => '&visit_motive_ids=2537716&agenda_ids=465619-465615-465609-465580-465601-466144-465582-466145-466151-465592-465594-465598-466143-466150-466152-466148-466146-466147-465584-466149&insurance_sector=public&practice_ids=158436&destroy_temporary=true&limit=4', 'Messe Berlin (Astra)' => '&visit_motive_ids=2597576&agenda_ids=493350-493326-493328-493324-493331-493329-493334-493348-493352-493320-493322-493335-493338-493340-493333-493353-493343-493345-493339-493347&insurance_sector=public&practice_ids=195952&destroy_temporary=true&limit=4', 'Flughafen Tempelhof (Moderna)' => '&visit_motive_ids=2537716&agenda_ids=499603-499605-499608-499615-499618-499620-499621-499623-499627-499629-499631-499633-499637-499639-499641-499647-499649-499651-499654-499657&insurance_sector=public&practice_ids=158433&destroy_temporary=true&limit=4', 'Velodrom Berlin (Biontech)' => '&visit_motive_ids=2495719&agenda_ids=457299-457310-457304-457291-457319-457306-457315-457321-457217-457296-457288-457991-457312-457195-457205-457193-457211-457283-457201&insurance_sector=public&practice_ids=158435&destroy_temporary=true&limit=4', 'Erika-Heß-Eisstadion' => '&visit_motive_ids=2537716&agenda_ids=457979-457966-457971-457975-457970-457955-457968-457953-457976-457963-457977-457946-457954-457956-457960-457973-457959-457944-457964-457961-457967&insurance_sector=public&practice_ids=158437&destroy_temporary=true&limit=4', ); my $url = 'https://www.doctolib.de/availabilities.json?start_date='; my $dt = DateTime->now; my $date = $dt->ymd; my $epoch = $dt->epoch; foreach my $station (keys %data) { my $json = decode_json do_request($date,$url,$data{$station}); if($json->{'total'} > 20) { if(read_last_hit($station,$json->{'total'},$epoch) == 0) { print $station.": total => ".$json->{'total'}." @ ".$date."\n"; send_notification($station.": total => ".$json->{'total'}." @ ".$date); } write_last_hit($station,$json->{'total'},$epoch); } elsif(defined $json->{'next_slot'}) { #print $station.": found next_slot\n"; #send_notification($station.": found next_slot"); my ($ns_year,$ns_month,$ns_day) = split("-",$json->{'next_slot'}); my $next_slot_dt = DateTime->new( year => $ns_year, month => $ns_month, day => $ns_day ); my $date_diff = $dt->delta_days($next_slot_dt); my $json2 = decode_json do_request($json->{'next_slot'},$url,$data{$station}); if($json2->{'total'} > 20 and $date_diff->in_units('days') > 1 and $date_diff->in_units('days') < 40) { if(read_last_hit($station,$json2->{'total'},$epoch) == 0) { print $station.": total => ".$json2->{'total'}." @ ".$json->{'next_slot'}."\n"; send_notification($station.": total => ".$json2->{'total'}." @ ".$json->{'next_slot'}); } write_last_hit($station,$json2->{'total'},$epoch); } elsif(defined $json2->{'next_slot'}) { print "\t".$station.": LOOP?\n"; send_notification($station.": LOOP?"); } else { #print "\t".$station.": total => ".$json2->{'total'}." @ ".$json->{'next_slot'}."\n"; } } else { #print $station.": total => ".$json->{'total'}." @ ".$date."\n"; } sleep 1; } sub write_last_hit { my ($station,$hits,$epoch) = @_; open(FILE_W,">>","/tmp/search_for_date-last_hit.txt") or die "Can't open file: $!"; print FILE_W $station."::".$hits."::".$epoch."\n"; close(FILE_W); } sub read_last_hit { my ($station,$hits,$epoch) = @_; my $too_early = 0; if(! -e "/tmp/search_for_date-last_hit.txt") { open(NEW_FILE,">","/tmp/search_for_date-last_hit.txt") or die "Can't create file: $!"; close(NEW_FILE); } open(FILE_R,"<","/tmp/search_for_date-last_hit.txt") or die "Can't open file: $!"; my @array = ; close(FILE_R); foreach my $line (@array) { chomp($line); if($line =~/^(.+)\:\:$hits\:\:(\d+)$/) { my $station_old = $1; my $epoch_old = $2; $epoch -= 120; if($station eq $station_old and $epoch < $epoch_old) { #print $epoch." < ".$epoch_old."\n"; $too_early = 1; } elsif($station eq $station_old and $epoch >= $epoch_old) { #print $epoch." > ".$epoch_old."\n"; } else { #print $station." ne ".$station_old." | ".$epoch." != ".$epoch_old."\n"; } } } return $too_early; } sub send_notification { my ($msg) = @_; LWP::UserAgent->new()->post( "https://api.pushover.net/1/messages.json", [ "token" => "", "user" => "", "message" => "Impfstation gefunden: ".$msg, ]); } sub do_request { my ($date,$url1,$url2) = @_; my $url = $url1.$date.$url2; my $httptiny = HTTP::Tiny->new("timeout" => 20); my $response = $httptiny->request("GET",$url); return $response->{'content'}; }