Bot-ChatBots-Telegram

 view release on metacpan or  search on metacpan

lib/Bot/ChatBots/Telegram/Guide/Tutorial.pod  view on Meta::CPAN


Telegram will not insist on this, anyway, and it is also possible to
generate a self-signed certificate and then hand it over to Telegram. On
the other hand, it I<will> insist on the certificate and the domain or IP
address of your endpoint to match, so you will have to generate your
certificate depending on it, e.g. using the following command:

   $ openssl req -x509  -nodes \
      -newkey rsa:2048 \
      -sha256 \
      -days 365 \
      -subj "/C=IT/ST=Roma/L=Roma/O=Pinco Pals/CN=$BOT_IP" \
      -keyout server.key \
      -out server.crt

Hence, C<server.key> and C<server.crt> will be hanging around in the
directory you are using, keep this in mind!

You can fiddle with the C<-subj> part of course, as long as you make sure
that the C<CN> part matches the IP address or domain name you chose for
your bot endpoint.

=head3 Decide a path and set C<BOT_PATH>

This is really up to you. If you are expanding a previous program, using
a reverse proxy, or just want to go deeper than the root path, you can
just do so:

   $ export BOT_PATH='...'

You can also decide to leave this part empty.

=head3 Wrap it all together

I find it useful to put all environment variables in a single
configuration file that can be C<source>d in a shell:

   # we will ignore BOT_PROTO and just use https
   export BOT_ADDRESS='...'   # Public IP or public domain address
   export BOT_PORT='...'      # 80, 88, 443, 8443
   export BOT_PATH='/'        # or leave it empty
   export BOT_URL="https://$BOT_ADDRESS:$BOT_PORT$BOT_PATH"


=head2 The Program

Our program will be a full-fledged L<Mojolicious> application this time,
although this does not mean it will be much more complicated. After the
previous section, we know there are two additional files in the directory,
namely the certificate file C<server.crt> and the private key file
C<server.key>.

   #!/usr/bin/env perl

   use strict;
   use warnings;
   use Mojolicious::Lite;

   my $token   = $ENV{TOKEN};
   my $bot_url = $ENV{BOT_URL};
   my $certificate = do { local (@ARGV, $/) = 'server.crt'; <> };

   plugin 'Bot::ChatBots::Telegram' => instances => [
      [
         'WebHook',
         processor   => \&process_record,
         register    => 1,
         token       => $token,
         unregister  => 1,
         url         => $bot_url,
         certificate => $certificate,
      ],
   ];

   # set this as a "shim" to make the whole thing similar to LongPoll
   my $bcb = app->chatbots->telegram->instances->[0];

   # encapsulating initialization comes handy
   setup_recurring();

   # now it's time to hand operations over to Mojolicious
   app->start;

   ### EVERYTHING IS UNCHANGED BELOW THIS LINE ############################
   # track nagging...
   {
      my %nagged;
      sub setup_recurring {
         Mojo::IOLoop->recurring(
            10 => sub {
               my $sender = $bcb->sender;
               for my $chat_id (keys %nagged) {
                  $sender->send_message(
                     {
                        text => 'whooops!',
                        chat_id => $chat_id,
                     }
                  );
               }
            }
         );
      }
      sub nag_on  { $nagged{$_[0]} = 1 }
      sub nag_off { delete $nagged{$_[0]} }
   }

   sub process_record {
      my $record = shift;

      my $type    = $record->{data_type};
      my $payload = $record->{payload};
      if ($type eq 'Message' && exists($payload->{from}) ) {
         my $text      = $payload->{text} || '';
         my $peer_name = $payload->{from}{first_name} || 'U. N. Known';
         my $chat_id   = $record->{channel}{id};
         print {*STDERR} "$peer_name says: $text\n";
         if (($text eq '/start') || ($text eq '/help')) {
            $record->{send_response} = <<'END';
   Very simple:
   * for help type /help
   * for greeting type /hello
   * for being annoyed every 10 s type /nag on
   * to stop annoyance type /nag off
   * to be reminded type /remind <seconds> <message>
   END
         }
         elsif ($text eq '/hello') {
            $record->{send_response} = "Hello to you, $peer_name";
         }
         elsif ($text eq '/nag on') {
            nag_on($chat_id);
            $record->{send_response} = 'OK, to deactivate /nag off';
         }
         elsif ($text eq '/nag off') {
            nag_off($chat_id);
            $record->{send_response} = 'OK, to reactivate /nag on';
         }
         elsif (my (          $delay,        $msg) = $text =~ m{
               \A /remind \s+ ([1-9]\d*) \s+ (.*)
            }mxs)
         {
            Mojo::IOLoop->timer($delay => sub {
               $bcb->sender->send_message(
                  {
                     text => "$peer_name: remember $msg",
                     chat_id => $chat_id,
                  }
               );
            });
            $record->{send_response} = "I'll try my best!";
         }
      }

      return $record; # follow on..
   }

As you will notice, only the first part changed with respect to the
proactive version of the longpoll bot (there is a comment line indicating
where differences end). This is the new part:

   #!/usr/bin/env perl

   use strict;
   use warnings;
   use Mojolicious::Lite;

   my $token   = $ENV{TOKEN};
   my $bot_url = $ENV{BOT_URL};
   my $certificate = do { local (@ARGV, $/) = 'server.crt'; <> };

   plugin 'Bot::ChatBots::Telegram' => instances => [
      [
         'WebHook',
         processor   => \&process_record,
         register    => 1,
         token       => $token,
         unregister  => 1,
         url         => $bot_url,
         certificate => $certificate,
      ],
   ];

   # set this as a "shim" to make the whole thing similar to LongPoll
   my $bcb = app->chatbots->telegram->instances->[0];

   # encapsulating initialization comes handy
   setup_recurring();

   # now it's time to hand operations over to Mojolicious
   app->start;

A few comments:

=over

=item *

as anticipated, it will be a full-fledged L<Mojolicious> application, but
it needs not be a complicated one. L<Mojolicious::Lite> will do fine;

=item *

in addition to C<TOKEN> we now have to be aware of environment variable
C<BOT_URL> and load the TLS certificate file C<server.crt>, so that it can
be communicated to Telegram (this is needed only for self-signed
certificates);

=item *

not surprisingly, we are using L<Bot::ChatBots::Telegram::WebHook> instead
of L<Bot::ChatBots::Telegram::LongPoll>. It is a L<Mojolicious> plugin, so
we load it as such with the interface shown in the example;

=item *

our code from the longpoll days was relying upon a variable C<$bcb> to get
access to a L<Bot::ChatBots::Telegram::Sender> object. Again, this is not
really necessary, because you only need C<TOKEN> to get yours, but we are
defining this variable anyway to show the ease of transition from the
longpoll version to the webhook;

=item *

the call to C<setup_recurring()> is the same as before, which gives you
why it's handy to encapsulate these configurations in one place;

=item *

last, we C<start> the application instead of the poller.



( run in 0.536 second using v1.01-cache-2.11-cpan-5a3173703d6 )