Merge branch 'master' into update_teapot_bot_lib_to_api_v5.1
This commit is contained in:
103
README_RU.md
Normal file
103
README_RU.md
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
# Aleesa-Telegram-bot - Болтливый бот для Telegram
|
||||||
|
|
||||||
|
## Что это такое
|
||||||
|
|
||||||
|
Это бот основанный на перловых модулях [Telegram::Bot][1] для работы в
|
||||||
|
мессенджере Telegram и [Hailo][2] для генерации "умной" беседы.
|
||||||
|
|
||||||
|
Для корректной работы пришлось форкнуть [Telegram::Bot][1] чтобы обновить
|
||||||
|
интерфейс API до [Telegram Bot API v5][3]. Для избежания неоднозначностей модуль
|
||||||
|
пришлось переименовать и теперь он идёт в составе бота. Возможно, когда модуль
|
||||||
|
отлежится и будет похож на стабильный, он стнет самостоятельным и поселится на
|
||||||
|
metacpan.org
|
||||||
|
|
||||||
|
Конфиг бота должен лежать в **data/config.json**, пример конфига расположен в
|
||||||
|
**data/sample_config.json**.
|
||||||
|
|
||||||
|
Бота можно запустить через **bin/aleesa-telegram-bot** и он взлетит как юниксовый
|
||||||
|
демон (сделает двойной fork() и отцепится от stdio).
|
||||||
|
|
||||||
|
## Установка
|
||||||
|
|
||||||
|
Чтобы запустить приложение, его надо "забурстрапить" - загрузить и собрать все
|
||||||
|
необходимые зависимости.
|
||||||
|
|
||||||
|
Понадобится perl-5.18 а желательно новее, "Development Tools" или подобная
|
||||||
|
группа пакетов , perl, perl-devel, perl-local-lib, perl-app-cpanm, sqlite-devel,
|
||||||
|
zlib-devel, openssl-devel, libdb4-devel (Berkeley DB devel), make.
|
||||||
|
|
||||||
|
После установки этих пакетов можно будет запустить:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
|
||||||
|
bash bootstrap.sh
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
и по идее всё что нужно будет выкачено, собрано и разложено куда надо.
|
||||||
|
|
||||||
|
## Запуск и работа
|
||||||
|
|
||||||
|
Бот не использует [вебхуки][4], поэтому ничего дополнительного, кроме хорошего
|
||||||
|
подключения к интернету ему не нужно.
|
||||||
|
|
||||||
|
### Дополнительные настройки
|
||||||
|
|
||||||
|
Некоторые "плагины" требуют регистрации в сторонних сервисах (API keys, secrets
|
||||||
|
итд), чтобы бот не сыпал ошибками в логи (возможно, в дальнейшем возможно будет
|
||||||
|
отключить эти плагины).
|
||||||
|
|
||||||
|
#### OpenWeatherMap
|
||||||
|
|
||||||
|
Для работы погодного плагина надо [заргистрироваться][5], [залогиниться][6] и
|
||||||
|
[создать][7] api key. Free plan предлагает всё необхдимое для работы "плагина".
|
||||||
|
|
||||||
|
Когда api key будет создан, его нужно вписать в **config.json** "секцию"
|
||||||
|
openweathermap в качестве appid. На этом всё.
|
||||||
|
|
||||||
|
#### Imgur
|
||||||
|
|
||||||
|
Этот плагин "просто есть" и его на самом деле ничто и никто не использует. И в
|
||||||
|
этой связи все настройки плагина можно оставить "как есть" в примере конфига.
|
||||||
|
|
||||||
|
Тем не менее, если есть большое желание, то можно [залогиниться][8] и
|
||||||
|
[зарегистрировать][9] бота как приложение, а потом разрешить приложению
|
||||||
|
пользоваться API-шкой, как описано в [этом][10] или [этом][11] мануале.
|
||||||
|
|
||||||
|
#### Flickr
|
||||||
|
|
||||||
|
Этот "плагин" действительно используется для добычи ссылок на картинки в разных
|
||||||
|
"картиночных" командах бота.
|
||||||
|
|
||||||
|
Есть скрипт, который поможет с регистрацией приложения в api Flickr -
|
||||||
|
**bin/flickr_init.pl** он достанет все нужные ключи и секреты к ним.
|
||||||
|
|
||||||
|
До запуска этого скрипта, бот должен быть забутстраплен через **`bootstrap.sh`**.
|
||||||
|
|
||||||
|
[Залогиньтесь][11] и создайте [app][12] in своём Flicker-овском аккаунте. Далее,
|
||||||
|
*разлогиньтесь*, это нужно, чтобы потом проще было достать параметр verifier.
|
||||||
|
|
||||||
|
Запишите [app key и secret][13] в **config.json**. Удостоверьтесь, что параметр
|
||||||
|
"verifier" в конфиге либо закоменчен либо отсутствует.
|
||||||
|
|
||||||
|
Запустите **bin/flickr_init.pl** и следуте предлагаемым инструкциям - в итоге
|
||||||
|
будет дана ссылка, которую надо скопипастить в браузер и через один-два
|
||||||
|
редиректа вы попадёте на example.com. Из адресной строки браузера надо
|
||||||
|
скопировать параметр "verifier" и вписать его в **config.json**.
|
||||||
|
|
||||||
|
Чтобы сохранить в бд плагина API access token для нормальной работы "плагина",
|
||||||
|
второй раз запустите **bin/flickr_init.pl**. На этом всё.
|
||||||
|
|
||||||
|
[1]: https://metacpan.org/pod/Telegram::Bot
|
||||||
|
[2]: https://metacpan.org/pod/Hailo
|
||||||
|
[3]: https://core.telegram.org/bots/api
|
||||||
|
[4]: https://core.telegram.org/bots/api#getting-updates
|
||||||
|
[5]: https://home.openweathermap.org/users/sign_up
|
||||||
|
[6]: https://home.openweathermap.org/users/sign_in
|
||||||
|
[7]: https://home.openweathermap.org/api_keys
|
||||||
|
[8]: https://imgur.com/signin
|
||||||
|
[9]: https://api.imgur.com/oauth2/addclient
|
||||||
|
[10]: https://apidocs.imgur.com/#authorization-and-oauth
|
||||||
|
[11]: https://identity.flickr.com/login
|
||||||
|
[12]: https://www.flickr.com/services/apps/create/apply/
|
||||||
|
[13]: https://www.flickr.com/services/api/keys/
|
||||||
5
cpanfile
5
cpanfile
@@ -1,5 +1,8 @@
|
|||||||
requires 'Carp';
|
requires 'Carp';
|
||||||
|
requires 'CHI';
|
||||||
|
requires 'CHI::Driver::BerkeleyDB';
|
||||||
requires 'Cwd::utf8';
|
requires 'Cwd::utf8';
|
||||||
|
requires 'DateTime';
|
||||||
requires 'DB_File';
|
requires 'DB_File';
|
||||||
requires 'SQLite_File';
|
requires 'SQLite_File';
|
||||||
requires 'Digest::HMAC_SHA1';
|
requires 'Digest::HMAC_SHA1';
|
||||||
@@ -10,7 +13,6 @@ requires 'File::Basename';
|
|||||||
requires 'File::Path';
|
requires 'File::Path';
|
||||||
requires 'Hailo';
|
requires 'Hailo';
|
||||||
requires 'HTML::TokeParser';
|
requires 'HTML::TokeParser';
|
||||||
requires 'HTTP::Tiny';
|
|
||||||
requires 'IO::Socket::SSL';
|
requires 'IO::Socket::SSL';
|
||||||
requires 'JSON';
|
requires 'JSON';
|
||||||
requires 'JSON::XS';
|
requires 'JSON::XS';
|
||||||
@@ -23,6 +25,7 @@ requires 'Math::Random::Secure';
|
|||||||
requires 'Mojo::Base';
|
requires 'Mojo::Base';
|
||||||
requires 'Mojo::IOLoop';
|
requires 'Mojo::IOLoop';
|
||||||
requires 'Mojo::UserAgent';
|
requires 'Mojo::UserAgent';
|
||||||
|
requires 'Mojo::UserAgent::Cached';
|
||||||
requires 'Text::Fuzzy';
|
requires 'Text::Fuzzy';
|
||||||
requires 'URI::Encode::XS';
|
requires 'URI::Encode::XS';
|
||||||
requires 'URI::URL';
|
requires 'URI::URL';
|
||||||
|
|||||||
@@ -653,7 +653,15 @@ sub _process_message {
|
|||||||
|
|
||||||
foreach my $listener (@{ $self->listeners }) {
|
foreach my $listener (@{ $self->listeners }) {
|
||||||
# call the listener code, supplying ourself and the update
|
# call the listener code, supplying ourself and the update
|
||||||
|
my $evalRet = eval {
|
||||||
$listener->($self, $update);
|
$listener->($self, $update);
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
unless ($evalRet) {
|
||||||
|
cluck "An error occured during update processing: $EVAL_ERROR";
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -54,115 +54,14 @@ sub create_from_array {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unless (ref ($array) eq 'ARRAY') {
|
unless (ref ($array) eq 'ARRAY') {
|
||||||
cluck 'Not array ' . Dumper ($hash);
|
cluck 'Not array ' . Dumper ($array);
|
||||||
return $obj;
|
return $obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
cluck "Dumping class: " . Dumper ($class);
|
cluck "Dumping class: " . Dumper ($class);
|
||||||
cluck "Dumping array: " . Dumper ($array);
|
cluck "Dumping array: " . Dumper ($array);
|
||||||
Dumping array: $VAR1 = [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
'text' => 'Spotify',
|
|
||||||
'url' => 'https://open.spotify.com/track/44MxUoeZg6FXDlHMrSbVjX'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'text' => 'Tidal',
|
|
||||||
'url' => 'https://listen.tidal.com/track/159119367'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'url' => 'https://music.apple.com/us/album/_/1534782030?i=1534782031',
|
|
||||||
'text' => 'Apple'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
'text' => 'Deezer',
|
|
||||||
'url' => 'https://www.deezer.com/track/1112603372'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'text' => 'YouTube',
|
|
||||||
'url' => 'https://music.youtube.com/watch?v=KT1jJDh7ir0'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'url' => 'https://song.link/s/44MxUoeZg6FXDlHMrSbVjX',
|
|
||||||
'text' => 'eXtra'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
Dumping array: $VAR1 = [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
'callback_data' => 'l85882326|open.spotify.com/track/44MxUoeZg6FXDlHMrSbVjX',
|
|
||||||
'text' => 'Get links'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
# https://core.telegram.org/bots/api#inlinekeyboardmarkup
|
|
||||||
# который содержит в себе тупо массив
|
|
||||||
# Array of Array of Teapot::Bot::Object::InlineKeyboardButton
|
|
||||||
|
|
||||||
# https://core.telegram.org/bots/api#userprofilephotos
|
|
||||||
# аналогично предыдущему содержит
|
|
||||||
# Array of Array of Teapot::Bot::Object::PhotoSize
|
|
||||||
|
|
||||||
# deal with each type of field
|
|
||||||
foreach my $type (keys %{ $class->fields }) {
|
|
||||||
my @fields_of_this_type = @{ $class->fields->{$type} };
|
|
||||||
|
|
||||||
foreach my $field (@fields_of_this_type) {
|
|
||||||
# ignore undefined fields (sic!)
|
|
||||||
next if (! defined $field);
|
|
||||||
|
|
||||||
# ignore fields for which we have no value in the hash
|
|
||||||
next if (! defined $hash->{$field} );
|
|
||||||
|
|
||||||
# warn "type: $type field $field\n";
|
|
||||||
if ($type eq 'scalar') {
|
|
||||||
if ($obj->_field_is_array($field)) {
|
|
||||||
# simple scalar array ref, so just copy it
|
|
||||||
my $val = $hash->{$field};
|
|
||||||
# deal with boolean stuff so we don't pollute our object
|
|
||||||
# with JSON
|
|
||||||
if (ref($val) eq 'JSON::PP::Boolean') {
|
|
||||||
$val = !!$val;
|
|
||||||
}
|
|
||||||
$obj->$field($val);
|
|
||||||
}
|
|
||||||
elsif ($obj->_field_is_array_of_arrays) {
|
|
||||||
croak 'Not yet implemented for scalars';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
my $val = $hash->{$field};
|
|
||||||
if (ref($val) eq 'JSON::PP::Boolean') {
|
|
||||||
$val = 0+$val;
|
|
||||||
|
|
||||||
}
|
|
||||||
$obj->$field($val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
if ($obj->_field_is_array($field)) {
|
|
||||||
my @sub_array;
|
|
||||||
foreach my $data ( @{ $hash->{$field} } ) {
|
|
||||||
push @sub_array, $type->create_from_hash($data, $brain);
|
|
||||||
}
|
|
||||||
$obj->$field(\@sub_array);
|
|
||||||
}
|
|
||||||
elsif ($obj->_field_is_array_of_arrays) {
|
|
||||||
croak 'Not yet implemented for scalars';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$obj->$field($type->create_from_hash($hash->{$field}, $brain));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
# it's stub, return $obj
|
||||||
return $obj;
|
return $obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +79,7 @@ sub create_from_hash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unless (ref ($hash) eq 'HASH') {
|
unless (ref ($hash) eq 'HASH') {
|
||||||
if (ref ($array) eq 'ARRAY') {
|
if (ref ($hash) eq 'ARRAY') {
|
||||||
return create_from_array($class, $hash, $brain);
|
return create_from_array($class, $hash, $brain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,14 +13,14 @@ has 'custom_title'; # optional. Owner and administrators only.
|
|||||||
has 'is_anonymous'; # Optional. Owner and administrators only.
|
has 'is_anonymous'; # Optional. Owner and administrators only.
|
||||||
|
|
||||||
has 'can_be_edited'; # optional. Administrators only.
|
has 'can_be_edited'; # optional. Administrators only.
|
||||||
has 'can_manage_chat' # Optional. Administrators only. True, if the administrator can access the chat event
|
has 'can_manage_chat'; # Optional. Administrators only. True, if the administrator can access the chat event
|
||||||
# log, chat statistics, message statistics in channels, see channel members, see
|
# log, chat statistics, message statistics in channels, see channel members, see
|
||||||
# anonymous administrators in supergroups and ignore slow mode. Implied by any other
|
# anonymous administrators in supergroups and ignore slow mode. Implied by any other
|
||||||
# administrator privilege
|
# administrator privilege
|
||||||
has 'can_post_messages'; # optional. Administrators only.
|
has 'can_post_messages'; # optional. Administrators only.
|
||||||
has 'can_edit_messages'; # optional. Administrators only.
|
has 'can_edit_messages'; # optional. Administrators only.
|
||||||
has 'can_delete_messages'; # optional. Administrators only.
|
has 'can_delete_messages'; # optional. Administrators only.
|
||||||
has 'can_manage_voice_chats' # Optional. Administrators only. True, if the administrator can manage voice chats
|
has 'can_manage_voice_chats'; # Optional. Administrators only. True, if the administrator can manage voice chats
|
||||||
has 'can_restrict_members'; # optional. Administrators only.
|
has 'can_restrict_members'; # optional. Administrators only.
|
||||||
has 'can_promote_members'; # optional. Administrators only.
|
has 'can_promote_members'; # optional. Administrators only.
|
||||||
has 'can_change_info'; # optional. Administrators and restricted only.
|
has 'can_change_info'; # optional. Administrators and restricted only.
|
||||||
|
|||||||
14
lib/buni.pm
14
lib/buni.pm
@@ -7,8 +7,8 @@ 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 Carp qw (cluck);
|
use Carp qw (cluck);
|
||||||
use HTTP::Tiny;
|
|
||||||
use HTML::TokeParser;
|
use HTML::TokeParser;
|
||||||
|
use Mojo::UserAgent;
|
||||||
|
|
||||||
use version; our $VERSION = qw (1.0);
|
use version; our $VERSION = qw (1.0);
|
||||||
use Exporter qw (import);
|
use Exporter qw (import);
|
||||||
@@ -19,18 +19,18 @@ sub buni {
|
|||||||
my $ret = 'Нету Buni';
|
my $ret = 'Нету Buni';
|
||||||
|
|
||||||
for (1..3) {
|
for (1..3) {
|
||||||
my $http = HTTP::Tiny->new (timeout => 3);
|
my $ua = Mojo::UserAgent->new->connect_timeout (3);
|
||||||
$r = $http->get ('http://www.bunicomic.com/?random&nocache=1');
|
$r = $ua->get ('http://www.bunicomic.com/?random&nocache=1')->result;
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
|
|
||||||
sleep 2;
|
sleep 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
my $p = HTML::TokeParser->new(\$r->{content});
|
my $p = HTML::TokeParser->new(\$r->body);
|
||||||
my @a;
|
my @a;
|
||||||
|
|
||||||
# additional {} required in order "last" to work properly :)
|
# additional {} required in order "last" to work properly :)
|
||||||
@@ -47,7 +47,7 @@ sub buni {
|
|||||||
} while ($#{$a[0]} > 1);
|
} while ($#{$a[0]} > 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cluck sprintf 'Server return status %s with message: %s', $r->{status}, $r->{reason};
|
cluck sprintf 'Server return status %s with message: %s', $r->code, $r->message;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $ret;
|
return $ret;
|
||||||
|
|||||||
58
lib/drink.pm
58
lib/drink.pm
@@ -5,11 +5,16 @@ use strict;
|
|||||||
use warnings;
|
use warnings;
|
||||||
use utf8;
|
use utf8;
|
||||||
use open qw (:std :utf8);
|
use open qw (:std :utf8);
|
||||||
|
use CHI;
|
||||||
|
use CHI::Driver::BerkeleyDB;
|
||||||
|
use DateTime;
|
||||||
use English qw ( -no_match_vars );
|
use English qw ( -no_match_vars );
|
||||||
use Encode;
|
use Encode;
|
||||||
use Carp qw (cluck);
|
use Carp qw (cluck);
|
||||||
use HTTP::Tiny;
|
|
||||||
use HTML::TokeParser;
|
use HTML::TokeParser;
|
||||||
|
use Mojo::Log;
|
||||||
|
use Mojo::UserAgent::Cached;
|
||||||
|
use POSIX qw (strftime);
|
||||||
|
|
||||||
use version; our $VERSION = qw (1.0);
|
use version; our $VERSION = qw (1.0);
|
||||||
use Exporter qw (import);
|
use Exporter qw (import);
|
||||||
@@ -23,19 +28,56 @@ sub drink {
|
|||||||
my ($dayNum, $monthNum) = (localtime ())[3, 4];
|
my ($dayNum, $monthNum) = (localtime ())[3, 4];
|
||||||
my $url = sprintf 'https://kakoysegodnyaprazdnik.ru/baza/%s/%s', $MONTH[$monthNum], $dayNum;
|
my $url = sprintf 'https://kakoysegodnyaprazdnik.ru/baza/%s/%s', $MONTH[$monthNum], $dayNum;
|
||||||
|
|
||||||
for (1..3) {
|
# Those POSIX assholes just forgot to add unix timestamps without TZ offset, so...
|
||||||
my $http = HTTP::Tiny->new (timeout => 3);
|
my ($mday, $mon, $year) = (gmtime ())[3, 4, 5];
|
||||||
$r = $http->get ($url, {'Accept-Charset' => 'utf-8', 'Accept-Language' => 'ru-RU'});
|
my $offset = strftime ('%z', gmtime ());
|
||||||
|
my $offsetMinutes = (substr $offset, -2) * 60;
|
||||||
|
my $offsetHours = (substr $offset, 1, 2) * 60 * 60;
|
||||||
|
my $offsetSign;
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ((substr $offset, 0, 1) eq '+') {
|
||||||
|
$offsetSign = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $expirationDate = DateTime->new (
|
||||||
|
year => $year + 1900,
|
||||||
|
month => $mon + 1,
|
||||||
|
day => $mday,
|
||||||
|
hour => 0,
|
||||||
|
minute => 0,
|
||||||
|
second => 0
|
||||||
|
)->add (days => 1)->strftime ('%s');
|
||||||
|
|
||||||
|
if ((substr $offset, 0, 1) eq '+') {
|
||||||
|
$expirationDate = $expirationDate - $offsetHours - $offsetMinutes;
|
||||||
|
} else {
|
||||||
|
$expirationDate = $expirationDate + $offsetHours + $offsetMinutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (1..3) {
|
||||||
|
my $ua = Mojo::UserAgent::Cached->new->connect_timeout (3);
|
||||||
|
$ua->cache_agent(
|
||||||
|
CHI->new (
|
||||||
|
driver => 'BerkeleyDB',
|
||||||
|
root_dir => 'data/cache',
|
||||||
|
namespace => __PACKAGE__,
|
||||||
|
expires_at => $expirationDate,
|
||||||
|
expires_on_backend => 1,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
# just to make Mojo::UserAgent::Cached happy
|
||||||
|
$ua->logger (Mojo::Log->new (path => '/dev/null', level => 'error'));
|
||||||
|
$r = $ua->get ($url => {'Accept-Language' => 'ru-RU', 'Accept-Charset' => 'utf-8'})->result;
|
||||||
|
|
||||||
|
if ($r->is_success) {
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
|
|
||||||
sleep 2;
|
sleep 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
my $p = HTML::TokeParser->new(\$r->{content});
|
my $p = HTML::TokeParser->new(\$r->body);
|
||||||
my @a;
|
my @a;
|
||||||
my @holyday;
|
my @holyday;
|
||||||
|
|
||||||
@@ -58,7 +100,7 @@ sub drink {
|
|||||||
$ret = join "\n", @holyday;
|
$ret = join "\n", @holyday;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cluck sprintf 'Server return status %s with message: %s', $r->{status}, $r->{reason};
|
cluck sprintf 'Server return status %s with message: %s', $r->code, $r->message;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $ret;
|
return $ret;
|
||||||
|
|||||||
103
lib/flickr.pm
103
lib/flickr.pm
@@ -9,10 +9,9 @@ use English qw ( -no_match_vars );
|
|||||||
use Carp qw (cluck);
|
use Carp qw (cluck);
|
||||||
use Digest::HMAC_SHA1 qw (hmac_sha1);
|
use Digest::HMAC_SHA1 qw (hmac_sha1);
|
||||||
use File::Path qw (make_path);
|
use File::Path qw (make_path);
|
||||||
use JSON::XS;
|
|
||||||
use HTTP::Tiny;
|
|
||||||
use Math::Random::Secure qw (irand);
|
use Math::Random::Secure qw (irand);
|
||||||
use MIME::Base64;
|
use MIME::Base64;
|
||||||
|
use Mojo::UserAgent;
|
||||||
use SQLite_File;
|
use SQLite_File;
|
||||||
use URI::Encode::XS qw (uri_encode);
|
use URI::Encode::XS qw (uri_encode);
|
||||||
|
|
||||||
@@ -120,15 +119,15 @@ sub flickrRequestToken {
|
|||||||
uri_encode ($flickr_callback_url)
|
uri_encode ($flickr_callback_url)
|
||||||
);
|
);
|
||||||
|
|
||||||
my $http = HTTP::Tiny->new (timeout => 5);
|
my $ua = Mojo::UserAgent->new->connect_timeout (5);
|
||||||
my $r = $http->get ($url);
|
my $r = $ua->get ($url)->result;
|
||||||
my %params;
|
my %params;
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
# they ignore format=json, so...
|
# they ignore format=json, so...
|
||||||
%params = map {split /=/} split /&/, $r->{content};
|
%params = map {split /=/} split /&/, $r->body;
|
||||||
} else {
|
} else {
|
||||||
say "Something bad returns from flickr api: $r->{status} $r->{content}"; ## no critic (InputOutput::RequireCheckedSyscalls)
|
say "Something bad returns from flickr api: $r->code $r->message"; ## no critic (InputOutput::RequireCheckedSyscalls)
|
||||||
}
|
}
|
||||||
|
|
||||||
return %params;
|
return %params;
|
||||||
@@ -161,15 +160,15 @@ sub flickrAccessToken {
|
|||||||
uri_encode ($oauth_signature)
|
uri_encode ($oauth_signature)
|
||||||
);
|
);
|
||||||
|
|
||||||
my $http = HTTP::Tiny->new (timeout => 5);
|
my $ua = Mojo::UserAgent->new->connect_timeout (5);
|
||||||
my $r = $http->get ($url);
|
my $r = $ua->get ($url)->result;
|
||||||
my %params;
|
my %params;
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
# they ignore format=json
|
# they ignore format=json
|
||||||
%params = map {split /=/} split /&/, $r->{content};
|
%params = map {split /=/} split /&/, $r->body;
|
||||||
} else {
|
} else {
|
||||||
say "Something bad returns from flickr api: $r->{status} $r->{content}"; ## no critic (InputOutput::RequireCheckedSyscalls)
|
say "Something bad returns from flickr api: $r->code $r->message"; ## no critic (InputOutput::RequireCheckedSyscalls)
|
||||||
}
|
}
|
||||||
|
|
||||||
return %params;
|
return %params;
|
||||||
@@ -183,13 +182,13 @@ sub flickrAuthorization {
|
|||||||
$flickr_request_token,
|
$flickr_request_token,
|
||||||
);
|
);
|
||||||
|
|
||||||
my $http = HTTP::Tiny->new (timeout => 5);
|
my $ua = Mojo::UserAgent->new->connect_timeout (5);
|
||||||
my $r = $http->get ($url);
|
my $r = $ua->get ($url)->result;
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
return $r->{url};
|
return $r->url;
|
||||||
} else {
|
} else {
|
||||||
say "Something went wrong: $r->{status} $r->{content}"; ## no critic (InputOutput::RequireCheckedSyscalls)
|
say "Something went wrong: $r->code $r->message"; ## no critic (InputOutput::RequireCheckedSyscalls)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -224,24 +223,23 @@ sub flickrTestLogin {
|
|||||||
uri_encode ($oauth_signature)
|
uri_encode ($oauth_signature)
|
||||||
);
|
);
|
||||||
|
|
||||||
my $http = HTTP::Tiny->new (timeout => 5);
|
my $ua = Mojo::UserAgent->new->connect_timeout (5);
|
||||||
my $r = $http->get ($url);
|
my $r = $ua->get ($url)->result;
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
my $response = eval {
|
my $response = eval {
|
||||||
my $j = JSON::XS->new->utf8->relaxed;
|
return $r->json;
|
||||||
return $j->decode ($r->{content});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (defined $response) {
|
if (defined $response) {
|
||||||
say Dumper $response; ## no critic (InputOutput::RequireCheckedSyscalls)
|
say Dumper $response; ## no critic (InputOutput::RequireCheckedSyscalls)
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
say "Flickr api returns incorrect json: $r->{content}"; ## no critic (InputOutput::RequireCheckedSyscalls)
|
say "Flickr api returns incorrect json: $r->message"; ## no critic (InputOutput::RequireCheckedSyscalls)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
say "HTTP status code not 200: $r->{status}, $r>{contenr}"; ## no critic (InputOutput::RequireCheckedSyscalls)
|
say "HTTP status code not 200: $r->code, $r->message"; ## no critic (InputOutput::RequireCheckedSyscalls)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -282,13 +280,12 @@ sub flickrSearchByText {
|
|||||||
uri_encode ($text)
|
uri_encode ($text)
|
||||||
);
|
);
|
||||||
|
|
||||||
my $http = HTTP::Tiny->new (timeout => 5);
|
my $ua = Mojo::UserAgent->new->connect_timeout (5);
|
||||||
my $r = $http->get ($url);
|
my $r = $ua->get ($url)->result;
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
my $response = eval {
|
my $response = eval {
|
||||||
my $j = JSON::XS->new->utf8->relaxed;
|
return $r->json;
|
||||||
return $j->decode ($r->{content});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (defined $response) {
|
if (defined $response) {
|
||||||
@@ -326,31 +323,31 @@ sub flickrSearchByText {
|
|||||||
uri_encode ($text)
|
uri_encode ($text)
|
||||||
);
|
);
|
||||||
|
|
||||||
$http = HTTP::Tiny->new (timeout => 5);
|
$ua = Mojo::UserAgent->new->connect_timeout (5);
|
||||||
$r = $http->get ($url);
|
$r = $ua->get ($url)->result;
|
||||||
|
|
||||||
if ($r->{success}) {
|
|
||||||
|
if ($r->is_success) {
|
||||||
$response = eval {
|
$response = eval {
|
||||||
my $j = JSON::XS->new->utf8->relaxed;
|
return $r->json;
|
||||||
return $j->decode ($r->{content});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (defined $response) {
|
if (defined $response) {
|
||||||
return $response;
|
return $response;
|
||||||
} else {
|
} else {
|
||||||
cluck "Flickr api returns incorrect json: $r->{content}";
|
cluck "Flickr api returns incorrect json: $r->message";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cluck "Flickr api returns incorrect json: $r->{content}";
|
cluck "Flickr api returns incorrect json: $r->message";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cluck "Flickr api returns incorrect json: $r->{content}";
|
cluck "Flickr api returns incorrect json: $r->message";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cluck "HTTP status code not 200: $r->{status}, $r->{content}";
|
cluck "HTTP status code not 200: $r->code, $r->message";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -391,13 +388,12 @@ sub flickrSearchByTags {
|
|||||||
uri_encode ($tags)
|
uri_encode ($tags)
|
||||||
);
|
);
|
||||||
|
|
||||||
my $http = HTTP::Tiny->new (timeout => 5);
|
my $ua = Mojo::UserAgent->new->connect_timeout (5);
|
||||||
my $r = $http->get ($url);
|
my $r = $ua->get ($url)->result;
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
my $response = eval {
|
my $response = eval {
|
||||||
my $j = JSON::XS->new->utf8->relaxed;
|
return $r->json;
|
||||||
return $j->decode ($r->{content});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (defined $response) {
|
if (defined $response) {
|
||||||
@@ -435,31 +431,30 @@ sub flickrSearchByTags {
|
|||||||
uri_encode ($tags)
|
uri_encode ($tags)
|
||||||
);
|
);
|
||||||
|
|
||||||
$http = HTTP::Tiny->new (timeout => 5);
|
$ua = Mojo::UserAgent->new->connect_timeout (5);
|
||||||
$r = $http->get ($url);
|
$r = $ua->get ($url)->result;
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
$response = eval {
|
$response = eval {
|
||||||
my $j = JSON::XS->new->utf8->relaxed;
|
return $r->json;
|
||||||
return $j->decode ($r->{content});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (defined $response) {
|
if (defined $response) {
|
||||||
return $response;
|
return $response;
|
||||||
} else {
|
} else {
|
||||||
cluck "Flickr api returns incorrect json: $r->{content}";
|
cluck "Flickr api returns incorrect json: $r->message";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cluck "Flickr api returns incorrect json: $r->{content}";
|
cluck "Flickr api returns incorrect json: $r->message";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cluck "Flickr api returns incorrect json: $r->{content}";
|
cluck "Flickr api returns incorrect json: $r->message";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cluck "HTTP status code not 200: $r->{status}, $r>{contenr}";
|
cluck "HTTP status code not 200: $r->code, $r->message";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -484,8 +479,8 @@ sub flickr_init {
|
|||||||
my $flickr_request_token_secret = $secret{flickr_request_token_secret};
|
my $flickr_request_token_secret = $secret{flickr_request_token_secret};
|
||||||
|
|
||||||
unless (defined $flickr_request_token || defined $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";
|
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";
|
say 'Please remove verifier settings from config.json and run this script again'; ## no critic (InputOutput::RequireCheckedSyscalls)
|
||||||
untie %secret;
|
untie %secret;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
51
lib/image.pm
51
lib/image.pm
@@ -8,9 +8,8 @@ use open qw (:std :utf8);
|
|||||||
use English qw ( -no_match_vars );
|
use English qw ( -no_match_vars );
|
||||||
use Carp qw (cluck);
|
use Carp qw (cluck);
|
||||||
use File::Path qw (make_path);
|
use File::Path qw (make_path);
|
||||||
use JSON::XS;
|
|
||||||
use HTTP::Tiny;
|
|
||||||
use Math::Random::Secure qw (irand);
|
use Math::Random::Secure qw (irand);
|
||||||
|
use Mojo::UserAgent;
|
||||||
use SQLite_File;
|
use SQLite_File;
|
||||||
use conf qw (loadConf);
|
use conf qw (loadConf);
|
||||||
use flickr qw (flickr_by_tags);
|
use flickr qw (flickr_by_tags);
|
||||||
@@ -29,19 +28,19 @@ sub kitty {
|
|||||||
my $ret = 'Нету кошечек, все разбежались.';
|
my $ret = 'Нету кошечек, все разбежались.';
|
||||||
|
|
||||||
for (1..3) {
|
for (1..3) {
|
||||||
my $http = HTTP::Tiny->new (timeout => 3);
|
my $ua = Mojo::UserAgent->new->connect_timeout (3);
|
||||||
$r = $http->get ('https://api.thecatapi.com/v1/images/search');
|
$r = $ua->get ('https://api.thecatapi.com/v1/images/search')->result;
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
|
|
||||||
sleep 2;
|
sleep 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
my $jcat = eval {
|
my $jcat = eval {
|
||||||
decode_json ($r->{content})
|
return $r->json;
|
||||||
};
|
};
|
||||||
|
|
||||||
unless (defined $jcat) {
|
unless (defined $jcat) {
|
||||||
@@ -53,7 +52,7 @@ sub kitty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cluck sprintf 'Server return status %s with message: %s', $r->{status}, $r->{reason};
|
cluck sprintf 'Server return status %s with message: %s', $r->code, $r->message;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $ret;
|
return $ret;
|
||||||
@@ -64,19 +63,19 @@ sub fox {
|
|||||||
my $ret = 'Нету лисичек, все разбежались.';
|
my $ret = 'Нету лисичек, все разбежались.';
|
||||||
|
|
||||||
for (1..3) {
|
for (1..3) {
|
||||||
my $http = HTTP::Tiny->new (timeout => 3);
|
my $ua = Mojo::UserAgent->new->connect_timeout (3);
|
||||||
$r = $http->get ('https://randomfox.ca/floof/');
|
$r = $ua->get ('https://randomfox.ca/floof/')->result;
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
|
|
||||||
sleep 2;
|
sleep 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
my $jfox = eval {
|
my $jfox = eval {
|
||||||
decode_json ($r->{content})
|
return $r->json;
|
||||||
};
|
};
|
||||||
|
|
||||||
unless (defined $jfox) {
|
unless (defined $jfox) {
|
||||||
@@ -88,7 +87,7 @@ sub fox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cluck sprintf 'Server return status %s with message: %s', $r->{status}, $r->{reason};
|
cluck sprintf 'Server return status %s with message: %s', $r->code, $r->message;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $ret;
|
return $ret;
|
||||||
@@ -99,19 +98,19 @@ sub oboobs {
|
|||||||
my $ret = 'Нету cисичек, все разбежались.';
|
my $ret = 'Нету cисичек, все разбежались.';
|
||||||
|
|
||||||
for (1..3) {
|
for (1..3) {
|
||||||
my $http = HTTP::Tiny->new (timeout => 3);
|
my $ua = Mojo::UserAgent->new->connect_timeout (3);
|
||||||
$r = $http->get ('http://api.oboobs.ru/boobs/0/1/random');
|
$r = $ua->get ('http://api.oboobs.ru/boobs/0/1/random')->result;
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
|
|
||||||
sleep 2;
|
sleep 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
my $joboobs = eval {
|
my $joboobs = eval {
|
||||||
decode_json ($r->{content})
|
return $r->json;
|
||||||
};
|
};
|
||||||
|
|
||||||
unless (defined $joboobs) {
|
unless (defined $joboobs) {
|
||||||
@@ -123,7 +122,7 @@ sub oboobs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cluck sprintf 'Server return status %s with message: %s', $r->{status}, $r->{reason};
|
cluck sprintf 'Server return status %s with message: %s', $r->code, $r->message;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $ret;
|
return $ret;
|
||||||
@@ -134,19 +133,19 @@ sub obutts {
|
|||||||
my $ret = 'Нету попок, все разбежались.';
|
my $ret = 'Нету попок, все разбежались.';
|
||||||
|
|
||||||
for (1..3) {
|
for (1..3) {
|
||||||
my $http = HTTP::Tiny->new (timeout => 3);
|
my $ua = Mojo::UserAgent->new->connect_timeout (3);
|
||||||
$r = $http->get ('http://api.obutts.ru/butts/0/1/random');
|
$r = $ua->get ('http://api.obutts.ru/butts/0/1/random')->result;
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
|
|
||||||
sleep 2;
|
sleep 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
my $jobutts = eval {
|
my $jobutts = eval {
|
||||||
decode_json ($r->{content})
|
return $r->json;
|
||||||
};
|
};
|
||||||
|
|
||||||
unless (defined $jobutts) {
|
unless (defined $jobutts) {
|
||||||
@@ -158,7 +157,7 @@ sub obutts {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cluck sprintf 'Server return status %s with message: %s', $r->{status}, $r->{reason};
|
cluck sprintf 'Server return status %s with message: %s', $r->code, $r->message;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $ret;
|
return $ret;
|
||||||
|
|||||||
44
lib/imgur.pm
44
lib/imgur.pm
@@ -8,9 +8,8 @@ use open qw (:std :utf8);
|
|||||||
use English qw ( -no_match_vars );
|
use English qw ( -no_match_vars );
|
||||||
use Carp qw (cluck);
|
use Carp qw (cluck);
|
||||||
use File::Path qw (make_path);
|
use File::Path qw (make_path);
|
||||||
use JSON::XS;
|
|
||||||
use HTTP::Tiny;
|
|
||||||
use Math::Random::Secure qw (irand);
|
use Math::Random::Secure qw (irand);
|
||||||
|
use Mojo::UserAgent;
|
||||||
use SQLite_File;
|
use SQLite_File;
|
||||||
use conf qw (loadConf);
|
use conf qw (loadConf);
|
||||||
|
|
||||||
@@ -30,7 +29,6 @@ sub imgur {
|
|||||||
|
|
||||||
my $refresh_url = 'https://api.imgur.com/oauth2/token';
|
my $refresh_url = 'https://api.imgur.com/oauth2/token';
|
||||||
my $search_url = 'https://api.imgur.com/3/gallery/search/';
|
my $search_url = 'https://api.imgur.com/3/gallery/search/';
|
||||||
my $http = HTTP::Tiny->new (timeout => 5);
|
|
||||||
|
|
||||||
# load all tokens from api secrets db
|
# load all tokens from api secrets db
|
||||||
my $backingfile = sprintf '%s/secrets.sqlite', $dir;
|
my $backingfile = sprintf '%s/secrets.sqlite', $dir;
|
||||||
@@ -62,20 +60,13 @@ sub imgur {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# query api
|
# query api
|
||||||
my $r = $http->get (
|
my $ua = Mojo::UserAgent->new->connect_timeout (3);
|
||||||
$search_url . $tag,
|
my $r = $ua->get ($search_url . $tag => {Authorization => "Bearer $imgur_access_token"})->result;
|
||||||
{
|
|
||||||
headers => {
|
|
||||||
Authorization => "Bearer $imgur_access_token"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
# looks like we need to refresh token
|
# looks like we need to refresh token
|
||||||
if (($r->{status} == 401) || ($r->{status} == 403)) {
|
if (($r->code == 401) || ($r->code == 403)) {
|
||||||
$r = $http->post_form (
|
$ua->post (
|
||||||
$refresh_url,
|
$refresh_url => form {
|
||||||
{
|
|
||||||
refresh_token => $imgur_refresh_token,
|
refresh_token => $imgur_refresh_token,
|
||||||
client_id => $imgur_client_id,
|
client_id => $imgur_client_id,
|
||||||
clinent_secret => $imgur_client_secret,
|
clinent_secret => $imgur_client_secret,
|
||||||
@@ -83,13 +74,13 @@ sub imgur {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
unless ($r->{success}) {
|
unless ($r->is_success) {
|
||||||
cluck "Unable to refresh imgur token, please re-authorise app! or imgur experiencing api outage: $r->{status} $r->{content}";
|
cluck "Unable to refresh imgur token, please re-authorise app! or imgur experiencing api outage: $r->code $r->message";
|
||||||
untie %secret;
|
untie %secret;
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $refresh = eval { decode_json $r->{content} };
|
my $refresh = eval { return $r->json; };
|
||||||
|
|
||||||
if (defined $refresh) {
|
if (defined $refresh) {
|
||||||
$imgur_refresh_token = $refresh->{refresh_token};
|
$imgur_refresh_token = $refresh->{refresh_token};
|
||||||
@@ -103,20 +94,13 @@ sub imgur {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# query api again
|
# query api again
|
||||||
$r = $http->get (
|
$r = $ua->get ($search_url . $tag => {Authorization => "Bearer $imgur_access_token"})->result;
|
||||||
$search_url . $tag,
|
|
||||||
{
|
|
||||||
headers => {
|
|
||||||
Authorization => "Bearer $imgur_access_token"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
untie %secret;
|
untie %secret;
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
my $searchResult = eval { decode_json ($r->{content}) };
|
my $searchResult = eval { return $r->json; };
|
||||||
my @urls;
|
my @urls;
|
||||||
|
|
||||||
if (defined $searchResult) {
|
if (defined $searchResult) {
|
||||||
@@ -148,7 +132,7 @@ sub imgur {
|
|||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cluck "Unable to get results from imgur api: $r->{content}";
|
cluck "Unable to get results from imgur api: $r->message";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -156,7 +140,7 @@ sub imgur {
|
|||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cluck "Unable to get results from imgur api: $r->{status}, $r->{content}";
|
cluck "Unable to get results from imgur api: $r->code, $r->message";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ 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 Carp qw (cluck);
|
use Carp qw (cluck);
|
||||||
use HTTP::Tiny;
|
|
||||||
use HTML::TokeParser;
|
use HTML::TokeParser;
|
||||||
use Math::Random::Secure qw (irand);
|
use Math::Random::Secure qw (irand);
|
||||||
|
use Mojo::UserAgent;
|
||||||
|
|
||||||
use version; our $VERSION = qw (1.0);
|
use version; our $VERSION = qw (1.0);
|
||||||
use Exporter qw (import);
|
use Exporter qw (import);
|
||||||
@@ -21,18 +21,18 @@ sub monkeyuser {
|
|||||||
my @link;
|
my @link;
|
||||||
|
|
||||||
for (1..3) {
|
for (1..3) {
|
||||||
my $http = HTTP::Tiny->new (timeout => 3);
|
my $ua = Mojo::UserAgent->new->connect_timeout (3);
|
||||||
$r = $http->get ('https://www.monkeyuser.com/toc/');
|
$r = $ua->get ('https://www.monkeyuser.com/toc/')->result;
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
|
|
||||||
sleep 2;
|
sleep 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
my $p = HTML::TokeParser->new(\$r->{content});
|
my $p = HTML::TokeParser->new(\$r->body);
|
||||||
my @a;
|
my @a;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
|||||||
@@ -7,11 +7,13 @@ 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 Carp qw (carp cluck);
|
use Carp qw (carp cluck);
|
||||||
|
use CHI;
|
||||||
|
use CHI::Driver::BerkeleyDB;
|
||||||
use DB_File;
|
use DB_File;
|
||||||
use Digest::MD5 qw (md5_base64);
|
use Digest::MD5 qw (md5_base64);
|
||||||
use File::Path qw (make_path);
|
use File::Path qw (make_path);
|
||||||
use HTTP::Tiny;
|
use Mojo::Log;
|
||||||
use JSON::XS;
|
use Mojo::UserAgent::Cached;
|
||||||
use conf qw (loadConf);
|
use conf qw (loadConf);
|
||||||
use util qw (trim urlencode);
|
use util qw (trim urlencode);
|
||||||
|
|
||||||
@@ -71,45 +73,32 @@ sub weather {
|
|||||||
sub __weather {
|
sub __weather {
|
||||||
my $city = shift;
|
my $city = shift;
|
||||||
$city = urlencode $city;
|
$city = urlencode $city;
|
||||||
my $id = md5_base64 $city;
|
|
||||||
my $appid = $c->{openweathermap}->{appid};
|
my $appid = $c->{openweathermap}->{appid};
|
||||||
my $cachedir = $c->{openweathermap}->{cachedir};
|
my $cachedir = $c->{openweathermap}->{cachedir};
|
||||||
my $cachetimefile = $cachedir . '/openweathermap.Id-Timestamp.db';
|
|
||||||
my $cachedatafile = $cachedir . '/openweathermap.Id-Data.db';
|
|
||||||
my $now = time ();
|
my $now = time ();
|
||||||
my $fc;
|
my $fc;
|
||||||
my $w;
|
my $w;
|
||||||
|
|
||||||
unless (-d $cachedir) {
|
|
||||||
make_path ($cachedir) || do {
|
|
||||||
cluck "[ERROR] Unable to create $cachedir: $OS_ERROR";
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
# attach to cache data
|
|
||||||
tie my %cachetime, 'DB_File', $cachetimefile or do {
|
|
||||||
carp "[ERROR] Something nasty happen when cachetime ties to its data: $OS_ERROR";
|
|
||||||
return undef;
|
|
||||||
};
|
|
||||||
|
|
||||||
tie my %cachedata, 'DB_File', $cachedatafile or do {
|
|
||||||
carp "[ERROR] Something nasty happen when cachedata ties to its data: $OS_ERROR";
|
|
||||||
return undef;
|
|
||||||
};
|
|
||||||
|
|
||||||
# lookup cache
|
|
||||||
if (defined ($cachetime{$id}) && ($now - $cachetime{$id}) < 10800) {
|
|
||||||
$fc = decode_json $cachedata{$id};
|
|
||||||
} else {
|
|
||||||
my $r;
|
my $r;
|
||||||
|
|
||||||
# try 3 times and giveup
|
# try 3 times and giveup
|
||||||
for (1..3) {
|
for (1..3) {
|
||||||
my $http = HTTP::Tiny->new (timeout => 3);
|
my $ua = Mojo::UserAgent::Cached->new;
|
||||||
$r = $http->get (sprintf ('http://api.openweathermap.org/data/2.5/weather?q=%s&lang=ru&APPID=%s', $city, $appid));
|
$ua->local_dir ($cachedir);
|
||||||
|
$ua->cache_agent(
|
||||||
|
CHI->new (
|
||||||
|
driver => 'BerkeleyDB',
|
||||||
|
root_dir => $cachedir,
|
||||||
|
namespace => __PACKAGE__,
|
||||||
|
expires_in => '3 hours',
|
||||||
|
expires_on_backend => 1,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
# just to make Mojo::UserAgent::Cached happy
|
||||||
|
$ua->logger (Mojo::Log->new (path => '/dev/null', level => 'error'));
|
||||||
|
$r = $ua->get (sprintf ('http://api.openweathermap.org/data/2.5/weather?q=%s&lang=ru&APPID=%s', $city, $appid))->result;
|
||||||
|
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,30 +106,19 @@ sub __weather {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# all 3 times can give error, so check it here
|
# all 3 times can give error, so check it here
|
||||||
if ($r->{success}) {
|
if ($r->is_success) {
|
||||||
$fc = eval {
|
$fc = eval {
|
||||||
decode_json $r->{content};
|
return $r->json;
|
||||||
};
|
};
|
||||||
|
|
||||||
if ($EVAL_ERROR) {
|
unless ($fc) {
|
||||||
cluck "[WARN] openweathermap returns corrupted json: $EVAL_ERROR";
|
cluck "[WARN] openweathermap returns corrupted json: $EVAL_ERROR";
|
||||||
untie %cachetime;
|
|
||||||
untie %cachedata;
|
|
||||||
return undef;
|
return undef;
|
||||||
};
|
};
|
||||||
|
|
||||||
$cachetime{$id} = $now;
|
|
||||||
$cachedata{$id} = encode_json $fc;
|
|
||||||
} else {
|
} else {
|
||||||
cluck sprintf 'Server return status %s with message: %s', $r->{status}, $r->{reason};
|
cluck sprintf 'Server return status %s with message: %s', $r->code, $r->message;
|
||||||
untie %cachetime;
|
|
||||||
untie %cachedata;
|
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
untie %cachetime;
|
|
||||||
untie %cachedata;
|
|
||||||
|
|
||||||
# TODO: check all of this for existence
|
# TODO: check all of this for existence
|
||||||
$w->{'name'} = $fc->{name};
|
$w->{'name'} = $fc->{name};
|
||||||
|
|||||||
15
lib/xkcd.pm
15
lib/xkcd.pm
@@ -5,18 +5,23 @@ use strict;
|
|||||||
use warnings;
|
use warnings;
|
||||||
use utf8;
|
use utf8;
|
||||||
use open qw (:std :utf8);
|
use open qw (:std :utf8);
|
||||||
use HTTP::Tiny;
|
use Mojo::UserAgent;
|
||||||
|
|
||||||
use version; our $VERSION = qw (1.0);
|
use version; our $VERSION = qw (1.0);
|
||||||
use Exporter qw (import);
|
use Exporter qw (import);
|
||||||
our @EXPORT_OK = qw (xkcd);
|
our @EXPORT_OK = qw (xkcd);
|
||||||
|
|
||||||
sub xkcd {
|
sub xkcd {
|
||||||
my $http = HTTP::Tiny->new (timeout => 5, max_redirect => 0);
|
my $ua = Mojo::UserAgent->new->connect_timeout (5)->max_redirects (0);
|
||||||
my $r = $http->get ('https://xkcd.ru/random/');
|
my $r = $ua->get ('https://xkcd.ru/random/')->result;
|
||||||
|
|
||||||
if (defined $r->{headers} && defined $r->{headers}->{location} && $r->{headers}->{location} ne '') {
|
if (
|
||||||
return sprintf '[xkcd.ru](https://xkcd.ru/i/%s_v1.png)', substr ($r->{headers}->{location}, 1, -1);
|
defined $r->content &&
|
||||||
|
defined $r->content->headers &&
|
||||||
|
defined $r->content->headers->location &&
|
||||||
|
$r->content->headers->location ne ''
|
||||||
|
) {
|
||||||
|
return sprintf '[xkcd.ru](https://xkcd.ru/i/%s_v1.png)', substr ($r->content->headers->location, 1, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'Комикс-стрип нарисовать не так-то просто :(';
|
return 'Комикс-стрип нарисовать не так-то просто :(';
|
||||||
|
|||||||
Reference in New Issue
Block a user