From 4a312e23d15816559acbc56b7dbff16ec0f4015b Mon Sep 17 00:00:00 2001 From: eleksir Date: Sat, 19 Dec 2020 15:54:18 +0300 Subject: [PATCH] Add replyMd() to message object, implement canTalk() check in Teapot lib, re-arrange code --- lib/Teapot/Bot/Object/ChatPermissions.pm | 49 ++++++++- lib/Teapot/Bot/Object/Message.pm | 37 ++++++- lib/botlib.pm | 67 +++++++----- lib/telegrambot.pm | 126 +++-------------------- 4 files changed, 136 insertions(+), 143 deletions(-) diff --git a/lib/Teapot/Bot/Object/ChatPermissions.pm b/lib/Teapot/Bot/Object/ChatPermissions.pm index 3763475..675986a 100644 --- a/lib/Teapot/Bot/Object/ChatPermissions.pm +++ b/lib/Teapot/Bot/Object/ChatPermissions.pm @@ -1,6 +1,6 @@ package Teapot::Bot::Object::ChatPermissions; # ABSTRACT: The base class for Telegram 'ChatPermissions' type objects - +use Carp qw(carp); use Mojo::Base 'Teapot::Bot::Object::Base'; $Teapot::Bot::Object::ChatPermissions::VERSION = '0.022'; @@ -28,6 +28,53 @@ sub fields { }; } +sub canTalk { + my $self = shift; + my $chatid = shift; + + my $can_talk = 0; + + if ($chatid < 0) { + # group chat + my $chatobj = $self->_brain->getChat ({ 'chat_id' => $chatid }); + + # on api error, keep silence + unless ($chatobj) { + carp "Unable to get chat info for $chatid from telegram API"; + return 0; + } + + my $myObj = $self->_brain->getMe (); + + # on api error, keep silence + unless ($chatobj) { + carp "Unable to get chat info for $chatid from telegram API"; + return 0; + } + + my $myid = $myObj->id; + my $me = $self->_brain->getChatMember ({ 'chat_id' => $chatid, 'user_id' => $myid }); + + # on api error, keep silence + unless ($me) { + carp 'Unable to get chat info for bot itself from telegram API'; + return 0; + } + + if ($me->{'status'} eq 'administrator') { + $can_talk = 1; + } else { + my $group_talk = int ($chatobj->{permissions}->{can_send_messages}); + $can_talk = $group_talk; + } + } else { + # 1 on 1 chat with user + $can_talk = 1; + } + + return $can_talk; +} + 1; __END__ diff --git a/lib/Teapot/Bot/Object/Message.pm b/lib/Teapot/Bot/Object/Message.pm index 5f2090e..308bbc2 100644 --- a/lib/Teapot/Bot/Object/Message.pm +++ b/lib/Teapot/Bot/Object/Message.pm @@ -29,6 +29,7 @@ use Teapot::Bot::Object::PassportData; use Teapot::Bot::Object::InlineKeyboardMarkup; use Data::Dumper; +use Carp qw(cluck); $Teapot::Bot::Object::Message::VERSION = '0.022'; @@ -134,12 +135,44 @@ sub arrays { sub reply { my $self = shift; my $text = shift; - return $self->_brain->sendMessage({chat_id => $self->chat->id, text => $text}); + + my $chatid = $self->chat->id; + + if (Teapot::Bot::Object::ChatPermissions::canTalk($self, $chatid)) { + return $self->_brain->sendMessage({chat_id => $chatid, text => $text}); + } + + return {'error' => 1}; +} + +sub replyMd { + my $self = shift; + my $text = shift; + + my $chatid = $self->chat->id; + + if (Teapot::Bot::Object::ChatPermissions::canTalk ($self, $chatid)) { + my $send_args; + $send_args->{text} = $text; + $send_args->{parse_mode} = 'Markdown'; + $send_args->{chat_id} = $chatid; + + return $self->_brain->sendMessage($send_args); + } + + return {'error' => 1}; } sub typing { my $self = shift; - return $self->_brain->sendChatAction({chat_id => $self->chat->id}); + + my $chatid = $self->chat->id; + + if (Teapot::Bot::Object::ChatPermissions::canTalk ($self, $chatid)) { + return $self->_brain->sendChatAction({chat_id => $chatid}); + } + + return {'error' => 1}; } 1; diff --git a/lib/botlib.pm b/lib/botlib.pm index b8f4234..f9fd413 100644 --- a/lib/botlib.pm +++ b/lib/botlib.pm @@ -22,7 +22,7 @@ use archeologist qw (dig); use vars qw/$VERSION/; use Exporter qw(import); -our @EXPORT_OK = qw(weather trim randomCommonPhrase command); +our @EXPORT_OK = qw(weather trim randomCommonPhrase command highlight); $VERSION = '1.0'; my $c = loadConf(); @@ -233,32 +233,7 @@ sub command { my $text = shift; my $chatid = shift; my $reply; - my $fullname; - my $highlight; - my $username; - my $userid = $msg->from->id; - $username = $msg->from->username if ($msg->from->can ('username') && defined($msg->from->username)); - - if ($msg->from->can ('first_name') && defined ($msg->from->first_name)) { - $fullname = $msg->from->first_name; - - if ($msg->from->can ('last_name') && defined ($msg->from->last_name)) { - $fullname .= ' ' . $msg->from->last_name; - } - } elsif ($msg->from->can ('last_name') && defined ($msg->from->last_name)) { - $fullname .= $msg->from->last_name; - } - - if (defined ($username)) { - if (defined ($fullname)) { - $highlight = "[$fullname](tg://user?id=$userid)"; - } else { - $highlight = "[$username](tg://user?id=$userid)"; - } - } else { - $highlight = "[$fullname](tg://user?id=$userid)"; - } - + my ($userid, $username, $fullname, $highlight, $visavi) = highlight ($msg); if (substr ($text, 1) eq 'ping') { $reply = 'Pong.'; @@ -308,6 +283,44 @@ sub command { return $reply; } +sub highlight { + my $msg = shift; + + my $fullname; + my $highlight; + my $username; + my $visavi = ''; + my $userid = $msg->from->id; + $username = $msg->from->username if ($msg->from->can ('username') && defined($msg->from->username)); + + if ($msg->from->can ('first_name') && defined ($msg->from->first_name)) { + $fullname = $msg->from->first_name; + + if ($msg->from->can ('last_name') && defined ($msg->from->last_name)) { + $fullname .= ' ' . $msg->from->last_name; + } + } elsif ($msg->from->can ('last_name') && defined ($msg->from->last_name)) { + $fullname .= $msg->from->last_name; + } + + if (defined ($username)) { + $visavi .= '@' . $username; + + if (defined ($fullname)) { + $highlight = "[$fullname](tg://user?id=$userid)"; + $visavi .= ', ' . $fullname; + } else { + $highlight = "[$username](tg://user?id=$userid)"; + } + } else { + $highlight = "[$fullname](tg://user?id=$userid)"; + $visavi .= $fullname; + } + + $visavi .= " ($userid)"; + + return ($userid, $username, $fullname, $highlight, $visavi); +} 1; # vim: set ft=perl noet ai ts=4 sw=4 sts=4: diff --git a/lib/telegrambot.pm b/lib/telegrambot.pm index b9b28d4..3bbc285 100644 --- a/lib/telegrambot.pm +++ b/lib/telegrambot.pm @@ -12,7 +12,7 @@ use File::Path qw(mkpath); use Hailo; use Mojo::Base 'Teapot::Bot::Brain'; use conf qw(loadConf); -use botlib qw(weather trim randomCommonPhrase command); +use botlib qw(weather trim randomCommonPhrase command highlight); use karma qw(karmaSet); use fortune qw(fortune fortune_toggle_list); @@ -28,8 +28,6 @@ my $myusername; my $myfirst_name; my $mylast_name; my $myfullname; -# guess it later -my $can_talk = 0; has token => $c->{telegrambot}->{token}; @@ -57,12 +55,7 @@ sub __on_msg { my $chatid; my $chatname = 'Noname chat'; # user sending message info - my $userid; - my $username; - my $fullname; - my $vis_a_vi = 'unknown'; - - $can_talk = 0; + my ($userid, $username, $fullname, $highlight, $vis_a_vi) = highlight ($msg); unless ($myid) { my $myObj = Teapot::Bot::Brain::getMe ($self); @@ -102,92 +95,25 @@ sub __on_msg { $chatname = 'Noname chat'; } } - - # because of bot api restriction there is no events about changing permissions - # in chat sent to bot so before sending any answer, we should check if we can do so - - # N.B. there is situation when we do not recieve any messages from chat: if we - # are in exception list, even if it allows any kind of events for bot - - # If we are just member of chat, it looks like we obey general chat restrictions, - # so we need only to query getChat in this case - # If we admin, we can get|send messages even if group by itself disallows it. - - # works only for public chats, so id shoud be < 0. - if ($chatid < 0) { - my $chatobj = Teapot::Bot::Brain::getChat ($self, { 'chat_id' => $chatid }); - - unless ($chatobj) { - sleep 3; - $chatobj = Teapot::Bot::Brain::getChat ($self, { 'chat_id' => $chatid }); - } - - # actually evaluates to 1 for true and to 0 for false - $group_talk = int ($chatobj->{permissions}->{can_send_messages}); - my $me = Teapot::Bot::Brain::getChatMember ($self, { 'chat_id' => $chatid, 'user_id' => $myid }); - - unless ($me) { - sleep 3; - $me = Teapot::Bot::Brain::getChatMember ($self, { 'chat_id' => $chatid, 'user_id' => $myid }); - } - - if ($me->{'status'} eq 'administrator') { - carp "[DEBUG] I can talk in group chat $chatname ($chatid)" if $c->{debug}; - $can_talk = 1; - } else { - if ($c->{debug}) { - carp "[DEBUG] I can talk in group chat $chatname ($chatid)" if ($group_talk); - carp "[DEBUG] I can not talk in group chat $chatname ($chatid)" unless ($group_talk); - } - - $can_talk = $group_talk; - } - } else { - # private chat, so definely bot can talk - carp "[DEBUG] I can talk in private chat $chatname ($chatid)" if $c->{debug}; - $can_talk = 1; - } } else { carp '[INFO] Unable to get chatid'; } - # according api user must have username and/or first_name and/or last_name at least one of these fields - if ($msg->can ('from') && defined ($msg->from)) { - $userid = $msg->from->id; # it must be filled, so do not check it - $username = $msg->from->username if ($msg->from->can ('username') && defined($msg->from->username)); - - if ($msg->from->can ('first_name') && defined ($msg->from->first_name)) { - $fullname = $msg->from->first_name; - - if ($msg->from->can ('last_name') && defined ($msg->from->last_name)) { - $fullname .= ' ' . $msg->from->last_name; - } - } elsif ($msg->from->can ('last_name') && defined ($msg->from->last_name)) { - $fullname .= $msg->from->last_name; - } - - $vis_a_vi = visavi ($userid, $username, $fullname); - } - my $phrase = ''; # Newcommer event, greet our new member and suggest to introduce themself. if ($msg->can ('new_chat_members') && defined ($msg->new_chat_members)) { - my $send_args; - $send_args->{parse_mode} = 'Markdown'; - $send_args->{chat_id} = $chatid; - if (defined ($username)) { if (defined ($fullname)) { carp "[DEBUG] Newcommer in $chatname ($chatid): \@$username, $fullname ($userid)" if $c->{debug}; - $send_args->{text} = "Дратути, [$fullname](tg://user?id=$userid). Представьтес, пожалуйста, и расскажите, что вас сюда привело."; + $phrase = "Дратути, [$fullname](tg://user?id=$userid). Представьтес, пожалуйста, и расскажите, что вас сюда привело."; } else { carp "[DEBUG] Newcommer in $chatname ($chatid): \@$username ($userid)" if $c->{debug}; - $send_args->{text} = "Дратути, [$username](tg://user?id=$userid). Представьтес, пожалуйста, и расскажите, что вас сюда привело."; + $phrase = "Дратути, [$username](tg://user?id=$userid). Представьтес, пожалуйста, и расскажите, что вас сюда привело."; } } else { carp "[DEBUG] Newcommer in $chatname ($chatid): $fullname ($userid)" if $c->{debug}; - $send_args->{text} = "Дратути, [$fullname](tg://user?id=$userid). Представьтес, пожалуйста, и расскажите, что вас сюда привело."; + $phrase = "Дратути, [$fullname](tg://user?id=$userid). Представьтес, пожалуйста, и расскажите, что вас сюда привело."; } # let's emulate real human and delay answer @@ -201,7 +127,7 @@ sub __on_msg { sleep ( 3 + int ( rand (2))); - Teapot::Bot::Brain::sendMessage ($self, $send_args); + $msg->replyMd ($phrase); return; } @@ -234,8 +160,7 @@ sub __on_msg { if (substr ($text, 0, 1) eq $c->{telegrambot}->{csign}) { if (substr ($text, 1) eq 'help' || substr ($text, 1) eq 'помощь') { - my $send_args; - $send_args->{text} = << 'MYHELP'; + $reply = << 'MYHELP'; ``` !help | !помощь - список команд !dig | !копать - заняться археологией @@ -256,9 +181,7 @@ sub __on_msg { Но на самом деле я бот больше для общения, чем для исполнения команд. Поговоришь со мной? MYHELP - $send_args->{parse_mode} = 'Markdown'; - $send_args->{chat_id} = $userid; - Teapot::Bot::Brain::sendMessage ($self, $send_args); + $msg->replyMd ($reply); return; } else { $reply = command ($self, $msg, $text, $userid); @@ -281,7 +204,6 @@ MYHELP if (defined ($str) && $str ne '') { $reply = $str; - # TODO: move it to telegramlib.pm/botlib.pm! $phrase = trim ($text); while ($phrase =~ /[\.|\,|\?|\!]$/) { @@ -368,8 +290,7 @@ MYHELP # TODO: Log commands and answers } elsif (substr ($text, 0, 1) eq $c->{telegrambot}->{csign}) { if (substr ($text, 1) eq 'help' || substr ($text, 1) eq 'помощь') { - my $send_args; - $send_args->{text} = << 'MYHELP'; + $reply = << 'MYHELP'; ``` !help | !помощь - список команд !dig | !копать - заняться археологией @@ -390,9 +311,7 @@ MYHELP Но на самом деле я бот больше для общения, чем для исполнения команд. Поговоришь со мной? MYHELP - $send_args->{parse_mode} = 'Markdown'; - $send_args->{chat_id} = $chatid; - Teapot::Bot::Brain::sendMessage ($self, $send_args); + $msg->replyMd ($reply); return; } else { $reply = command ($self, $msg, $text, $chatid); @@ -432,7 +351,7 @@ MYHELP } } - if (defined ($reply) && $reply ne '' && $can_talk) { + if (defined ($reply) && $reply ne '') { # work a bit more on input phrase $phrase = trim ($phrase); @@ -447,7 +366,7 @@ MYHELP } elsif (lc ($reply) eq substr ($phrase, 0, -1)) { # in case of trailing dot $reply = randomCommonPhrase (); - } elsif (substr(lc ($reply), 0, -1) eq $phrase) { + } elsif (substr (lc ($reply), 0, -1) eq $phrase) { $reply = randomCommonPhrase (); } @@ -456,11 +375,7 @@ MYHELP carp sprintf ('[DEBUG] In public chat %s (%s) bot reply to %s: %s', $chatname, $chatid, $vis_a_vi, $reply) if $c->{debug}; $msg->reply ($reply); } else { - if ($can_talk) { - carp sprintf ('[DEBUG] In public chat %s (%s) bot is not required to reply to %s', $chatname, $chatid, $vis_a_vi) if $c->{debug}; - } else { - carp sprintf ('[DEBUG] In public chat %s (%s) bot can\'t talk, but reply to %s is: %s', $chatname, $chatid, $vis_a_vi, $reply) if $c->{debug}; - } + carp sprintf ('[DEBUG] In public chat %s (%s) bot is not required to reply to %s', $chatname, $chatid, $vis_a_vi) if $c->{debug}; } # should be channel, so we can't talk @@ -493,21 +408,6 @@ sub run_telegrambot { return; } -sub visavi { - my ($userid, $username, $fullname) = @_; - my $name = ''; - - if (defined ($username)) { - $name .= '@' . $username; - $name .= ', ' . $fullname if (defined ($fullname)); - } else { - $name .= $fullname; - } - - $name .= " ($userid)"; - return $name; -} - 1; # vim: ft=perl noet ai ts=4 sw=4 sts=4: