1

Use dedicated geo api instead of builtin for openweathermap api requests

This commit is contained in:
eleksir
2023-05-04 15:32:45 +03:00
parent 7787be73b3
commit b29b2f3345

View File

@@ -4,13 +4,13 @@ use 5.018; ## no critic (ProhibitImplicitImport)
use strict; use strict;
use warnings; use warnings;
use utf8; use utf8;
use open qw (:std :utf8); use open qw (:std :utf8);
use English qw ( -no_match_vars ); use English qw ( -no_match_vars );
use CHI (); use CHI ();
use CHI::Driver::BerkeleyDB (); use CHI::Driver::BerkeleyDB ();
use HTTP::Tiny (); use HTTP::Tiny ();
use JSON::XS qw (decode_json); use JSON::XS qw (decode_json);
use Log::Any qw ($log); use Log::Any qw ($log);
use BotLib::Conf qw (LoadConf); use BotLib::Conf qw (LoadConf);
use BotLib::Util qw (trim urlencode); use BotLib::Util qw (trim urlencode);
@@ -29,6 +29,74 @@ sub Weather {
$city = ucfirst $city; $city = ucfirst $city;
my $cache = CHI->new (
driver => 'BerkeleyDB',
root_dir => $c->{cachedir},
namespace => __PACKAGE__,
);
my $geoCallBack = sub {
my $appid = $c->{openweathermap}->{appid};
my $geo;
my $r;
# Try 3 times and giveup
for (1..3) {
my $http = HTTP::Tiny->new (timeout => 3);
$r = $http->get (
sprintf (
'http://api.openweathermap.org/geo/1.0/direct?q=%s&limit=1&APPID=%s',
urlencode ($city),
$appid,
),
);
if ($r->{success}) {
last;
}
sleep 2;
}
# All 3 times it can give us error, so check this case here
if ($r->{success}) {
$geo = eval {
decode_json ($r->{content});
};
unless ($geo) {
$log->warn ("[WARN] openweathermap geo api returns corrupted json: $EVAL_ERROR");
return undef;
}
} else {
$log->warn (
sprintf (
'[WARN] Server api.openweathermap.org return status %s with message: %s in request to geo',
$r->{status},
$r->{reason},
),
);
return undef;
}
if (defined ($geo->[0]->{lat}) && defined ($geo->[0]->{lon})) {
# Success
return $geo->[0];
} else {
# Error, no coordinates
$log->warn ('[WARN] Server api.openweathermap.org does not return coordinates in response to geo request');
return undef;
}
};
my $geo = $cache->compute (
$city,
'2 months',
$geoCallBack,
);
my $weatherCallBack = sub { my $weatherCallBack = sub {
my $appid = $c->{openweathermap}->{appid}; my $appid = $c->{openweathermap}->{appid};
@@ -41,96 +109,102 @@ sub Weather {
my $w; my $w;
my $r; my $r;
# try 3 times and giveup if (defined $geo) {
for (1..3) { # try 3 times and giveup
my $http = HTTP::Tiny->new (timeout => 3); for (1..3) {
$r = $http->get (sprintf ('http://api.openweathermap.org/data/2.5/weather?q=%s&lang=ru&APPID=%s', urlencode ($city), $appid)); my $http = HTTP::Tiny->new (timeout => 3);
$r = $http->get (
sprintf (
'http://api.openweathermap.org/data/2.5/weather?lat=%s&lon=%s&lang=ru&APPID=%s',
$geo->{lat},
$geo->{lon},
$appid,
),
);
if ($r->{success}) { if ($r->{success}) {
last; last;
}
sleep 2;
} }
sleep 2; # all 3 times can give error, so check it here
} if ($r->{success}) {
$fc = eval {
decode_json ($r->{content});
};
# all 3 times can give error, so check it here unless (defined $fc) {
if ($r->{success}) { $log->warn ("[WARN] openweathermap returns corrupted json: $EVAL_ERROR");
$fc = eval { return undef;
decode_json ($r->{content}); }
}; } else {
$log->warn (sprintf 'Server return status %s with message: %s', $r->{status}, $r->{reason});
unless (defined $fc) {
$log->warn ("[WARN] openweathermap returns corrupted json: $EVAL_ERROR");
return undef; return undef;
} }
# TODO: check all of this for existence
$w->{'name'} = $fc->{name};
$w->{'state'} = $fc->{state};
$w->{'country'} = $fc->{sys}->{country};
$w->{'longitude'} = $fc->{coord}->{lon};
$w->{'latitude'} = $fc->{coord}->{lat};
$w->{'temperature_min'} = int ($fc->{main}->{temp_min} - 273.15);
$w->{'temperature_max'} = int ($fc->{main}->{temp_max} - 273.15);
$w->{'temperature_feelslike'} = int ($fc->{main}->{feels_like} - 273.15);
$w->{'humidity'} = $fc->{main}->{humidity};
$w->{'pressure'} = int ($fc->{main}->{pressure} * 0.75006375541921);
$w->{'description'} = $fc->{weather}->[0]->{description};
$w->{'wind_speed'} = $fc->{wind}->{speed};
$w->{'wind_direction'} = 'разный';
my $dir = int ($fc->{wind}->{deg} + 0);
if ($dir == 0) {
$w->{'wind_direction'} = 'северный';
} elsif ($dir > 0 && $dir <= 30) {
$w->{'wind_direction'} = 'северо-северо-восточный';
} elsif ($dir > 30 && $dir <= 60) {
$w->{'wind_direction'} = 'северо-восточный';
} elsif ($dir > 60 && $dir < 90) {
$w->{'wind_direction'} = 'восточно-северо-восточный';
} elsif ($dir == 90) {
$w->{'wind_direction'} = 'восточный';
} elsif ($dir > 90 && $dir <= 120) {
$w->{'wind_direction'} = 'восточно-юго-восточный';
} elsif ($dir > 120 && $dir <= 150) {
$w->{'wind_direction'} = 'юговосточный';
} elsif ($dir > 150 && $dir < 180) {
$w->{'wind_direction'} = 'юго-юго-восточный';
} elsif ($dir == 180) {
$w->{'wind_direction'} = 'южный';
} elsif ($dir > 180 && $dir <= 210) {
$w->{'wind_direction'} = 'юго-юго-западный';
} elsif ($dir > 210 && $dir <= 240) {
$w->{'wind_direction'} = 'юго-западный';
} elsif ($dir > 240 && $dir < 270) {
$w->{'wind_direction'} = 'западно-юго-западный';
} elsif ($dir == 270) {
$w->{'wind_direction'} = 'западный';
} elsif ($dir > 270 && $dir <= 300) {
$w->{'wind_direction'} = 'западно-северо-западный';
} elsif ($dir > 300 && $dir <= 330) {
$w->{'wind_direction'} = 'северо-западный';
} elsif ($dir > 330 && $dir < 360) {
$w->{'wind_direction'} = 'северо-северо-западный';
} elsif ($dir == 360) {
$w->{'wind_direction'} = 'северный';
}
return $w;
} else { } else {
$log->warn (sprintf 'Server return status %s with message: %s', $r->{status}, $r->{reason}); # No geo, no weather :'(
return undef; return undef;
} }
# TODO: check all of this for existence
$w->{'name'} = $fc->{name};
$w->{'state'} = $fc->{state};
$w->{'country'} = $fc->{sys}->{country};
$w->{'longitude'} = $fc->{coord}->{lon};
$w->{'latitude'} = $fc->{coord}->{lat};
$w->{'temperature_min'} = int ($fc->{main}->{temp_min} - 273.15);
$w->{'temperature_max'} = int ($fc->{main}->{temp_max} - 273.15);
$w->{'temperature_feelslike'} = int ($fc->{main}->{feels_like} - 273.15);
$w->{'humidity'} = $fc->{main}->{humidity};
$w->{'pressure'} = int ($fc->{main}->{pressure} * 0.75006375541921);
$w->{'description'} = $fc->{weather}->[0]->{description};
$w->{'wind_speed'} = $fc->{wind}->{speed};
$w->{'wind_direction'} = 'разный';
my $dir = int ($fc->{wind}->{deg} + 0);
if ($dir == 0) {
$w->{'wind_direction'} = 'северный';
} elsif ($dir > 0 && $dir <= 30) {
$w->{'wind_direction'} = 'северо-северо-восточный';
} elsif ($dir > 30 && $dir <= 60) {
$w->{'wind_direction'} = 'северо-восточный';
} elsif ($dir > 60 && $dir < 90) {
$w->{'wind_direction'} = 'восточно-северо-восточный';
} elsif ($dir == 90) {
$w->{'wind_direction'} = 'восточный';
} elsif ($dir > 90 && $dir <= 120) {
$w->{'wind_direction'} = 'восточно-юго-восточный';
} elsif ($dir > 120 && $dir <= 150) {
$w->{'wind_direction'} = 'юговосточный';
} elsif ($dir > 150 && $dir < 180) {
$w->{'wind_direction'} = 'юго-юго-восточный';
} elsif ($dir == 180) {
$w->{'wind_direction'} = 'южный';
} elsif ($dir > 180 && $dir <= 210) {
$w->{'wind_direction'} = 'юго-юго-западный';
} elsif ($dir > 210 && $dir <= 240) {
$w->{'wind_direction'} = 'юго-западный';
} elsif ($dir > 240 && $dir < 270) {
$w->{'wind_direction'} = 'западно-юго-западный';
} elsif ($dir == 270) {
$w->{'wind_direction'} = 'западный';
} elsif ($dir > 270 && $dir <= 300) {
$w->{'wind_direction'} = 'западно-северо-западный';
} elsif ($dir > 300 && $dir <= 330) {
$w->{'wind_direction'} = 'северо-западный';
} elsif ($dir > 330 && $dir < 360) {
$w->{'wind_direction'} = 'северо-северо-западный';
} elsif ($dir == 360) {
$w->{'wind_direction'} = 'северный';
}
return $w;
}; };
my $cache = CHI->new (
driver => 'BerkeleyDB',
root_dir => $c->{cachedir},
namespace => __PACKAGE__,
);
my $w = $cache->compute ( my $w = $cache->compute (
$city, "$geo->{lat}+$geo->{lon}",
'3 hours', '3 hours',
$weatherCallBack, $weatherCallBack,
); );
@@ -139,8 +213,9 @@ sub Weather {
if ($w) { if ($w) {
if ($w->{temperature_min} == $w->{temperature_max}) { if ($w->{temperature_min} == $w->{temperature_max}) {
$reply = sprintf ("Погода в городе %s, %s:\n%s, ветер %s %s м/c, температура %s°C, ощущается как %s°C, относительная влажность %s%%, давление %s мм.рт.с", $reply = sprintf (
$w->{name}, "Погода в городе %s, %s:\n%s, ветер %s %s м/c, температура %s°C, ощущается как %s°C, относительная влажность %s%%, давление %s мм.рт.с",
$city,
$w->{country}, $w->{country},
ucfirst ($w->{description}), ucfirst ($w->{description}),
$w->{wind_direction}, $w->{wind_direction},
@@ -151,8 +226,9 @@ sub Weather {
$w->{pressure}, $w->{pressure},
); );
} else { } else {
$reply = sprintf ("Погода в городе %s, %s:\n%s, ветер %s %s м/c, температура %s-%s°C, ощущается как %s°C, относительная влажность %s%%, давление %s мм.рт.ст", $reply = sprintf (
$w->{name}, "Погода в городе %s, %s:\n%s, ветер %s %s м/c, температура %s-%s°C, ощущается как %s°C, относительная влажность %s%%, давление %s мм.рт.ст",
$city,
$w->{country}, $w->{country},
ucfirst ($w->{description}), ucfirst ($w->{description}),
$w->{wind_direction}, $w->{wind_direction},