Bot-ChatBots-Telegram

 view release on metacpan or  search on metacpan

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

   # now it's time to hand operations over to Mojo::IOLoop
   $bcb->start;

   # 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..
   }


The program is a bit more complicated than before, but it does so much
more! Again, some notes:

=over

=item *

we are setting C<start> to C<0> when creating the object, instead of C<1>
as before. This will allow us to avoid starting the loop right on the
spot, define additional things (in our case, encapsulated inside
C<setup_recurring>) and then C<< $bcb->start >> the loop.

=item *

As anticipated, L<Mojo::IOLoop> is the real workhorse behind this, so we
can take advantage of its capabilities. Before C<start>ing, we set up
a recurrent job that will send a nagging message every 10 seconds to all
channels that ask for it (tracked through variable C<%nagged>).

=item *

The C<process_record> didn't change shape, just got a bit longer. The help
evolved to explain all the new commands, some of which are quite simple
(e.g. the nagging handling ones just set or reset a flag in C<%nagged>),
other again take advantage of L<Mojo::IOLoop> to do something (like the
new command C</remind>).

=item *

In both callbacks passed to L<Mojo::IOLoop> we use
a L<Bot::ChatBots::Telegram::Sender> object, relying upon the same object
that C<$bcb> uses to get new updates. As already anticipated, nothing

=item *

In all cases we send a quick feedback to the user, relying upon
C<send_message> as before. This will use C<$brb>'s internal
L<Bot::ChatBots::Telegram::Sender> instance to invoke the Telegram API;
stops you from creating another object using the same I<token>.

=back

=head2 Example Session

Start the new bot and try it:

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

   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 *



( run in 1.228 second using v1.01-cache-2.11-cpan-f56aa216473 )