#!/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=397800-397776-402408-397766&insurance_sector=public&practice_ids=158431&destroy_temporary=true&limit=4', 'Messe Berlin/ Halle 21 (Biontech)' => '&visit_motive_ids=2495719&agenda_ids=457591-457443-457477-457487-457405-457414-457511-457594-457432-397846-457408-457421-457435-457489-457563-457567-457569-457439-457493-457453-457406-457416-457418-457426-457400-457404-457409-457419-457420-457427-457448-457483-457425-457428-457415-457504-457597-457566-457412-457457-457436-457463-397845-397844-457411-457497-457424-457429-457430-457442-457470-404659-457596-457407-457410-457593&insurance_sector=public&practice_ids=158434&destroy_temporary=true&limit=4', 'Flughafen Berlin-Tegel (Biontech)' => '&visit_motive_ids=2495719&agenda_ids=457379-457323-457329-457374-457334-457346-457253-457255-457256-457294-457317-457335-457399-457514-457350-457326-457330-457254-457267-457303-457275-457276-457281-457289-457300-457301-457302-457307-457309-457314-457331-457355-457388-457515-457338-457263-457266-457277-457286-457287-457308-457320-457343-457268-457500-397841-457512-457382-457385-457324-457460-457513-457285-457392-457395-457251-397843-457252-457264-457271-457279-457290-457292-457318-457358-457327-457341-457293-457250-457305-457377-457396-457333-457349-457265-457313-457316-457352-457295-457390-457363-457282-457297-397842-457336-457337-457413-404656-457510&insurance_sector=public&practice_ids=158436&destroy_temporary=true&limit=4', 'Flughafen Berlin-Tegel (Moderna)' => '&visit_motive_ids=2537716&agenda_ids=465527-465550-465592-465598-465601-465651-465543-465615-465553-465594-465630-465678-465575-465653-466144-466139-466141-466153-466157-465701-465532-465609-466127-466128-466129-466130-466131-466132-466133-466134-466135-466136-466137-466138-466140-466143-466145-466147-466148-466149-466150-466151-466152-466154-466155-466156-466158-466159-466160-466161-465555-465558-465580-465582-465584-465619-465534-466146-465526&insurance_sector=public&practice_ids=158436&destroy_temporary=true&limit=4', 'Flughafen Tempelhof (Astra)' => '&visit_motive_ids=2597576&agenda_ids=404658-397960-397955-397956&insurance_sector=public&practice_ids=158433&destroy_temporary=true&limit=4', 'Flughafen Tempelhof (Moderna)' => '&visit_motive_ids=2537716&agenda_ids=467896-467894-467900-467908-467934-467937-467912-467901-467933-467893-467938-467939-467940-467903-467905-467906-467907-467910-467911-467935-467936-467897-467898-467899-467895&insurance_sector=public&practice_ids=158433&destroy_temporary=true&limit=4', 'Velodrom Berlin (Biontech)' => '&visit_motive_ids=2495719&agenda_ids=404654-457215-457244-397972-457210-457239-457213-457278-457283-457304-457306-457229-457234-457299-457212-457216-457288-457291-457315-457227-457204-457237-457296-397974-457312-457280-457206-457310-457319-397973-457243-457208-457218-457245-457274-457321&insurance_sector=public&practice_ids=158435&destroy_temporary=true&limit=4', 'Erika-Heß-Eisstadion' => '&visit_motive_ids=2537716&agenda_ids=397976-397975-457975-457951-457902-457907-457917-457924-457933-457947-457946-457971-457961-457964-457945-457955-457940-457953-457968-457920-457960-457963-457906-404655-457973-457977-457931-457956-457952-457903-457912-457916-457928-457976-457943-457954-457901-457915-457913-457918-457922-457938-457939-457927-457935-457936-457979-457966-457970-457930-457967-457944-457910-397977-457959-457926-457941-457923-457937&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; 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'}; }