4 Commits

Author SHA1 Message Date
eleksir
657090b5f7 Fix inconsistence of city name pick decision introduced in fa59102 2023-05-07 18:57:15 +03:00
eleksir
a0fd6faafc Introduce option country in config to use either country code or coordinates as secondary id for city.
Looks like openweathermap.org gives incorrect or mangled coutry codes for some cities for whatever reasons.
So we have to introduce this option in order to cool-down irritation of some engaging users.
2023-05-07 16:22:33 +03:00
eleksir
fa5910218e Use dedicated geo api instead of builtin for openweathermap api 2023-05-04 13:29:38 +03:00
eleksir
079ea8098e Mute perlcritic complains about long regexp 2023-05-04 13:25:47 +03:00
3 changed files with 216 additions and 65 deletions

View File

@@ -132,3 +132,5 @@ lib_sections = NAME | VERSION | DESCRIPTION | AUTHOR | LICENSE AND COPYRIGHT
[-TooMuchCode::ProhibitLargeBlock] [-TooMuchCode::ProhibitLargeBlock]
# read my lips: fuck off # read my lips: fuck off
[-RegularExpressions::ProhibitComplexRegexes]

View File

@@ -26,6 +26,8 @@
"forwards_max" : 5, "forwards_max" : 5,
"openweathermap" : { "openweathermap" : {
# (For shitty political reasons) 0 - use coordinates instead of coutry code, 1 - use country codes
"country" : 0,
"appid" : "get_it_on_openweathermap.org" "appid" : "get_it_on_openweathermap.org"
}, },

View File

@@ -130,7 +130,12 @@ sub Anek {
$got_anek = 1; $got_anek = 1;
} }
} else { } else {
$log->warn (sprintf '[WARN] anekdot.ru server returns incorrect json, full response text message: %s', $response_text); $log->warn (
sprintf (
'[WARN] anekdot.ru server returns incorrect json, full response text message: %s',
$response_text,
),
);
} }
} else { } else {
$log->warn (sprintf '[WARN] anekdot.ru server returns unexpected response text: %s', $r->body); $log->warn (sprintf '[WARN] anekdot.ru server returns unexpected response text: %s', $r->body);
@@ -191,7 +196,6 @@ sub Buni {
sub Drink { sub Drink {
my $r; my $r;
my $ret = 'Не знаю праздников - вджобываю весь день на шахтах, как проклятая.'; my $ret = 'Не знаю праздников - вджобываю весь день на шахтах, как проклятая.';
my ($dayNum, $monthNum) = (localtime ())[3, 4];
my $url = 'https://prazdniki-segodnya.ru/'; my $url = 'https://prazdniki-segodnya.ru/';
# Those POSIX assholes just forgot to add unix timestamps without TZ offset, so... # Those POSIX assholes just forgot to add unix timestamps without TZ offset, so...
@@ -265,7 +269,7 @@ sub Drink {
if (ref $item) { if (ref $item) {
foreach my $item1 (@{$item}) { foreach my $item1 (@{$item}) {
my %h = eval { %{$item1} }; my %h = eval { %{$item1} };
if (defined ($h{class}) && $h{class} eq 'list-group-item text-monospace') { if (defined ($h{class}) && $h{class} eq 'list-group-item text-monospace') {
$holiday {'* ' . decode ('UTF-8', $p->get_trimmed_text ('/div'))} = 1; $holiday {'* ' . decode ('UTF-8', $p->get_trimmed_text ('/div'))} = 1;
} }
@@ -279,7 +283,13 @@ sub Drink {
$ret = join "\n", sort (keys (%holiday)); $ret = join "\n", sort (keys (%holiday));
} }
} else { } else {
$log->warn (sprintf '[WARN] prazdniki-segodnya.ru server return status %s with message: %s', $r->code, $r->message); $log->warn (
sprintf (
'[WARN] prazdniki-segodnya.ru server return status %s with message: %s',
$r->code,
$r->message,
),
);
} }
return $ret; return $ret;
@@ -563,69 +573,128 @@ sub Weather {
$city = 'Екатеринбург'; $city = 'Екатеринбург';
} }
my $w = __weather ($city);
my $reply; my $reply;
if ($w) { # Get coordinates
if ($w->{temperature_min} == $w->{temperature_max}) { my $g = __weather_geo ($city);
$reply = sprintf (
"Погода в городе %s, %s:\n%s, ветер %s %s м/c, температура %s°C, ощущается как %s°C, относительная влажность %s%%, давление %s мм.рт.ст", unless (defined $g) {
$w->{name},
$w->{country},
ucfirst $w->{description},
$w->{wind_direction},
$w->{wind_speed},
$w->{temperature_min},
$w->{temperature_feelslike},
$w->{humidity},
$w->{pressure},
);
} elsif ($w->{temperature_min} < 0 && $w->{temperature_max} <= 0) {
$reply = sprintf (
"Погода в городе %s, %s:\n%s, ветер %s %s м/c, температура от %s до %s°C, ощущается как %s°C, относительная влажность %s%%, давление %s мм.рт.ст",
$w->{name},
$w->{country},
ucfirst $w->{description},
$w->{wind_direction},
$w->{wind_speed},
$w->{temperature_max},
$w->{temperature_min},
$w->{temperature_feelslike},
$w->{humidity},
$w->{pressure},
);
} else {
$reply = sprintf (
"Погода в городе %s, %s:\n%s, ветер %s %s м/c, температура от %s до %s°C, ощущается как %s°C, относительная влажность %s%%, давление %s мм.рт.ст",
$w->{name},
$w->{country},
ucfirst $w->{description},
$w->{wind_direction},
$w->{wind_speed},
$w->{temperature_min},
$w->{temperature_max},
$w->{temperature_feelslike},
$w->{humidity},
$w->{pressure},
);
}
} else {
$reply = "Я не знаю, какая погода в $city"; $reply = "Я не знаю, какая погода в $city";
} else {
my $w = __weather ($g->{lat}, $g->{lon});
if ($w) {
if ($w->{temperature_min} == $w->{temperature_max}) {
if ($c->{openweathermap}->{country}) {
$reply = sprintf (
"Погода в городе %s, %s:\n%s, ветер %s %s м/c, температура %s°C, ощущается как %s°C, относительная влажность %s%%, давление %s мм.рт.ст",
$city,
$w->{country},
ucfirst $w->{description},
$w->{wind_direction},
$w->{wind_speed},
$w->{temperature_min},
$w->{temperature_feelslike},
$w->{humidity},
$w->{pressure},
);
} else {
$reply = sprintf (
"Погода в городе %s, (ш:%s, д:%s):\n%s, ветер %s %s м/c, температура %s°C, ощущается как %s°C, относительная влажность %s%%, давление %s мм.рт.ст",
$city,
$g->{lat},
$g->{lon},
ucfirst $w->{description},
$w->{wind_direction},
$w->{wind_speed},
$w->{temperature_min},
$w->{temperature_feelslike},
$w->{humidity},
$w->{pressure},
);
}
} elsif ($w->{temperature_min} < 0 && $w->{temperature_max} <= 0) {
if ($c->{openweathermap}->{country}) {
$reply = sprintf (
"Погода в городе %s, %s:\n%s, ветер %s %s м/c, температура от %s до %s°C, ощущается как %s°C, относительная влажность %s%%, давление %s мм.рт.ст",
$city,
$w->{country},
ucfirst $w->{description},
$w->{wind_direction},
$w->{wind_speed},
$w->{temperature_max},
$w->{temperature_min},
$w->{temperature_feelslike},
$w->{humidity},
$w->{pressure},
);
} else {
$reply = sprintf (
"Погода в городе %s, (ш:%s, д:%s):\n%s, ветер %s %s м/c, температура от %s до %s°C, ощущается как %s°C, относительная влажность %s%%, давление %s мм.рт.ст",
$city,
$g->{lat},
$g->{lon},
ucfirst $w->{description},
$w->{wind_direction},
$w->{wind_speed},
$w->{temperature_max},
$w->{temperature_min},
$w->{temperature_feelslike},
$w->{humidity},
$w->{pressure},
);
}
} else {
if ($c->{openweathermap}->{country}) {
$reply = sprintf (
"Погода в городе %s, %s:\n%s, ветер %s %s м/c, температура от %s до %s°C, ощущается как %s°C, относительная влажность %s%%, давление %s мм.рт.ст",
$city,
$w->{country},
ucfirst $w->{description},
$w->{wind_direction},
$w->{wind_speed},
$w->{temperature_min},
$w->{temperature_max},
$w->{temperature_feelslike},
$w->{humidity},
$w->{pressure},
);
} else {
$reply = sprintf (
"Погода в городе %s, (ш:%s, д:%s):\n%s, ветер %s %s м/c, температура от %s до %s°C, ощущается как %s°C, относительная влажность %s%%, давление %s мм.рт.ст",
$city,
$g->{lat},
$g->{lon},
ucfirst $w->{description},
$w->{wind_direction},
$w->{wind_speed},
$w->{temperature_min},
$w->{temperature_max},
$w->{temperature_feelslike},
$w->{humidity},
$w->{pressure},
);
}
}
} else {
$reply = "Я не знаю, какая погода в $city";
}
} }
return $reply; return $reply;
} }
sub __weather { sub __weather {
my $city = shift; my $lat = shift;
$city = urlencode $city; my $lon = shift;
my $appid = $c->{openweathermap}->{appid}; my $appid = $c->{openweathermap}->{appid};
my $fc; my $fc;
my $w; my $w;
my $r; my $r;
# try 3 times and giveup # Try 3 times and giveup
for (1..3) { for (1..3) {
my $ua = Mojo::UserAgent::Cached->new; my $ua = Mojo::UserAgent::Cached->new;
$ua->local_dir ($cachedir); $ua->local_dir ($cachedir);
@@ -640,7 +709,14 @@ sub __weather {
); );
# just to make Mojo::UserAgent::Cached happy # just to make Mojo::UserAgent::Cached happy
$ua->logger (Mojo::Log->new (path => File::Spec->devnull, level => 'error')); $ua->logger (Mojo::Log->new (path => File::Spec->devnull, level => 'error'));
$r = $ua->get (sprintf ('http://api.openweathermap.org/data/2.5/weather?q=%s&lang=ru&APPID=%s', $city, $appid))->result; $r = $ua->get (
sprintf (
'http://api.openweathermap.org/data/2.5/weather?lat=%s&lon=%s&lang=ru&APPID=%s',
$lat,
$lon,
$appid,
),
)->result;
if ($r->is_success) { if ($r->is_success) {
last; last;
@@ -665,19 +741,19 @@ sub __weather {
} }
# TODO: check all of this for existence # TODO: check all of this for existence
$w->{'name'} = $fc->{name}; $w->{'name'} = $fc->{name};
$w->{'state'} = $fc->{state}; $w->{'state'} = $fc->{state};
$w->{'country'} = $fc->{sys}->{country}; $w->{'country'} = $fc->{sys}->{country};
$w->{'longitude'} = $fc->{coord}->{lon}; $w->{'longitude'} = $fc->{coord}->{lon};
$w->{'latitude'} = $fc->{coord}->{lat}; $w->{'latitude'} = $fc->{coord}->{lat};
$w->{'temperature_min'} = int ($fc->{main}->{temp_min} - 273.15); $w->{'temperature_min'} = int ($fc->{main}->{temp_min} - 273.15);
$w->{'temperature_max'} = int ($fc->{main}->{temp_max} - 273.15); $w->{'temperature_max'} = int ($fc->{main}->{temp_max} - 273.15);
$w->{'temperature_feelslike'} = int ($fc->{main}->{feels_like} - 273.15); $w->{'temperature_feelslike'} = int ($fc->{main}->{feels_like} - 273.15);
$w->{'humidity'} = $fc->{main}->{humidity}; $w->{'humidity'} = $fc->{main}->{humidity};
$w->{'pressure'} = int ($fc->{main}->{pressure} * 0.75006375541921); $w->{'pressure'} = int ($fc->{main}->{pressure} * 0.75006375541921);
$w->{'description'} = $fc->{weather}->[0]->{description}; $w->{'description'} = $fc->{weather}->[0]->{description};
$w->{'wind_speed'} = $fc->{wind}->{speed}; $w->{'wind_speed'} = $fc->{wind}->{speed};
$w->{'wind_direction'} = 'разный'; $w->{'wind_direction'} = 'разный';
my $dir = int ($fc->{wind}->{deg} + 0); my $dir = int ($fc->{wind}->{deg} + 0);
if ($dir == 0) { if ($dir == 0) {
@@ -719,6 +795,77 @@ sub __weather {
return $w; return $w;
} }
sub __weather_geo {
my $city = shift;
$city = urlencode $city;
my $appid = $c->{openweathermap}->{appid};
my $geo;
my $r;
# Try 3 times and giveup
for (1..3) {
my $ua = Mojo::UserAgent::Cached->new;
$ua->local_dir ($cachedir);
$ua->cache_agent (
CHI->new (
driver => 'BerkeleyDB',
root_dir => $cachedir,
namespace => __PACKAGE__,
expires_in => '2 months',
expires_on_backend => 1,
),
);
# Just to make Mojo::UserAgent::Cached happy
$ua->logger (Mojo::Log->new (path => File::Spec->devnull, level => 'error'));
$r = $ua->get (
sprintf (
'http://api.openweathermap.org/geo/1.0/direct?q=%s&limit=1&APPID=%s',
$city,
$appid,
),
)->result;
if ($r->is_success) {
last;
}
sleep 2;
}
# All 3 times it can give us error, so check this case here
if ($r->is_success) {
$geo = eval {
return $r->json;
};
unless ($geo) {
$log->warn ("[WARN] openweathermap geo api returns corrupted json: $EVAL_ERROR");
return;
}
} else {
$log->warn (
sprintf (
'[WARN] Server api.openweathermap.org return status %s with message: %s in request to geo',
$r->code,
$r->message,
),
);
return;
}
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;
}
}
1; 1;
# vim: set ft=perl noet ai ts=4 sw=4 sts=4: # vim: set ft=perl noet ai ts=4 sw=4 sts=4: