package botlib; use 5.018; use strict; use warnings "all"; use utf8; use open qw(:std :utf8); use HTTP::Tiny; use URI::URL; use JSON::XS qw(decode_json encode_json); use Digest::MD5 qw(md5_base64); use DB_File; use conf qw(loadConf); use vars qw/$VERSION/; use Exporter qw(import); our @EXPORT = qw(weather logger trim randomCommonPhrase); $VERSION = "1.0"; my $c = loadConf(); sub weather($) { my $city = shift; $city = trim($city); return "Мне нужно ИМЯ города." if ($city eq ''); return "Длинновато для названия города." if (length($city) > 80); $city = ucfirst($city); my $w = __weather($city); my $reply; if ($w) { if ($w->{temperature_min} == $w->{temperature_max}) { $reply = sprintf ("Погода в городе %s, %s:\n%s, ветер %s %s м/c, температура %s°C, ощущается как %s°C, относительная влажность %s%%, давление %s мм.рт.ст", $w->{name}, $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:\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"; } return $reply; } sub __weather($) { my $city = shift; $city = __urlencode($city); my $id = md5_base64($city); my $appid = $c->{openweathermap}->{appid}; my $now = time(); my $fc; my $w; # attach to cache data tie my %cachetime, 'DB_File', $c->{openweathermap}->{cachetime} or do { warn "Something nasty happen when cachetime ties to its data: $!"; return undef; }; tie my %cachedata, 'DB_File', $c->{openweathermap}->{cachedata} or do { warn "Something nasty happen when cachedata ties to its data: $!"; return undef; }; # lookup cache if (defined ($cachetime{$id}) && ($now - $cachetime{$id}) < 10800) { $fc = decode_json($cachedata{$id}); } else { my $r; # try 3 times and giveup for (my $counter = 0; $counter < 3; $counter++) { next if ($r->{success}); my $http = HTTP::Tiny->new(timeout => 3); $r = $http->get(sprintf("http://api.openweathermap.org/data/2.5/weather?q=%s&lang=ru&APPID=%s", $city, $appid)); sleep 2; } # all 3 times can give error, so check it here if ($r->{success}) { $fc = eval { decode_json($r->{content}) }; if ($@) { warn "[WARN] openweathermap returns corrupted json: $@"; untie %cachetime; untie %cachedata; return undef; }; $cachetime{$id} = $now; $cachedata{$id} = encode_json($fc); } else { untie %cachetime; untie %cachedata; return undef; } } untie %cachetime; untie %cachedata; # TODO: check all of this for existance $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; } sub __urlencode($) { my $str = shift; my $urlobj = url $str; $str = $urlobj->as_string; $urlobj = undef; undef $urlobj; return $str; } sub logger { my $msg = shift; if ($c->{debug_log}) { my @date = localtime(); my @month = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); my $t = sprintf ("%02s %02d %02d:%02d:%02d ", $month[$date[4]], $date[3], $date[2], $date[1], $date[0]); my $mode = '>'; $mode = '>>' if (-f $c->{debug_log}); if (open (LOG, $mode, $c->{debug_log})) { print LOG $t . $msg . "\n"; close LOG; } } } sub trim($) { my $str = shift; while (substr ($str, 0, 1) =~ /^\s$/) { $str = substr($str, 1); } while (substr ($str, -1, 1) =~ /^\s$/) { chop ($str); } return $str; } sub randomCommonPhrase() { my @myphrase = ( "Так, блядь...", "*Закатывает рукава* И ради этого ты меня позвал?", "Ну чего ты начинаешь, нормально же общались", "Повтори свой вопрос, не поняла", "Выйди и зайди нормально", "Я подумаю", "Даже не знаю что на это ответить", "Ты упал такие вопросы девочке задавать?", "Можно и так, но не уверена", "А как ты думаешь?" ); return ($myphrase[rand($#myphrase -1)]); } 1; # vim: set ft=perl noet ai ts=4 sw=4 sts=4: