From c8a766881ea25a7cd8de761b0616c642172a6064 Mon Sep 17 00:00:00 2001 From: eleksir Date: Sun, 7 Mar 2021 14:34:16 +0300 Subject: [PATCH] flickr.pm: Implement flickr.com searching api --- bin/flickr.pl | 32 +++ bin/flickr_init.pl | 29 +++ bin/flickr_test.pl | 29 +++ cpanfile | 1 + lib/flickr.pm | 626 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 717 insertions(+) create mode 100644 bin/flickr.pl create mode 100644 bin/flickr_init.pl create mode 100644 bin/flickr_test.pl create mode 100644 lib/flickr.pm diff --git a/bin/flickr.pl b/bin/flickr.pl new file mode 100644 index 0000000..fd32567 --- /dev/null +++ b/bin/flickr.pl @@ -0,0 +1,32 @@ +#!/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_by_tags); + +my $url = flickr_by_tags ('bird,owl'); + +if ($url) { + say $url; ## no critic (InputOutput::RequireCheckedSyscalls) + exit 0; +} else { + exit 1; +} 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/bin/flickr_test.pl b/bin/flickr_test.pl new file mode 100644 index 0000000..908bc20 --- /dev/null +++ b/bin/flickr_test.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_test_login); + +if (flickr_test_login ()) { + exit 0; +} else { + exit 1; +} diff --git a/cpanfile b/cpanfile index cfe4d3c..ea7e192 100644 --- a/cpanfile +++ b/cpanfile @@ -23,3 +23,4 @@ requires 'Mojo::IOLoop'; requires 'Mojo::UserAgent'; requires 'Text::Fuzzy'; requires 'URI::URL'; +requires 'URI::Encode::XS'; \ No newline at end of file diff --git a/lib/flickr.pm b/lib/flickr.pm new file mode 100644 index 0000000..beeb5e8 --- /dev/null +++ b/lib/flickr.pm @@ -0,0 +1,626 @@ +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}; + 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 + ); + + 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) + return 1; + } + } + } + + 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}; + + 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}; + + # 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}; + + # 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;