Last active
February 17, 2023 01:31
-
-
Save Robertof/d9358b80d95850e2eb34 to your computer and use it in GitHub Desktop.
Basic Telegram bot implementation using WWW::Telegram::BotAPI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env perl | |
# Basic Telegram Bot implementation using WWW::Telegram::BotAPI | |
use strict; | |
use warnings; | |
use WWW::Telegram::BotAPI; | |
use utf8; | |
my $api = WWW::Telegram::BotAPI->new ( | |
token => (shift or die "ERROR: a token is required!\n") | |
); | |
# Bump up the timeout when Mojo::UserAgent is used (LWP::UserAgent uses 180s by default) | |
$api->agent->can ("inactivity_timeout") and $api->agent->inactivity_timeout (45); | |
my $me = $api->getMe or die; | |
my ($offset, $updates) = 0; | |
# The commands that this bot supports. | |
my $pic_id; # file_id of the last sent picture | |
my $commands = { | |
# Example demonstrating the use of parameters in a command. | |
"say" => sub { join " ", splice @_, 1 or "Usage: /say something" }, | |
# Example showing how to use the result of an API call. | |
"whoami" => sub { | |
sprintf "Hello %s, I am %s! How are you?", shift->{from}{username}, $me->{result}{username} | |
}, | |
# Example showing how to send multiple lines in a single message. | |
"knock" => sub { | |
sprintf "Knock-knock.\n- Who's there?\n@%s!", $me->{result}{username} | |
}, | |
# Example displaying a keyboard with some simple options. | |
"keyboard" => sub { | |
+{ | |
text => "Here's a cool keyboard.", | |
reply_markup => { | |
keyboard => [ [ "a" .. "c" ], [ "d" .. "f" ], [ "g" .. "i" ] ], | |
one_time_keyboard => \1 # \1 maps to "true" when being JSON-ified | |
} | |
} | |
}, | |
# Let me identify yourself by sending your phone number to me. | |
"phone" => sub { | |
+{ | |
text => "Would you allow me to get your phone number please?", | |
reply_markup => { | |
keyboard => [ | |
[ | |
{ | |
text => "Sure!", | |
request_contact => \1 | |
}, | |
"No, go away!" | |
] | |
], | |
one_time_keyboard => \1 | |
} | |
} | |
}, | |
# Test UTF-8 | |
"encoding" => sub { "Привет! こんにちは! Buondì!" }, | |
# Example sending a photo with a known picture ID. | |
"lastphoto" => sub { | |
return "You didn't send any picture!" unless $pic_id; | |
+{ | |
method => "sendPhoto", | |
photo => $pic_id, | |
caption => "Here it is!" | |
} | |
}, | |
"_unknown" => "Unknown command :( Try /start" | |
}; | |
# Generate the command list dynamically. | |
$commands->{start} = "Hello! Try /" . join " - /", grep !/^_/, keys %$commands; | |
# Special message type handling | |
my $message_types = { | |
# Save the picture ID to use it in `lastphoto`. | |
"photo" => sub { $pic_id = shift->{photo}[0]{file_id} }, | |
# Receive contacts! | |
"contact" => sub { | |
my $contact = shift->{contact}; | |
+{ | |
method => "sendMessage", | |
parse_mode => "Markdown", | |
text => sprintf ( | |
"Here's some information about this contact.\n" . | |
"- Name: *%s*\n- Surname: *%s*\n" . | |
"- Phone number: *%s*\n- Telegram UID: *%s*", | |
$contact->{first_name}, $contact->{last_name} || "?", | |
$contact->{phone_number}, $contact->{user_id} || "?" | |
) | |
} | |
} | |
}; | |
printf "Hello! I am %s. Starting...\n", $me->{result}{username}; | |
while (1) { | |
$updates = $api->getUpdates ({ | |
timeout => 30, # Use long polling | |
$offset ? (offset => $offset) : () | |
}); | |
unless ($updates and ref $updates eq "HASH" and $updates->{ok}) { | |
warn "WARNING: getUpdates returned a false value - trying again..."; | |
next; | |
} | |
for my $u (@{$updates->{result}}) { | |
$offset = $u->{update_id} + 1 if $u->{update_id} >= $offset; | |
if (my $text = $u->{message}{text}) { # Text message | |
printf "Incoming text message from \@%s\n", $u->{message}{from}{username}; | |
printf "Text: %s\n", $text; | |
next if $text !~ m!^/[^_].!; # Not a command | |
my ($cmd, @params) = split / /, $text; | |
my $res = $commands->{substr ($cmd, 1)} || $commands->{_unknown}; | |
# Pass to the subroutine the message object, and the parameters passed to the cmd. | |
$res = $res->($u->{message}, @params) if ref $res eq "CODE"; | |
next unless $res; | |
my $method = ref $res && $res->{method} ? delete $res->{method} : "sendMessage"; | |
$api->$method ({ | |
chat_id => $u->{message}{chat}{id}, | |
ref $res ? %$res : ( text => $res ) | |
}); | |
print "Reply sent.\n"; | |
} | |
# Handle other message types. | |
for my $type (keys %{$u->{message} || {}}) { | |
next unless exists $message_types->{$type} and | |
ref (my $res = $message_types->{$type}->($u->{message})); | |
my $method = delete ($res->{method}) || "sendMessage"; | |
$api->$method ({ | |
chat_id => $u->{message}{chat}{id}, | |
%$res | |
}) | |
} | |
} | |
} |
How would it go with the asynchronous interface?
Well, its very interesting for me too :)
I was just re-reading this gist when I found your comments (for some reason, GitHub didn't notify me!).
I created a (basic) example asynchronous bot too, available at this link.
Also if anyone has further questions, feel free to shoot me up an email at robertof <AT> cpan <DOT> org
.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
How would it go with the asynchronous interface?