From cf88be06db2d8115144a4ac2196aa35755a5c960 Mon Sep 17 00:00:00 2001 From: eleksir Date: Sun, 14 Mar 2021 18:28:02 +0300 Subject: [PATCH] =?UTF-8?q?Implemented=20!monkeyuser,=20!drink,=20!=D0=BF?= =?UTF-8?q?=D1=80=D0=B0=D0=B7=D0=B4=D0=BD=D0=B8=D0=BA,=20!owl,=20!=D1=81?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0,=20!bunny,=20!rabbit,=20!=D0=BA=D1=80=D0=BE?= =?UTF-8?q?=D0=BB=D0=B8=D0=BA=20commands?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/flickr_init.pl | 29 ++ data/sample_config.json | 8 + lib/botlib.pm | 16 + lib/buni.pm | 2 +- lib/drink.pm | 116 ++++++++ lib/flickr.pm | 639 ++++++++++++++++++++++++++++++++++++++++ lib/fox.pm | 2 +- lib/image.pm | 41 +++ lib/monkeyuser.pm | 62 ++++ 9 files changed, 913 insertions(+), 2 deletions(-) create mode 100644 bin/flickr_init.pl create mode 100644 lib/drink.pm create mode 100644 lib/flickr.pm create mode 100644 lib/image.pm create mode 100644 lib/monkeyuser.pm diff --git a/bin/flickr_init.pl b/bin/flickr_init.pl new file mode 100644 index 0000000..9b50657 --- /dev/null +++ b/bin/flickr_init.pl @@ -0,0 +1,29 @@ +#!/usr/bin/perl + +use 5.018; +use strict; +use warnings; +use utf8; +use open qw (:std :utf8); +use English qw ( -no_match_vars ); +use version; our $VERSION = qw (1.0); + +my $workdir; + +# before we run, change working dir +BEGIN { + use Cwd qw (chdir abs_path); + my @CWD = split /\//xms, abs_path ($PROGRAM_NAME); + if ($#CWD > 1) { $#CWD = $#CWD - 2; } + $workdir = join '/', @CWD; + chdir $workdir; +} + +use lib ("$workdir/lib", "$workdir/vendor_perl", "$workdir/vendor_perl/lib/perl5"); +use flickr qw (flickr_init); + +if (flickr_init ()) { + exit 0; +} else { + exit 1; +} diff --git a/data/sample_config.json b/data/sample_config.json index bb5155b..5c365ed 100644 --- a/data/sample_config.json +++ b/data/sample_config.json @@ -15,6 +15,14 @@ "csign": "!" }, }, + "image": { + "dir" : "data/image", + "flickr" : { + "consumer_key" : "md5-like_hash", + "consumer_secret" : "some_hash-like_string", + "verifier": "some_hash-like_string" + } + }, "openweathermap" : { "cachetime": "data/cache/openweathermap.Id-Timestamp.db", "cachedata": "data/cache/openweathermap.Id-Data.db", diff --git a/lib/botlib.pm b/lib/botlib.pm index 1764435..20c5d89 100644 --- a/lib/botlib.pm +++ b/lib/botlib.pm @@ -13,13 +13,16 @@ use Math::Random::Secure qw (irand); use archeologist qw (dig); use buni qw (buni); use conf qw (loadConf); +use drink qw (drink); use fisher qw (fish); use fortune qw (fortune); use fox qw (fox); use friday qw (friday); +use image wq (rabbit owl); use karma qw (karmaGet); use kitty qw (kitty); use lat qw (latAnswer); +use monkeyuser qw (monkeyuser); use util qw (trim utf2sha1); use weather qw (weather); use xkcd qw (xkcd); @@ -185,8 +188,11 @@ sub command { ${csign}help | ${csign}помощь - это сообщение ${csign}buni - комикс-стрип hapi buni +${csign}bunny - кролик +${csign}rabbit | ${csign}кролик - кролик ${csign}cat | ${csign}кис - кошечка ${csign}dig | ${csign}копать - заняться археологией +${csign}drink | ${csign}праздник - какой сегодня праздник? ${csign}fish | ${csign}fisher - порыбачить ${csign}рыба | ${csign}рыбка | ${csign}рыбалка - порыбачить ${csign}f | ${csign}ф - рандомная фраза из сборника цитат fortune_mod @@ -197,6 +203,8 @@ ${csign}karma фраза - посмотреть карму фраз ${csign}карма фраза - посмотреть карму фразы фраза++ | фраза-- - повысить или понизить карму фразы ${csign}lat | ${csign}лат - сгенерировать фразу из крылатого латинского выражения +${csign}monkeyuser - комикс-стрип MonkeyUser +${csign}owl | ${csign}сова - сова ${csign}ping | ${csign}пинг - попинговать бота ${csign}some_brew - выдать соответствующий напиток, бармен может налить rum, ром, vodka, водку, beer, пиво, tequila, текила, whisky, виски, absinthe, абсент ${csign}ver | ${csign}version - написать что-то про версию ПО @@ -219,6 +227,14 @@ EOL $reply = buni (); } elsif (substr ($text, 1) eq 'xkcd') { $reply = xkcd (); + } elsif (substr ($text, 1) eq 'monkeyuser') { + $reply = monkeyuser (); + } elsif (substr ($text, 1) eq 'drink' || substr ($text, 1) eq 'праздник') { + $reply = drink (); + } elsif (substr ($text, 1) eq 'bunny' || substr ($text, 1) eq 'rabbit' || substr ($text, 1) eq 'кролик') { + $reply = rabbit (); + } elsif (substr ($text, 1) eq 'owl' || substr ($text, 1) eq 'сова') { + $reply = owl (); } elsif ((length ($text) >= 6 && (substr ($text, 1, 6) eq 'karma ' || substr ($text, 1, 6) eq 'карма ')) || substr ($text, 1) eq 'karma' || substr ($text, 1) eq 'карма') { my $mytext = ''; diff --git a/lib/buni.pm b/lib/buni.pm index 6dddea7..b18f4b3 100644 --- a/lib/buni.pm +++ b/lib/buni.pm @@ -37,7 +37,7 @@ sub buni { { do { $#a = -1; - @a = $p->get_tag("meta"); + @a = $p->get_tag('meta'); ## no critic (Variables::RequireLocalizedPunctuationVars) if (defined $a[0][1]->{property} && $a[0][1]->{property} eq 'og:image') { $ret = $a[0][1]->{content}; diff --git a/lib/drink.pm b/lib/drink.pm new file mode 100644 index 0000000..44452a3 --- /dev/null +++ b/lib/drink.pm @@ -0,0 +1,116 @@ +package drink; + +use 5.018; +use strict; +use warnings; +use utf8; +use open qw (:std :utf8); +use English qw ( -no_match_vars ); +use Encode; +use Carp qw (cluck); +use HTTP::Tiny; +use HTML::TokeParser; + +use version; our $VERSION = qw (1.0); +use Exporter qw (import); +our @EXPORT_OK = qw (drink drink2); + +my @MONTH = qw (yanvar fevral mart aprel may iyun iyul avgust sentyabr oktyabr noyabr dekabr); + +sub drink { + my $r; + my $ret = 'Не знаю праздников - вджобываю весь день на шахтах, как проклятая.'; + my ($dayNum, $monthNum) = (localtime ())[3, 4]; + my $url = sprintf 'https://kakoysegodnyaprazdnik.ru/baza/%s/%s', $MONTH[$monthNum], $dayNum; + + for (1..3) { + my $http = HTTP::Tiny->new (timeout => 3); + $r = $http->get ($url, {'Accept-Charset' => 'utf-8', 'Accept-Language' => 'ru-RU'}); + + if ($r->{success}) { + last; + } + + sleep 2; + } + + if ($r->{success}) { + my $p = HTML::TokeParser->new(\$r->{content}); + my @a; + my @holyday; + + do { + $#a = -1; + @a = $p->get_tag('span'); ## no critic (Variables::RequireLocalizedPunctuationVars) + + if ($#{$a[0]} > 2 && defined $a[0][1]->{itemprop} && $a[0][1]->{itemprop} eq 'text') { + push @holyday,'* ' . decode ('UTF-8', $p->get_trimmed_text ('/span')); + } + + } while ($#{$a[0]} > 1); + + if ($#holyday > 0) { + # cut off something weird, definely not a "holyday" + $#holyday = $#holyday - 1; + } + + if ($#holyday > 0) { + $ret = join "\n", @holyday; + } + } else { + cluck sprintf 'Server return status %s with message: %s', $r->{status}, $r->{reason}; + } + + return $ret; +} + +sub drink2 { + my $r; + my $ret = 'Не знаю праздников - вджобываю весь день на шахтах, как проклятая.'; + my ($dayNum, $monthNum) = (localtime ())[3, 4]; + my $url = sprintf 'https://calendareveryday.ru/?id=prazdniki', $MONTH[$monthNum], $dayNum; + + for (1..3) { + my $http = HTTP::Tiny->new (timeout => 3); + $r = $http->get ($url, {'Accept-Charset' => 'utf-8', 'Accept-Language' => 'ru-RU'}); + + if ($r->{success}) { + last; + } + + sleep 2; + } + + if ($r->{success}) { + my $p = HTML::TokeParser->new(\$r->{content}); + my @a; + my @holyday; + + do { + $#a = -1; + @a = $p->get_tag('span'); ## no critic (Variables::RequireLocalizedPunctuationVars) + + if ($#{$a[0]} > 2 && defined $a[0][1]->{itemprop} && $a[0][1]->{itemprop} eq 'text') { + push @holyday,'* ' . decode ('UTF-8', $p->get_trimmed_text ('/span')); + } + + } while ($#{$a[0]} > 1); + + if ($#holyday > 0) { + # cut off something weird, definely not a "holyday" + $#holyday = $#holyday - 1; + } + + if ($#holyday > 0) { + $ret = join "\n", @holyday; + } + } else { + cluck sprintf 'Server return status %s with message: %s', $r->{status}, $r->{reason}; + } + + return $ret; +} + +1; + +# vim: set ft=perl noet ai ts=4 sw=4 sts=4: diff --git a/lib/flickr.pm b/lib/flickr.pm new file mode 100644 index 0000000..f09e6b1 --- /dev/null +++ b/lib/flickr.pm @@ -0,0 +1,639 @@ +package flickr; + +use 5.018; +use strict; +use warnings; +use utf8; +use open qw (:std :utf8); +use English qw ( -no_match_vars ); +use Carp qw (cluck); +use Digest::HMAC_SHA1 qw (hmac_sha1); +use File::Path qw (make_path); +use JSON::XS; +use HTTP::Tiny; +use Math::Random::Secure qw (irand); +use MIME::Base64; +use SQLite_File; +use URI::Encode::XS qw (uri_encode); + +use conf qw (loadConf); +use util qw (urlencode); + +use Data::Dumper; + +use version; our $VERSION = qw (1.0); +use Exporter qw (import); +our @EXPORT_OK = qw (flickr_init flickr_by_tags flickr_by_text flickr_test_login); + +my $c = loadConf (); +my $dir = $c->{image}->{dir}; + +my $flickr_consumer_key = $c->{image}->{flickr}->{consumer_key}; +my $flickr_consumer_secret = $c->{image}->{flickr}->{consumer_secret}; +my $flickr_verifier = $c->{image}->{flickr}->{verifier}; +my $flickr_request_token_url = 'https://www.flickr.com/services/oauth/request_token'; +my $flickr_authorization_url = 'https://www.flickr.com/services/oauth/authorize'; +my $flickr_access_token_url = 'https://www.flickr.com/services/oauth/access_token'; +my $flickr_callback_url = 'http://www.example.com'; +my $flickr_api_url = 'https://www.flickr.com/services/rest/'; + +sub mknonce { + # make random number, 8 symbols in length + my $nonce = sprintf '%08d', irand (99999999); + return $nonce; +} + +sub flickerSignReq { + my %p = @_; + my $string; + my $hmac; + + if ($p{type} eq 'request_token') { + my @parameters; + delete $p{type}; + push @parameters, 'oauth_signature_method=HMAC-SHA1'; + push @parameters, 'oauth_version=1.0'; + push @parameters, sprintf 'oauth_callback=%s', uri_encode ($flickr_callback_url); + push @parameters, sprintf 'oauth_consumer_key=%s', $flickr_consumer_key; + + foreach my $param (keys %p) { + push @parameters, sprintf ('%s=%s', $param, $p{$param}); + } + + $string = sprintf 'GET&%s&', uri_encode ($flickr_request_token_url); + $string .= uri_encode (join '&', sort (@parameters)); + # Thanks to twitter api dev portal, they explain this particular case: while we requesting this token + # we do not yet know access_token secret, so we just omit it in this signature + # https://developer.twitter.com/en/docs/authentication/oauth-1-0a/creating-a-signature + $hmac = Digest::HMAC_SHA1->new (sprintf '%s&', uri_encode ($flickr_consumer_secret)); + } elsif ($p{type} eq 'access_token') { + my @parameters; + my $flickr_request_token_secret = $p{oauth_token_secret}; + delete $p{oauth_token_secret}; + delete $p{type}; + push @parameters, 'oauth_signature_method=HMAC-SHA1'; + push @parameters, 'oauth_version=1.0'; + push @parameters, sprintf 'oauth_consumer_key=%s', $flickr_consumer_key; + + foreach my $param (keys %p) { + push @parameters, sprintf ('%s=%s', $param, $p{$param}); + } + + $string = sprintf 'GET&%s&', uri_encode ($flickr_access_token_url); + $string .= uri_encode (join '&', sort (@parameters)); + $hmac = Digest::HMAC_SHA1->new (sprintf '%s&%s', uri_encode ($flickr_consumer_secret), uri_encode ($flickr_request_token_secret)); + } elsif ($p{type} eq 'test_login' || $p{type} eq 'search_text' || $p{type} eq 'search_tags') { + my @parameters; + my $flickr_request_token_secret = $p{oauth_token_secret}; + delete $p{oauth_token_secret}; + delete $p{type}; + + foreach my $param (keys %p) { + push @parameters, sprintf ('%s=%s', $param, $p{$param}); + } + + $string = sprintf 'GET&%s&', uri_encode ($flickr_api_url); + $string .= uri_encode (join '&', sort (@parameters)); + $hmac = Digest::HMAC_SHA1->new (sprintf '%s&%s', uri_encode ($flickr_consumer_secret), uri_encode ($flickr_request_token_secret)); + } + + $hmac->add ($string); + return encode_base64 ($hmac->digest, ''); +} + +sub flickrRequestToken { + my $oauth_timestamp = time (); + my $oauth_nonce = mknonce (); + + my $oauth_signature = flickerSignReq ( + type => 'request_token', + oauth_timestamp => $oauth_timestamp, + oauth_nonce => $oauth_nonce + ); + + my $url = $flickr_request_token_url . sprintf ( + '?oauth_nonce=%s&oauth_timestamp=%s&oauth_consumer_key=%s&oauth_signature_method=HMAC-SHA1&oauth_version=1.0&&oauth_signature=%s&oauth_callback=%s', + $oauth_nonce, + $oauth_timestamp, + $flickr_consumer_key, + uri_encode ($oauth_signature), + uri_encode ($flickr_callback_url) + ); + + my $http = HTTP::Tiny->new (timeout => 5); + my $r = $http->get ($url); + my %params; + + if ($r->{success}) { + # they ignore format=json, so... + %params = map {split /=/} split /&/, $r->{content}; + } else { + say "Something bad returns from flickr api: $r->{status} $r->{content}"; ## no critic (InputOutput::RequireCheckedSyscalls) + } + + return %params; +} + +sub flickrAccessToken { + my $flickr_request_token = shift; + my $flickr_request_token_secret = shift; + my $flickr_verifier = shift; ## no critic (Variables::ProhibitReusedNames) + + my $oauth_timestamp = time (); + my $oauth_nonce = mknonce (); + + my $oauth_signature = flickerSignReq ( + type => 'access_token', + oauth_timestamp => $oauth_timestamp, + oauth_nonce => $oauth_nonce, + oauth_token => $flickr_request_token, + oauth_token_secret => $flickr_request_token_secret, + oauth_verifier => $flickr_verifier + ); + + my $url = $flickr_access_token_url . sprintf ( + '?oauth_nonce=%s&oauth_timestamp=%s&oauth_verifier=%s&oauth_consumer_key=%s&oauth_signature_method=HMAC-SHA1&oauth_version=1.0&oauth_token=%s&oauth_signature=%s', + $oauth_nonce, + $oauth_timestamp, + $flickr_verifier, + $flickr_consumer_key, + $flickr_request_token, + uri_encode ($oauth_signature) + ); + + my $http = HTTP::Tiny->new (timeout => 5); + my $r = $http->get ($url); + my %params; + + if ($r->{success}) { + # they ignore format=json + %params = map {split /=/} split /&/, $r->{content}; + } else { + say "Something bad returns from flickr api: $r->{status} $r->{content}"; ## no critic (InputOutput::RequireCheckedSyscalls) + } + + return %params; +} + +sub flickrAuthorization { + my $flickr_request_token = shift; + + my $url = $flickr_authorization_url . sprintf ( + '?oauth_token=%s&perms=read', + $flickr_request_token, + ); + + my $http = HTTP::Tiny->new (timeout => 5); + my $r = $http->get ($url); + + if ($r->{success}) { + return $r->{url}; + } else { + say "Something went wrong: $r->{status} $r->{content}"; ## no critic (InputOutput::RequireCheckedSyscalls) + return; + } +} + +sub flickrTestLogin { + my $flickr_access_token = shift; + my $flickr_access_token_secret = shift; + + my $oauth_timestamp = time (); + my $oauth_nonce = mknonce (); + + my $oauth_signature = flickerSignReq ( + nojsoncallback => 1, + oauth_nonce => $oauth_nonce, + format => 'json', + oauth_consumer_key => $flickr_consumer_key, + oauth_timestamp => $oauth_timestamp, + type => 'test_login', + method => 'flickr.test.login', + oauth_token => $flickr_access_token, + oauth_token_secret => $flickr_access_token_secret, + oauth_signature_method => 'HMAC-SHA1', + oauth_version => '1.0' + ); + + my $url = $flickr_api_url . sprintf ( + '?nojsoncallback=1&oauth_nonce=%s&format=json&oauth_consumer_key=%s&oauth_timestamp=%s&oauth_signature_method=HMAC-SHA1&oauth_version=1.0&oauth_token=%s&oauth_signature=%s&method=flickr.test.login', + $oauth_nonce, + $flickr_consumer_key, + $oauth_timestamp, + $flickr_access_token, + uri_encode ($oauth_signature) + ); + + my $http = HTTP::Tiny->new (timeout => 5); + my $r = $http->get ($url); + + if ($r->{success}) { + my $response = eval { + my $j = JSON::XS->new->utf8->relaxed; + return $j->decode ($r->{content}); + }; + + if (defined $response) { + say Dumper $response; ## no critic (InputOutput::RequireCheckedSyscalls) + return 1; + } else { + say "Flickr api returns incorrect json: $r->{content}"; ## no critic (InputOutput::RequireCheckedSyscalls) + return 0; + } + } else { + say "HTTP status code not 200: $r->{status}, $r>{contenr}"; ## no critic (InputOutput::RequireCheckedSyscalls) + return 0; + } +} + +sub flickrSearchByText { + my $flickr_access_token = shift; + my $flickr_access_token_secret = shift; + my $text = shift; + + my $oauth_timestamp = time (); + my $oauth_nonce = mknonce (); + + my $oauth_signature = flickerSignReq ( + nojsoncallback => 1, + oauth_nonce => $oauth_nonce, + format => 'json', + oauth_consumer_key => $flickr_consumer_key, + oauth_timestamp => $oauth_timestamp, + type => 'search_text', + content_type => 1, + media => 'photos', + method => 'flickr.photos.search', + text => $text, + per_page => 1, + oauth_token => $flickr_access_token, + oauth_token_secret => $flickr_access_token_secret, + oauth_signature_method => 'HMAC-SHA1', + oauth_version => '1.0' + ); + + my $url = $flickr_api_url . sprintf ( + '?nojsoncallback=1&oauth_nonce=%s&format=json&oauth_consumer_key=%s&oauth_timestamp=%s&oauth_signature_method=HMAC-SHA1&oauth_version=1.0&oauth_token=%s&oauth_signature=%s&method=flickr.photos.search&content_type=1&media=photos&per_page=1&text=%s', + $oauth_nonce, + $flickr_consumer_key, + $oauth_timestamp, + $flickr_access_token, + uri_encode ($oauth_signature), + uri_encode ($text) + ); + + my $http = HTTP::Tiny->new (timeout => 5); + my $r = $http->get ($url); + + if ($r->{success}) { + my $response = eval { + my $j = JSON::XS->new->utf8->relaxed; + return $j->decode ($r->{content}); + }; + + if (defined $response) { + # lets make another query, this time with random picture + my $page = irand int ($response->{photos}->{total} / 500); + $oauth_timestamp = time (); + $oauth_nonce = mknonce (); + $oauth_signature = flickerSignReq ( + nojsoncallback => 1, + oauth_nonce => $oauth_nonce, + format => 'json', + oauth_consumer_key => $flickr_consumer_key, + oauth_timestamp => $oauth_timestamp, + type => 'search_text', + content_type => 1, + media => 'photos', + method => 'flickr.photos.search', + text => $text, + page => $page, + per_page => 500, + oauth_token => $flickr_access_token, + oauth_token_secret => $flickr_access_token_secret, + oauth_signature_method => 'HMAC-SHA1', + oauth_version => '1.0' + ); + + $url = $flickr_api_url . sprintf ( + '?nojsoncallback=1&oauth_nonce=%s&format=json&oauth_consumer_key=%s&oauth_timestamp=%s&oauth_signature_method=HMAC-SHA1&oauth_version=1.0&oauth_token=%s&oauth_signature=%s&method=flickr.photos.search&content_type=1&media=photos&per_page=500&page=%d&text=%s', + $oauth_nonce, + $flickr_consumer_key, + $oauth_timestamp, + $flickr_access_token, + uri_encode ($oauth_signature), + $page, + uri_encode ($text) + ); + + $http = HTTP::Tiny->new (timeout => 5); + $r = $http->get ($url); + + if ($r->{success}) { + $response = eval { + my $j = JSON::XS->new->utf8->relaxed; + return $j->decode ($r->{content}); + }; + + if (defined $response) { + return $response; + } else { + cluck "Flickr api returns incorrect json: $r->{content}"; + return undef; + } + } else { + cluck "Flickr api returns incorrect json: $r->{content}"; + return undef; + } + } else { + cluck "Flickr api returns incorrect json: $r->{content}"; + return undef; + } + } else { + cluck "HTTP status code not 200: $r->{status}, $r->{content}"; + return undef; + } +} + +sub flickrSearchByTags { + my $flickr_access_token = shift; + my $flickr_access_token_secret = shift; + my $tags = shift; + + my $oauth_timestamp = time (); + my $oauth_nonce = mknonce (); + + my $oauth_signature = flickerSignReq ( + nojsoncallback => 1, + oauth_nonce => $oauth_nonce, + format => 'json', + oauth_consumer_key => $flickr_consumer_key, + oauth_timestamp => $oauth_timestamp, + type => 'search_tags', + content_type => 1, + media => 'photos', + method => 'flickr.photos.search', + tags => $tags, + per_page => 1, + oauth_token => $flickr_access_token, + oauth_token_secret => $flickr_access_token_secret, + oauth_signature_method => 'HMAC-SHA1', + oauth_version => '1.0' + ); + + my $url = $flickr_api_url . sprintf ( + '?nojsoncallback=1&oauth_nonce=%s&format=json&oauth_consumer_key=%s&oauth_timestamp=%s&oauth_signature_method=HMAC-SHA1&oauth_version=1.0&oauth_token=%s&oauth_signature=%s&method=flickr.photos.search&content_type=1&media=photos&per_page=1&tags=%s&tag_mode=all', + $oauth_nonce, + $flickr_consumer_key, + $oauth_timestamp, + $flickr_access_token, + uri_encode ($oauth_signature), + uri_encode ($tags) + ); + + my $http = HTTP::Tiny->new (timeout => 5); + my $r = $http->get ($url); + + if ($r->{success}) { + my $response = eval { + my $j = JSON::XS->new->utf8->relaxed; + return $j->decode ($r->{content}); + }; + + if (defined $response) { + # lets make another query, this time with random picture + my $page = irand int ($response->{photos}->{total} / 500); + $oauth_timestamp = time (); + $oauth_nonce = mknonce (); + $oauth_signature = flickerSignReq ( + nojsoncallback => 1, + oauth_nonce => $oauth_nonce, + format => 'json', + oauth_consumer_key => $flickr_consumer_key, + oauth_timestamp => $oauth_timestamp, + type => 'search_text', + content_type => 1, + media => 'photos', + method => 'flickr.photos.search', + tags => $tags, + page => $page, + per_page => 500, + oauth_token => $flickr_access_token, + oauth_token_secret => $flickr_access_token_secret, + oauth_signature_method => 'HMAC-SHA1', + oauth_version => '1.0' + ); + + $url = $flickr_api_url . sprintf ( + '?nojsoncallback=1&oauth_nonce=%s&format=json&oauth_consumer_key=%s&oauth_timestamp=%s&oauth_signature_method=HMAC-SHA1&oauth_version=1.0&oauth_token=%s&oauth_signature=%s&method=flickr.photos.search&content_type=1&media=photos&per_page=500&page=%d&tags=%s&tag_mode=all', + $oauth_nonce, + $flickr_consumer_key, + $oauth_timestamp, + $flickr_access_token, + uri_encode ($oauth_signature), + $page, + uri_encode ($tags) + ); + + $http = HTTP::Tiny->new (timeout => 5); + $r = $http->get ($url); + + if ($r->{success}) { + $response = eval { + my $j = JSON::XS->new->utf8->relaxed; + return $j->decode ($r->{content}); + }; + + if (defined $response) { + return $response; + } else { + cluck "Flickr api returns incorrect json: $r->{content}"; + return undef; + } + } else { + cluck "Flickr api returns incorrect json: $r->{content}"; + return undef; + } + } else { + cluck "Flickr api returns incorrect json: $r->{content}"; + return undef; + } + } else { + cluck "HTTP status code not 200: $r->{status}, $r>{contenr}"; + return undef; + } +} + +sub flickr_init { + my $backingfile = sprintf '%s/secrets.sqlite', $dir; + + unless (-d $dir) { + make_path ($dir) or do { + say "Unable to create $dir: $OS_ERROR"; ## no critic (InputOutput::RequireCheckedSyscalls) + return 0; + }; + } + + tie my %secret, 'SQLite_File', $backingfile || do { + say "[ERROR] Unable to tie to $backingfile: $OS_ERROR"; ## no critic (InputOutput::RequireCheckedSyscalls) + return 0; + }; + + if (defined $flickr_verifier && $flickr_verifier) { + my $flickr_request_token = $secret{flickr_request_token}; + my $flickr_request_token_secret = $secret{flickr_request_token_secret}; + + unless (defined $flickr_request_token || defined $flickr_request_token_secret) { + say "Looks like there is no request token or request token secret in $backingfile"; ## no critic (InputOutput::RequireCheckedSyscalls) + say 'Please remove verifier settings from config.json and run this script again'; ## no critic (InputOutput::RequireCheckedSyscalls) + untie %secret; + return 0; + } + + my %accessToken = flickrAccessToken ($flickr_request_token, $flickr_request_token_secret, $flickr_verifier); + + if (defined ($accessToken{oauth_token}) && defined ($accessToken{oauth_token_secret})) { + $secret{flickr_access_token} = $accessToken{oauth_token}; + $secret{flickr_access_token_secret} = $accessToken{oauth_token_secret}; + + say sprintf ( ## no critic (InputOutput::RequireCheckedSyscalls) + 'Access token (%s) and access token secret (%s) are in %s', + $accessToken{oauth_token}, + $accessToken{oauth_token_secret}, + $backingfile + ); + + untie %secret; + return 1; + } + } else { + my %req = flickrRequestToken (); + if (defined ($req{oauth_token}) && defined ($req{oauth_token_secret}) && defined ($req{oauth_token})) { + $secret{flickr_request_token} = $req{oauth_token}; + $secret{flickr_request_token_secret} = $req{oauth_token_secret}; + my $confirm_url = flickrAuthorization ($req{oauth_token}); + + if (defined $confirm_url) { + say "Please open this url in your browser and grant access for this app:\n$confirm_url"; ## no critic (InputOutput::RequireCheckedSyscalls) + say 'Do not forget to put oauth_verifier to config.json file and re-run this script to get access token'; ## no critic (InputOutput::RequireCheckedSyscalls) + say 'Note that you should be logged off from flickr account.'; ## no critic (InputOutput::RequireCheckedSyscalls) + untie %secret; + return 1; + } + } + } + + untie %secret; + return 0; +} + +sub flickr_test_login { + my $text = shift; + my $backingfile = sprintf '%s/secrets.sqlite', $dir; + + unless (-d $dir) { + make_path ($dir) or do { + say "Unable to create $dir: $OS_ERROR"; ## no critic (InputOutput::RequireCheckedSyscalls) + return 0; + }; + } + + tie my %secret, 'SQLite_File', $backingfile || do { + say "[ERROR] Unable to tie to $backingfile: $OS_ERROR"; ## no critic (InputOutput::RequireCheckedSyscalls) + return 0; + }; + + my $flickr_access_token = $secret{flickr_access_token}; + my $flickr_access_token_secret = $secret{flickr_access_token_secret}; + untie %secret; + + if (flickrTestLogin ($flickr_access_token, $flickr_access_token_secret)) { + return 1; + } else { + return 0; + } +} + +sub flickr_by_tags { + my $text = shift; + my $backingfile = sprintf '%s/secrets.sqlite', $dir; + + unless (-d $dir) { + make_path ($dir) or do { + say "Unable to create $dir: $OS_ERROR"; ## no critic (InputOutput::RequireCheckedSyscalls) + return 0; + }; + } + + tie my %secret, 'SQLite_File', $backingfile || do { + say "[ERROR] Unable to tie to $backingfile: $OS_ERROR"; ## no critic (InputOutput::RequireCheckedSyscalls) + return 0; + }; + + my $flickr_access_token = $secret{flickr_access_token}; + my $flickr_access_token_secret = $secret{flickr_access_token_secret}; + untie %secret; + + # NB: I honestly query random page (among number of pages) of results from api, but always got first page, + # no matter what is shown in response' "page" parameter. Looks like bug in API. Try to little bit mitigate + # this bug via query 500 results per page, then we can select random result in this heap. But anyway, let's + # make this thing future proof and leave randomizer in page select. + my $result = flickrSearchByTags ($flickr_access_token, $flickr_access_token_secret, $text); + + if ($result) { + if (defined ($result->{photos}) && defined ($result->{photos}->{photo})) { + my $item = irand int(@{$result->{photos}->{photo}}); + $item = ${$result->{photos}->{photo}}[$item]; + $item = sprintf 'https://live.staticflickr.com/%s/%s_%s_z.jpg', $item->{server}, $item->{id}, $item->{secret}; + return $item; + } else { + cluck 'Flickr api returns empty search result list'; + return; + } + } else { + return; + } +} + +sub flickr_by_text { + my $text = shift; + my $backingfile = sprintf '%s/secrets.sqlite', $dir; + + unless (-d $dir) { + make_path ($dir) or do { + say "Unable to create $dir: $OS_ERROR"; ## no critic (InputOutput::RequireCheckedSyscalls) + return 0; + }; + } + + tie my %secret, 'SQLite_File', $backingfile || do { + say "[ERROR] Unable to tie to $backingfile: $OS_ERROR"; ## no critic (InputOutput::RequireCheckedSyscalls) + return 0; + }; + + my $flickr_access_token = $secret{flickr_access_token}; + my $flickr_access_token_secret = $secret{flickr_access_token_secret}; + untie %secret; + + # NB: I honestly query random page (among number of pages) of results from api, but always got first page, + # no matter what is shown in response' "page" parameter. Looks like bug in API. Try to little bit mitigate + # this bug via query 500 results per page, then we can select random result in this heap. But anyway, let's + # make this thing future proof and leave randomizer in page select. + my $result = flickrSearchByText ($flickr_access_token, $flickr_access_token_secret, $text); + + if ($result) { + if (defined ($result->{photos}) && defined ($result->{photos}->{photo})) { + my $item = irand int(@{$result->{photos}->{photo}}); + $item = ${$result->{photos}->{photo}}[$item]; + $item = sprintf 'https://live.staticflickr.com/%s/%s_%s_z.jpg', $item->{server}, $item->{id}, $item->{secret}; + return $item; + } else { + cluck 'Flickr api returns empty search result list'; + return; + } + } else { + return; + } +} + +1; diff --git a/lib/fox.pm b/lib/fox.pm index 1aff987..a2ab2f8 100644 --- a/lib/fox.pm +++ b/lib/fox.pm @@ -44,7 +44,7 @@ sub fox { } } } else { - cluck sprintf 'Server return status %s with message: %s', $r->{status}, $r->{reason}; + carp sprintf 'Server return status %s with message: %s', $r->{status}, $r->{reason}; } return $ret; diff --git a/lib/image.pm b/lib/image.pm new file mode 100644 index 0000000..6f5edc2 --- /dev/null +++ b/lib/image.pm @@ -0,0 +1,41 @@ +package image; + +use 5.018; +use strict; +use warnings; +use utf8; +use open qw (:std :utf8); +use flickr qw (flickr_by_tags); + +use version; our $VERSION = qw (1.0); +use Exporter qw (import); +our @EXPORT_OK = qw (rabbit owl); + +my $c = loadConf (); +my $dir = $c->{image}->{dir}; + + +sub rabbit { + # rabbit, but bunny + my $url = flickr_by_tags ('animal,bunny'); + + if (defined $url) { + return $url; + } else { + return 'Нету кроликов, все разбежались.'; + } +} + +sub owl { + my $url = flickr_by_tags ('bird,owl'); + + if (defined $url) { + return $url; + } else { + return 'Нету сов, все разлетелись.'; + } +} + +1; + +# vim: set ft=perl noet ai ts=4 sw=4 sts=4: diff --git a/lib/monkeyuser.pm b/lib/monkeyuser.pm new file mode 100644 index 0000000..c2b2536 --- /dev/null +++ b/lib/monkeyuser.pm @@ -0,0 +1,62 @@ +package monkeyuser; + +use 5.018; +use strict; +use warnings; +use utf8; +use open qw (:std :utf8); +use English qw ( -no_match_vars ); +use Carp qw (cluck); +use HTTP::Tiny; +use HTML::TokeParser; +use Math::Random::Secure qw (irand); + +use version; our $VERSION = qw (1.0); +use Exporter qw (import); +our @EXPORT_OK = qw (monkeyuser); + +sub monkeyuser { + my $r; + my $ret = 'Нету Monkey User-ов, они все спрятались.'; + my @link; + + for (1..3) { + my $http = HTTP::Tiny->new (timeout => 3); + $r = $http->get ('https://www.monkeyuser.com/toc/'); + + if ($r->{success}) { + last; + } + + sleep 2; + } + + if ($r->{success}) { + my $p = HTML::TokeParser->new(\$r->{content}); + my @a; + + do { + $#a = -1; + @a = $p->get_tag('a'); ## no critic (Variables::RequireLocalizedPunctuationVars) + + if (defined $a[0][1]->{class} && $a[0][1]->{class} eq 'lazyload small-image') { + if (defined $a[0][1]->{'data-src'} && ($a[0][1]->{'data-src'} !~ /adlitteram/)) { + push @link, $a[0][1]->{'data-src'}; + } + } + + } while ($#{$a[0]} > 1); + + if ($#link > 0) { + $ret = sprintf 'https://www.monkeyuser.com%s', $link [irand (1 + $#link)]; + } + } else { + cluck sprintf 'Server return status %s with message: %s', $r->{status}, $r->{reason}; + } + + return $ret; +} + +1; + +# vim: set ft=perl noet ai ts=4 sw=4 sts=4: