EV-Telegram-TDLib
view release on metacpan or search on metacpan
lib/EV/Telegram/TDLib/Bots.pm view on Meta::CPAN
package EV::Telegram::TDLib::Bots;
use strict;
use warnings;
use Carp qw(croak);
use MIME::Base64 ();
our $VERSION = '0.04';
=head1 NAME
EV::Telegram::TDLib::Bots - bot methods for EV::Telegram::TDLib
=head1 DESCRIPTION
One of the mixins L<EV::Telegram::TDLib> inherits from. It has no
interface of its own and is not meant to be used directly: loading the
main module loads this one, and its methods are called on a client.
They are documented together with the rest of the API, under
L<EV::Telegram::TDLib/"Bots mixin">.
=cut
sub CLONE_SKIP { 1 }
our %UPDATES = (
updateNewCallbackQuery => \&update_callback_query,
updateNewInlineCallbackQuery => \&update_callback_query,
updateNewBusinessCallbackQuery => \&update_callback_query,
updateNewInlineQuery => \&update_inline_query,
);
# TL `bytes` travels base64-encoded over the JSON interface, so callback
# payloads are decoded on the way in and encoded on the way out; a caller
# that never sees the encoding cannot get it wrong
# the query is decoded before the on_callback_query guard, so a bot that only
# registers on_callback_data still receives one
sub update_callback_query {
my ($self, $obj) = @_;
my $payload = $obj->{payload} // {};
my %q = (
id => $obj->{id},
sender_user_id => $obj->{sender_user_id},
chat_id => $obj->{chat_id},
message_id => $obj->{message_id},
type => $payload->{'@type'},
);
# a press on a message sent through inline mode has no chat: it names
# the message by inline_message_id instead
$q{inline_message_id} = $obj->{inline_message_id}
if defined $obj->{inline_message_id};
# one on a business message carries the message itself, and the
# connection it came through
if (ref $obj->{message} eq 'HASH' && ref $obj->{message}{message} eq 'HASH') {
@q{qw(chat_id message_id)} = @{ $obj->{message}{message} }{qw(chat_id id)};
$q{connection_id} = $obj->{connection_id};
}
$q{data} = MIME::Base64::decode_base64($payload->{data})
if defined $payload->{data};
$q{game_short_name} = $payload->{game_short_name}
if defined $payload->{game_short_name};
$self->route_callback_data(\%q);
my $cb = $self->{on_callback_query} or return;
$self->guarded($cb, \%q);
}
sub on_callback_query {
my ($self, $cb) = @_;
$self->{on_callback_query} = $cb if @_ > 1;
return $self->{on_callback_query};
}
sub answer_callback_query {
my ($self, @args) = @_;
my $cb = ref $args[-1] eq 'CODE' ? pop @args : sub {};
my ($id, @rest) = @args;
my %opt = opts(@rest);
need('callback_query_id', $id);
# int64 crosses the JSON interface as a string; a number would lose
# precision on ids above 2^53
$self->send({
'@type' => 'answerCallbackQuery',
callback_query_id => plain_text('a callback query id', $id),
text => plain_text('an answer text', $opt{text}),
show_alert => json_bool($opt{show_alert}),
url => plain_text('a url', $opt{url}),
cache_time => num('cache_time', $opt{cache_time} // 0),
}, $cb);
return;
}
# rows: [ [ { text => 'Yes', data => 'yes' }, { text => 'Docs', url => '...' } ] ]
sub inline_keyboard {
my ($self, $rows) = @_;
croak 'inline_keyboard needs an arrayref of rows'
unless ref $rows eq 'ARRAY';
my @out;
for my $row (@$rows) {
lib/EV/Telegram/TDLib/Bots.pm view on Meta::CPAN
croak 'set_bot_name needs a name; pass the empty string to clear it'
unless @args;
my ($name, @rest) = @args;
my %opt = opts(@rest);
$self->send({
'@type' => 'setBotName',
bot_user_id => $self->bot_id(\%opt),
language_code => plain_text('a language code', $opt{language_code}),
name => plain_text('a bot name', $name),
}, $cb);
return;
}
sub set_bot_description {
my ($self, @args) = @_;
my $cb = ref $args[-1] eq 'CODE' ? pop @args : sub {};
croak 'set_bot_description needs a description; pass the empty string '
. 'to clear it' unless @args;
my ($text, @rest) = @args;
my %opt = opts(@rest);
# the long text, shown on the bot's empty chat screen
$self->send({
'@type' => 'setBotInfoDescription',
bot_user_id => $self->bot_id(\%opt),
language_code => plain_text('a language code', $opt{language_code}),
description => plain_text('a description', $text),
}, $cb);
return;
}
sub set_bot_short_description {
my ($self, @args) = @_;
my $cb = ref $args[-1] eq 'CODE' ? pop @args : sub {};
croak 'set_bot_short_description needs a description; pass the empty '
. 'string to clear it' unless @args;
my ($text, @rest) = @args;
my %opt = opts(@rest);
# the one-liner shown in the bot's profile and in search results
$self->send({
'@type' => 'setBotInfoShortDescription',
bot_user_id => $self->bot_id(\%opt),
language_code => plain_text('a language code', $opt{language_code}),
short_description => plain_text('a description', $text),
}, $cb);
return;
}
sub set_bot_photo {
my ($self, @args) = @_;
my $cb = ref $args[-1] eq 'CODE' ? pop @args : sub {};
my ($path, @rest) = @args;
my %opt = opts(@rest);
$self->send({
'@type' => 'setBotProfilePhoto',
bot_user_id => $self->bot_id(\%opt),
photo => $self->input_chat_photo($path, \%opt),
}, $cb);
return;
}
sub update_inline_query {
my ($self, $obj) = @_;
my $cb = $self->{on_inline_query} or return;
$self->guarded($cb, {
id => $obj->{id},
sender_user_id => $obj->{sender_user_id},
query => $obj->{query},
offset => $obj->{offset},
chat_type => ($obj->{chat_type} || {})->{'@type'},
user_location => $obj->{user_location},
});
}
sub on_inline_query {
my ($self, $cb) = @_;
$self->{on_inline_query} = $cb if @_ > 1;
return $self->{on_inline_query};
}
# results: [ { id => '1', title => 'A', message => 'sent when picked' }, ... ]
sub answer_inline_query {
my ($self, @args) = @_;
my $cb = ref $args[-1] eq 'CODE' ? pop @args : sub {};
my ($id, $results, @rest) = @args;
my %opt = opts(@rest);
need('inline_query_id', $id);
croak 'answer_inline_query needs an arrayref of results'
unless ref $results eq 'ARRAY';
my $n = 0;
my @out;
my $bad;
for my $r (@$results) {
croak 'each inline result must be a hashref' unless ref $r eq 'HASH';
croak 'each result needs a title' unless defined $r->{title};
my $text = defined $r->{message} ? $r->{message} : $r->{title};
push @out, {
'@type' => 'inputInlineQueryResultArticle',
id => defined $r->{id}
? plain_text('a result id', $r->{id})
: "" . ++$n,
title => plain_text('a result title', $r->{title}),
description => plain_text('a result description', $r->{description}),
url => plain_text('a url', $r->{url}),
thumbnail_url => defined $r->{thumbnail_url}
? plain_text('a thumbnail url',
$r->{thumbnail_url}) : '',
thumbnail_width => num('thumbnail_width', $r->{thumbnail_width} // 0),
thumbnail_height => num('thumbnail_height', $r->{thumbnail_height} // 0),
input_message_content => {
'@type' => 'inputMessageText',
text => do {
my $t = $self->format_text($text,
$r->{parse_mode} // $opt{parse_mode});
# a parse failure must reach the caller, not TDLib
$bad ||= $t if ($t->{'@type'} // '') eq 'error';
$t;
},
},
($r->{reply_markup} ? (reply_markup => $r->{reply_markup}) : ()),
};
}
( run in 0.485 second using v1.01-cache-2.11-cpan-57ff5998125 )