=pod

=encoding utf8

=head1 NAME

Bot::ChatBots::Telegram::WebHook - Telegram updates receiver, web hook

=head1 SYNOPSIS

   use Log::Any qw< $log >;
   use Log::Any::Adapter;
   use Mojolicious::Lite;
   Log::Any::Adapter->set('Stderr');
   plugin 'Bot::ChatBots::Telegram' => instances => [
      [
         'WebHook',
         processor  => \&processor,
         register   => 1,
         token      => $ENV{TOKEN},
         unregister => 0,
         url        => 'https://example.com:8443/mybot',
      ],
      # more can follow here...
   ];
   app->start;
   sub processor { # tube-compliant
      my $record = shift; # ... $record is normalized, by default

      # ... your business logic goes here...
      my $message = 'Howdy!';

      # In a WebHook you're actually processing a HTTP Request from
      # Telegram, so you can set a response via Mojolicious
      if (automatic_via_https_response()) { # WebHook only
         $record->{response} = $message;
      }
      elsif (do_it_yourself_via_https_response()) { # WebHook only
         $record->{source}{refs}{controller}->render(text => $message);
         $record->{source}{flags}{rendered} = 1; # if Bot::ChatBot <= 0.006
      }
      elsif (automatic_via_sender()) { # same as LongPoll
         $record->{send_response} = $message;
      }
      elsif (do_it_yourself_via_sender()) { # same as LongPoll
         my $sender = $record->{source}{refs}{sender};
         $sender->send_response($message, record => $record);
      }
      # else nothing is sent back, just a HTTP 204 by default

      return $record; # follow on..
   }

=head1 DESCRIPTION

This is an updates receiver and dispatcher for the Telegram
infrastructure. It connects to Telegram's API for I<webhooks> style (i.e.
waiting for push messages to arrive), so you need a routable address/port
to get the ball rolling.

Strictly speaking, you MUST only provide a L</processor> and either
L</url> or L</path>. The latter is used to set a POST route, while the
former is what is called whenever a new message arrives through that
route.

Most probably you will also want to automatically register your webhook at
Telegram. In this case, you MUST also provide a L</token>. You can also
set automatic registration (and unregistration) when creating the object.

When invoked, the L</processor> tube can set a response through either of
the following keys:

=over

=item C<response>

send the response as the HTTP Response to the HTTP Request that determined
the Update. This is only available for WebHooks (LongPoll does not receive
HTTP requests because it... polls), and has the further characteristic of
not receiving a response itself (because it's already a response). Note
that this parameter is ignored if you generate the HTTP response yourself
(via the standard Mojolicious way of doing this);

=item C<send_response>

send the response using L</sender>. In this it behaves exactly as the
L<Bot::ChatBots::Telegram::LongPoll> correspondent.

=back

You can also set both of them: they are independent mechanisms and as long
as you understand how they work there is no reason why you shouldn't. The
following diagram will hopefully help you with this.


 .                            ..Bot Application.....
                              :                    :
   __________________         :     ____________   :
  /                  \        :    /            \  :
  |                  |-1---------->|            |  :
  |  Telegram Server |        :    |  WebHook   |  :
  |                  |<----------2-|            |  :
  \__________________/   "response"\____________/  :
              |  ^            :        |           :
              5  |            :        3 "send_response"
              |  |            :        |           :
              |  |            :     ___v______     :
              |  |            :    /          \    :
              |  +---------------4-|          |    :
              |               :    |  Sender  |    :
              +------------------->|          |    :
                              :    \__________/    :
                              :                    :
                              :....................:

   1, 2: WebHook Request/Response
   3   : internal call
   4, 5: Telegram API Request/Response



=head1 ACCESSORS

This class consumes the following roles, inheriting their accessors:

=over

=item *

L<Bot::ChatBots::Telegram::Role::Source>:

=over

=item *

L<Bot::ChatBots::Telegram::Role::Source/sender>:

=item *

L<Bot::ChatBots::Telegram::Role::Source/token>:

=back

=item *

L<Bot::ChatBots::Role::WebHook>

=over

=item *

L<Bot::ChatBots::Role::WebHook/app>

=item *

L<Bot::ChatBots::Role::WebHook/code>

=item *

L<Bot::ChatBots::Role::WebHook/method>

=item *

L<Bot::ChatBots::Role::WebHook/path>

=item *

L<Bot::ChatBots::Role::WebHook/url>

=back

=back

and also adds its own, as described in the sections below. It is worth
noticing the presence of L<Bot::ChatBots::Telegram::Role::Source/sender>,
which gives you back a L<Bot::ChatBots::Telegram::Sender> object suitable
for sending messages to the Telegram API; the same object is used
internally to send responses to Update requests when C<send_response> is
specified.

=head2 B<< auto_register >>

   my $boolean = $obj->auto_register;

get the value of the automatic registration flag, which can be set upon
construction via option C<register>. When set, the L</register> method
will be called automatically upon construction.

B<NOTE>: this parameters is initialized via the C<register> option in the
constructor L</new>.

=head2 B<< auto_unregister >>

   my $boolean = $obj->auto_unregister;

get the value of the automatic unregistration flag, which can be set upon
construction via option C<unregister>. When set, the L</unregister> method
will be called automatically upon destruction.

Note that some PaaS environment (notably Dokku) might keep the "old"
instance around while the new one is already up and running. In this case,
it is suggested to keep this boolean value to its default false value and
avoid unwanted unregistrations.

B<NOTE>: this parameters is initialized via the C<unregister> option in
the constructor L</new>.

=head2 B<< certificate >>

   $obj->certificate($certificate);
   my $current = $obj->certificate;

set/get the currently installed certificate.

It can be either a string or a hash with the key C<certificate> pointing
to that string, see L<WWW::Telegram::BotAPI> for additional details, or
Telegram's documentation.

=head1 METHODS

This class consumes the following roles, inheriting their methods:

=over

=item *

L<Bot::ChatBots::Telegram::Role::Source>

=item *

L<Bot::ChatBots::Role::WebHook>

=back

The following sections describe the addiitonal ones.

=head2 B<< BUILD >>

This method is called upon object construction. It takes care to call
L</install_route> from L<Bot::ChatBots::Role::WebHook> so that you don't
have to. Additionally, if L</auto_register> is true, L</register> is
called automatically.

=head2 B<< DEMOLISH >>

This method is called upon object destruction. It takes care to call
L</unregister> if L</auto_unregister> is set to a true value.

=head2 B<< new >>

   my $obj = Bot::ChatBots::Telegram::WebHook->new(%args);

   # mostly used in Mojolicious applications
   plugin 'Bot::ChatBots::Telegram' => instances => [ [ 'WebHook', %args ] ];

Create a new instance. Mostly used behind the scenes by
L<Bot::ChatBots::MojoPlugin> as in the second example above.

The arguments hash C<%hash> supports the following keys:

=over

=item C<***>

all keys from L<Bot::ChatBots::Telegram::Role::Source> and
L<Bot::ChatBots::Role::WebHook>

=item C<certificate>

in case a privately generated certificate has to be used, it can be set
here or passed to the L</register> method. You don't need to set this if
your certificate is publicly available and trusted (i.e. released by
a recognised Certification Authority).

=item C<register>

if true, L</register> is called upon construction, without parameters.

=item C<unregister>

if true, L</unregister> is called upon destruction. Beware of setting this
automation though, especially if your bot is installed through a platform
as a service like Dokku, because this might interphere with your upgrade
process.

=back

=head2 B<< parse_request >>

   my @updates = $obj->parse_request($req);

Parse the request and extract updates from it. The current Telegram
WebHook API sends one single update per message. Returns the update.

=head2 B<< process >>

   my $outcome = $obj->process($record);

Process a record. The main workhorse is inherited from
L<Bot::ChatBots::Role::WebHook>, although this wraps around it to add
automatic sending of a response in case:

=over

=item *

C<$outcome> is a hash reference with a C<response> key inside, AND

=item *

C<< $record->{source}{flags}{rendered} >> is false (i.e. the response
hasn't been rendered yet). See
L<Bot::ChatBots::Role::WebHook/process_updates> for more information about
these flags.

=back

When a response is rendered, C<< $record->{source}{flags}{rendered} >> is
set to C<1> (a I<true> value) to signal that rendering already happened
(this should prevent the calling function from doing the rendering again).

=head2 B<< register >>

   $obj->register(%args);
   $obj->register(\%args);

Register the C<url> (possibly derived from C<path> if needed) as the
webhook in Telegram. Note that both C<url> and C<path> are inherited
methods; you can override them in C<%args>. Same goes for C<token>.

If you plan to use an auto-generated certificate, you will need to provide
it either setting L</certificate> (possibly upon construction in L</new>)
or via a parameter in C<%args>.

Supported keys in C<%args>, all overriding values in the object when
applicable:

=over

=item C<app>

application object (defaults to value from inherited method C<app>)

=item C<certificate>

certificate in case it is auto-generated, defaults to value from object
(see L</certificate> and L</new>).

=item C<controller>

controller object (defaults to value from inherited method C<app>)

=item C<path>

path value or object (defaults to value from inherited method C<app>).
Used in case parameter C<url> below is not present or possibly derived
from the object's C<url> method;

=item C<token>

token value (defaults to value from inherited method C<app>)

=item C<url>

webhook target URL value or object (defaults to value from inherited
method C<app>)

=back

=head2 B<< token >>

See L<Bot::ChatBots::Telegram::Base/token>.


=head2 B<< unregister >>

   $obj->unregister;

De-register webhook from Telegram (after which you can restart using
long-polling, for example).

=head1 SEE ALSO

L<Bot::ChatBots>, L<Bot::ChatBots::Role::WebHook>.

=head1 AUTHOR

Flavio Poletti <polettix@cpan.org>

=head1 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.

=cut
