# Name

WWW::Telegram::BotAPI - Perl implementation of the Telegram Bot API

# Synopsis

```perl
use WWW::Telegram::BotAPI;
my $api = WWW::Telegram::BotAPI->new (
    token => 'my_token'
);
# The API methods die when an error occurs.
say $api->getMe->{result}{username};
# ... but error handling is available as well.
my $result = eval { $api->getMe }
    or die 'Got error message: ', $api->parse_error->{msg};
# Uploading files is easier than ever.
$api->sendPhoto ({
    chat_id => 123456,
    photo   => {
        file => '/home/me/cool_pic.png'
    },
    caption => 'Look at my cool photo!'
});
# Complex objects are as easy as writing a Perl object.
$api->sendMessage ({
    chat_id      => 123456,
    # Object: ReplyKeyboardMarkup
    reply_markup => {
        resize_keyboard => \1, # \1 = true when JSONified, \0 = false
        keyboard => [
            # Keyboard: row 1
            [
                # Keyboard: button 1
                'Hello world!',
                # Keyboard: button 2
                {
                    text => 'Give me your phone number!',
                    request_contact => \1
                }
            ]
        ]
    }
});
# Asynchronous request are supported with Mojo::UserAgent.
$api = WWW::Telegram::BotAPI->new (
    token => 'my_token',
    async => 1 # WARNING: may fail if Mojo::UserAgent is not available!
);
$api->sendMessage ({
    chat_id => 123456,
    text    => 'Hello world!'
}, sub {
    my ($ua, $tx) = @_;
    die 'Something bad happened!' if $tx->error;
    say $tx->res->json->{ok} ? 'YAY!' : ':('; # Not production ready!
});
Mojo::IOLoop->start;
```

# Description

This module provides an easy to use interface for the
[Telegram Bot API](https://core.telegram.org/bots/api). It also supports async requests out of the
box using [Mojo::UserAgent](https://metacpan.org/pod/Mojo%3A%3AUserAgent), which makes this module easy to integrate with an existing
[Mojolicious](https://metacpan.org/pod/Mojolicious) application.

# Methods

[WWW::Telegram::BotAPI](https://metacpan.org/pod/WWW%3A%3ATelegram%3A%3ABotAPI) implements the following methods.

## new

```perl
my $api = WWW::Telegram::BotAPI->new (%options);
```

Creates a new [WWW::Telegram::BotAPI](https://metacpan.org/pod/WWW%3A%3ATelegram%3A%3ABotAPI) instance.

**WARNING:** you should only create one instance of this module and reuse it when needed. Calling
`new` each time you run an async request causes unexpected behavior with [Mojo::UserAgent](https://metacpan.org/pod/Mojo%3A%3AUserAgent) and
won't work correctly. See also
[issue #13 on GitHub](https://github.com/Robertof/perl-www-telegram-botapi/issues/13).

`%options` may contain the following:

- `token => 'my_token'`

    The token that will be used to authenticate the bot.

    **This is required! The method will croak if this option is not specified.**

- `api_url => 'https://api.example.com/token/%s/method/%s'`

    A format string that will be used to create the final API URL. The first parameter specifies
    the token, the second one specifies the method.

    Defaults to `https://api.telegram.org/bot%s/%s`.

- `async => 1`

    Enables asynchronous requests.

    **This requires [Mojo::UserAgent](https://metacpan.org/pod/Mojo%3A%3AUserAgent), and the method will croak if it isn't found.**

    Defaults to `0`.

- `force_lwp => 1`

    Forces the usage of [LWP::UserAgent](https://metacpan.org/pod/LWP%3A%3AUserAgent) instead of [Mojo::UserAgent](https://metacpan.org/pod/Mojo%3A%3AUserAgent), even if the latter is
    available.

    By default, the module tries to load [Mojo::UserAgent](https://metacpan.org/pod/Mojo%3A%3AUserAgent), and on failure it uses [LWP::UserAgent](https://metacpan.org/pod/LWP%3A%3AUserAgent).

## AUTOLOAD

```perl
$api->getMe;
$api->sendMessage ({
    chat_id => 123456,
    text    => 'Hello world!'
});
# with async => 1 and the IOLoop already started
$api->setWebhook ({ url => 'https://example.com/webhook' }, sub {
    my ($ua, $tx) = @_;
    die if $tx->error;
    say 'Webhook set!'
});
```

This module makes use of ["Autoloading" in perlsub](https://metacpan.org/pod/perlsub#Autoloading). This means that **every current and future
method of the Telegram Bot API can be used by calling its Perl equivalent**, without requiring an
update of the module.

If you'd like to avoid using `AUTOLOAD`, then you may simply call the ["api\_request"](#api_request) method
specifying the method name as the first argument.

```perl
$api->api_request ('getMe');
```

This is, by the way, the exact thing the `AUTOLOAD` method of this module does.

## api\_request

```perl
# Remember: each of these samples can be aliased with
# $api->methodName ($params).
$api->api_request ('getMe');
$api->api_request ('sendMessage', {
    chat_id => 123456,
    text    => 'Oh, hai'
});
# file upload
$api->api_request ('sendDocument', {
    chat_id  => 123456,
    document => {
        filename => 'dump.txt',
        content  => 'secret stuff'
    }
});
# complex objects are supported natively since v0.04
$api->api_request ('sendMessage', {
    chat_id      => 123456,
    reply_markup => {
        keyboard => [ [ 'Button 1', 'Button 2' ] ]
    }
});
# with async => 1 and the IOLoop already started
$api->api_request ('getMe', sub {
    my ($ua, $tx) = @_;
    die if $tx->error;
    # ...
});
```

This method performs an API request. The first argument must be the method name
([here's a list](https://core.telegram.org/bots/api#available-methods)).

Once the request is completed, the response is decoded using [JSON::MaybeXS](https://metacpan.org/pod/JSON%3A%3AMaybeXS) and then
returned. If [Mojo::UserAgent](https://metacpan.org/pod/Mojo%3A%3AUserAgent) is used as the user-agent, then the response is decoded
automatically using [Mojo::JSON](https://metacpan.org/pod/Mojo%3A%3AJSON).

If the request is not successful or the server tells us something isn't `ok`, then this method
dies with the first available error message (either the error description or the status line).
You can make this method non-fatal using `eval`:

```perl
my $response = eval { $api->api_request ($method, $args) }
    or warn "Request failed with error '$@', but I'm still alive!";
```

Further processing of error messages can be obtained using ["parse\_error"](#parse_error).

Request parameters can be specified using an hash reference. Additionally, complex objects can be
specified like you do in JSON. See the previous examples or the example bot provided in
["SEE ALSO"](#see-also).

File uploads can be specified using an hash reference containing the following mappings:

- `file => '/path/to/file.ext'`

    Path to the file you want to upload.

    Required only if `content` is not specified.

- `filename => 'file_name.ext'`

    An optional filename that will be used instead of the real name of the file.

    Particularly recommended when `content` is specified.

- `content => 'Being a file is cool :-)'`

    The content of the file to send. When using this, `file` must not be specified.

- `AnyCustom => 'Header'`

    Custom headers can be specified as hash mappings.

Upload of multiple files is not supported. See ["tx" in Mojo::UserAgent::Transactor](https://metacpan.org/pod/Mojo%3A%3AUserAgent%3A%3ATransactor#tx) for more
information about file uploads.

To resend files, you don't need to perform a file upload at all. Just pass the ID as a normal
parameter.

```perl
$api->sendPhoto ({
    chat_id => 123456,
    photo   => $photo_id
});
```

When asynchronous requests are enabled, a callback can be specified as an argument.
The arguments passed to the callback are, in order, the user-agent (a [Mojo::UserAgent](https://metacpan.org/pod/Mojo%3A%3AUserAgent) object)
and the response (a [Mojo::Transaction::HTTP](https://metacpan.org/pod/Mojo%3A%3ATransaction%3A%3AHTTP) object). More information can be found in the
documentation of [Mojo::UserAgent](https://metacpan.org/pod/Mojo%3A%3AUserAgent) and [Mojo::Transaction::HTTP](https://metacpan.org/pod/Mojo%3A%3ATransaction%3A%3AHTTP).

**NOTE:** ensure that the event loop [Mojo::IOLoop](https://metacpan.org/pod/Mojo%3A%3AIOLoop) is started when using asynchronous requests.
This is not needed when using this module inside a [Mojolicious](https://metacpan.org/pod/Mojolicious) app.

The order of the arguments, except of the first one, does not matter:

```perl
$api->api_request ('sendMessage', $parameters, $callback);
$api->api_request ('sendMessage', $callback, $parameters); # same thing!
```

## parse\_error

```perl
unless (eval { $api->doSomething(...) }) {
    my $error = $api->parse_error;
    die "Unknown error: $error->{msg}" if $error->{type} eq 'unknown';
    # Handle error gracefully using "type", "msg" and "code" (optional)
}
# Or, use it with a custom error message.
my $error = $api->parse_error ($message);
```

When sandboxing calls to [WWW::Telegram::BotAPI](https://metacpan.org/pod/WWW%3A%3ATelegram%3A%3ABotAPI) methods using `eval`, it is useful to parse
error messages using this method.

**WARNING:** up until version 0.09, this method incorrectly stopped at the first occurence of `at`
in error messages, producing results such as `missing ch` instead of `missing chat`.

This method accepts an error message as its first argument, otherwise `$@` is used.

An hash reference containing the following elements is returned:

- `type => unknown|agent|api`

    The source of the error.

    `api` specifies an error originating from Telegram's BotAPI. When `type` is `api`, the key
    `code` is guaranteed to exist.

    `agent` specifies an error originating from this module's user-agent. This may indicate a network
    issue, a non-200 HTTP response code or any error not related to the API.

    `unknown` specifies an error with no known source.

- `msg => ...`

    The error message.

- `code => ...`

    The error code. **This key only exists when `type` is `api`**.

## agent

```perl
my $user_agent = $api->agent;
```

Returns the instance of the user-agent used by the module. You can determine if the module is using
[LWP::UserAgent](https://metacpan.org/pod/LWP%3A%3AUserAgent) or [Mojo::UserAgent](https://metacpan.org/pod/Mojo%3A%3AUserAgent) by using `isa`:

```perl
my $is_lwp = $user_agent->isa ('LWP::UserAgent');
```

### Using a proxy

Since all the painful networking stuff is delegated to one of the two supported user agents
(either [LWP::UserAgent](https://metacpan.org/pod/LWP%3A%3AUserAgent) or [Mojo::UserAgent](https://metacpan.org/pod/Mojo%3A%3AUserAgent)), you can use their built-in support for proxies
by accessing the user agent object. An example of how this may look like is the following:

```perl
my $user_agent = $api->agent;
if ($user_agent->isa ('LWP::UserAgent')) {
  # Use LWP::Protocol::connect (for https)
  $user_agent->proxy ('https', '...');
  # Or if you prefer, load proxy settings from the environment.
  # $user_agent->env_proxy;
} else {
  # Mojo::UserAgent (builtin)
  $user_agent->proxy->https ('...');
  # Or if you prefer, load proxy settings from the environment.
  # $user_agent->detect;
}
```

**NOTE:** Unfortunately, [Mojo::UserAgent](https://metacpan.org/pod/Mojo%3A%3AUserAgent) returns an opaque `Proxy connection failed` when
something goes wrong with the `CONNECT` request made to the proxy. To alleviate this, since
version 0.12, this module prints the real reason of failure in debug mode. See ["DEBUGGING"](#debugging).
If you need to access the real error reason in your code, please see
[issue #29 on GitHub](https://github.com/Robertof/perl-www-telegram-botapi/issues/29).

# Debugging

To perform some cool troubleshooting, you can set the environment variable `TELEGRAM_BOTAPI_DEBUG`
to a true value:

```perl
TELEGRAM_BOTAPI_DEBUG=1 perl script.pl
```

This dumps the content of each request and response in a friendly, human-readable way.
It also prints the version and the configuration of the module. As a security measure, the bot's
token is automatically removed from the output of the dump.

Since version 0.12, enabling this flag also gives more details when a proxy connection fails.

**WARNING:** using this option along with an old Mojolicious version (< 6.22) leads to a warning,
and forces [LWP::UserAgent](https://metacpan.org/pod/LWP%3A%3AUserAgent) instead of [Mojo::UserAgent](https://metacpan.org/pod/Mojo%3A%3AUserAgent). This is because [Mojo::JSON](https://metacpan.org/pod/Mojo%3A%3AJSON)
used incompatible boolean values up to version 6.21, which led to an horrible death of
[JSON::MaybeXS](https://metacpan.org/pod/JSON%3A%3AMaybeXS) when serializing the data.

# Caveats

When asynchronous mode is enabled, no error handling is performed. You have to do it by
yourself as shown in the ["SYNOPSIS"](#synopsis).

# See also

[LWP::UserAgent](https://metacpan.org/pod/LWP%3A%3AUserAgent), [Mojo::UserAgent](https://metacpan.org/pod/Mojo%3A%3AUserAgent),
[https://core.telegram.org/bots/api](https://core.telegram.org/bots/api), [https://core.telegram.org/bots](https://core.telegram.org/bots),
[example implementation of a Telegram bot](https://git.io/vlOK0),
[example implementation of an async Telegram bot](https://git.io/vDrwL)

# Author

Roberto Frenna (robertof AT cpan DOT org)

# Bugs

Please report any bugs or feature requests to
[https://github.com/Robertof/perl-www-telegram-botapi](https://github.com/Robertof/perl-www-telegram-botapi).

# Thanks

Thanks to [the authors of Mojolicious](https://metacpan.org/pod/Mojolicious) for inspiration about the license and the
documentation.

# License

Copyright (C) 2015, Roberto Frenna.

This program is free software, you can redistribute it and/or modify it under the terms of the
Artistic License version 2.0.