The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Bot::ChatBots::Telegram::Keyboard - Telegram Keyboard handler

SYNOPSIS

   use Bot::ChatBots::Telegram::Keyboard qw< keyboard >;
   # `keyboard` is a wrapper around constructor
   # Bot::ChatBots::Telegram::Keyboard->new()

   my $keyboard = keyboard(
      [ # first row of the keyboard
         {
            text => "Happyness",      # shown in the button
            _value => '/happyness', # substituted upon call
         },
         {
            text => "+1",                # shown in the button
            _value => '/happyness +1', # substituted upon call
         },
         {
            text => "+2",                # shown in the button
            _value => '/happyness +2', # substituted upon call
         },
         {
            text => "+3",                # shown in the button
            _value => '/happyness +3', # substituted upon call
         },
      ],
      [ # second row of the keyboard. Note that the second, third
        # and fourth button hold the same labels as in the first
        # row... which can be problematic because the label is
        # what Telegram clients send back when the button is hit
         {
            text => "Relax",      # shown in the button
            _value => '/relax', # substituted upon call
         },
         {
            text => "+1",            # shown in the button
            _value => '/relax +1',  # substituted upon call
         },
         {
            text => "+2",            # shown in the button
            _value => '/relax +2', # substituted upon call
         },
         {
            text => "+3",            # shown in the button
            _value => '/relax +3', # substituted upon call
         },
      ],
      [
         {
            text => 'Location',     # shown in the button
            request_location => \1, # flag for Telegram
         },
         {
            text => 'Help',      # shown in the button
            _value => '/help', # substituted upon call
         }
      ],
      # ...
   );

   # Suppose you have a Bot::ChatBots::Telegram::Sender now...
   $sender->send_message(
      text => "Here's your keyboard",
      chat_id => $chat_id, # whatever it is...
      reply_markup => {
         resize_keyboard => \1,
         keyboard => $keyboard->displayable,
      },
   );

   # You're setting the Bot::ChatBots::Telegram::WebHook and need a
   # processor...
   sub processor {
      my $record = shift;

      my $command = $keyboard->get_value($record);
      # ...

      return $record;
   }

DESCRIPTION

Telegram custom keyboards are a handy feature to provide a cleaner interface to your users. They're pretty basic: you set up the keyboard with some text on a few buttons (including emojis) and whenever the user pushes the button, that text is sent back to your bot.

While effective and amazing, this can prove stiff when compared to e.g. how HTML handles buttons. In HTML, you can independently set what the user sees and what's sent to the server (in the form of a value). This allows you to:

  • easily manage translations without changing the behavior of the button, and

  • have buttons with the same labels trigger different actions, e.g. based on where they are placed.

As an example of the latter case, consider a keyboard in which you want to track two different variables, Happyness and Relax. You want to provide a button for showing their current value, and some buttons to increase those values. It might be something like this:

   +-----------+-----------+-----------+-----------+
   | Happyness |    +1     |    +2     |    +3     |
   +-----------+-----------+-----------+-----------+
   |   Relax   |    +1     |    +2     |    +3     |
   +-----------+-----------+-----------+-----------+

It's clear from the context what the different +1, +2 and +3 mean, but they can't be shown like this because they will just send their label without providing any hint about the context.

This module defines a class to help you with this. It allows you to define a Telegram keyboard that allows tracking each button individually and associate a translation _value to it, so that when it is received you can get that _value back, and e.g. use it as if it had been written by the client in the first place.

The button tracking is accomplished by appending to the label of each keyboard button a unique sequence of zero-width Unicode characters. This allows having different labels for each of them, while at the same time ensuring that they look exactly the same when printed. Luckily, Telegram clients as of February 2017 send the whole sequence instead of just the visible, non-zero-width characters.

FUNCTIONS

To ease with the creation of keyboards, a helper function is available:

keyboard

   my $keyboard = keyboard(@keyboard_rows); # OR
      $keyboard = keyboard(\@keyboard_rows); # OR
      $keyboard = keyboard(id => $id, keyboard => \@rows);

This is just a wrapper around the constructor. A such, it can throw exeptions, see "DIAGNOSTICS" for the details.

ACCESSORS

displayable

   my $kbd_for_client = $obj->displayable;

Read-only accessor for a rendition of the keyboard that is suitable for sending to the client. Any button that originally contained a _value has this field removed (as it's not in Telegram's specification) and its label is changed with a suitable zero-width tracking code. As such, it can be used like this:

   $sender->send_message(
      text => "Here's your keyboard",
      chat_id => $chat_id, # whatever it is...
      reply_markup => {
         resize_keyboard => \1,
         keyboard => $obj->displayable,
      },
   );

METHODS

get_value

   my $text = $obj->get_value($record); # OR
      $text = $obj->get_value($record->{payload}); # OR
      $text = $obj->get_value($record->{payload}{text});

Get the command string from an input record (e.g. coming in a webhook). Returns the _value fields content in the keyboard definition if such a command tracker is found in the input, undef otherwise.

The input can be:

  • a record, i.e. something with a payload field inside. In this case, whatever is in the text subfield is taken;

  • a payload, in which case a text field inside is taken;

  • a plain string.

If the input is a hash reference but neither payload nor text are found, it is assumed to be a payload hash without a text and the text will be assumed to be undef. If you want to have 100% of control, always pass a plain string.

get_keyboard_id

   my $id = $obj_or_class->get_keyboard_id($record); # OR
      $id = $obj_or_class->get_keyboard_id($record->{payload}); # OR
      $id = $obj_or_class->get_keyboard_id($record->{payload}{text});

Get the keyboard identifier from an input record (e.g. coming from a webhook). Returns an unsigned integer value. You can set this value for a keyboard via option keyboard_id during construction, see "new".

The input can be the same as in "get_value". Note that this method can be used as either an instance or a class method, with the same result.

new

   my $kbd = Bot::ChatBots::Telegram::Keyboard->new(%args); # OR
      $kbd = Bot::ChatBots::Telegram::Keyboard->new(\%args);

Constructor. It accepts the following parameters:

keyboard

an array of arrays of hashes, just like a normal Telegram keyboard. As an extension, each keyboard button can additionally support a _value parameter, holding the real value to associate to the button (which can be retrieved via "get_value").

id

an unsigned integer to enable distinguishing different keyboards, its value can be retrieved via "get_keyboard_id" from a received record. Defaults to 0.

This method can throw exceptions if the input is not good, see "DIAGNOSTICS" for the details.

DIAGNOSTICS

The following exceptions can be thrown by this class via Ouch, all with code 500:

no input keyboard

the constructor was not provided the requird keyboard input parameter. See "new".

get_value(): pass either hash references or plain scalars

method "get_value" can accept either a plain scalar (used as text) or a hash reference where the text will be searched. Everything else leads to this exception.

invalid input keyboard, not an ARRAY
invalid input keyboard, not an AoA
invalid input keyboard, not an AoAoH

constructor "new" accepts a single input parameter keyboard that MUST be a reference to an array of arrays of hashes. The first exception is thrown if the input is not an array, the second if any item inside the outer array is not an array itself, the third if the items in each row are not hashes.

SEE ALSO

Bot::ChatBots::Telegram.

AUTHOR

Flavio Poletti <polettix@cpan.org>

COPYRIGHT AND LICENSE

Copyright (C) 2016, 2018 by Flavio Poletti <polettix@cpan.org>

This module is free software. You can redistribute it and/or modify it under the terms of the Artistic License 2.0.

This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose.