LINE-Bot-API

 view release on metacpan or  search on metacpan

lib/LINE/Bot/API.pm  view on Meta::CPAN

package LINE::Bot::API;
use strict;
use warnings;
our $VERSION = '1.21';

use LINE::Bot::API::Builder::SendMessage;
use LINE::Bot::API::Client;
use LINE::Bot::API::Event;
use LINE::Bot::API::Response::Common;
use LINE::Bot::API::Response::Content;
use LINE::Bot::API::Response::Count;
use LINE::Bot::API::Response::FriendDemographics;
use LINE::Bot::API::Response::NumberOfSentMessages;
use LINE::Bot::API::Response::NumberOfMessageDeliveries;
use LINE::Bot::API::Response::Profile;
use LINE::Bot::API::Response::GroupMemberProfile;
use LINE::Bot::API::Response::GroupSummary;
use LINE::Bot::API::Response::RoomMemberProfile;
use LINE::Bot::API::Response::IssueLinkToken;
use LINE::Bot::API::Response::RichMenu;
use LINE::Bot::API::Response::RichMenuList;
use LINE::Bot::API::Response::TargetLimit;
use LINE::Bot::API::Response::TotalUsage;
use LINE::Bot::API::Response::Token;
use LINE::Bot::API::Response::NumberOfFollowers;
use LINE::Bot::API::Response::UserInteractionStatistics;
use LINE::Bot::API::Response::BotInfo;
use LINE::Bot::API::Response::WebhookInformation;
use LINE::Bot::API::Response::WebhookTest;
use LINE::Bot::API::Response::Followers;

use constant {
    DEFAULT_MESSAGING_API_ENDPOINT => 'https://api.line.me/v2/bot/',
    DEFAULT_SOCIAL_API_ENDPOINT    => 'https://api.line.me/v2/oauth/',
    DEFAULT_CONTENT_API_ENDPOINT   => 'https://api-data.line.me/v2/bot/',
    DEFAULT_OAUTH2_API_ENDPOINT    => 'https://api.line.me/oauth2/v2.1/',
};
use Furl;
use Carp 'croak';
use URI;
use URI::Escape;
use URI::QueryParam;

sub new {
    my($class, %args) = @_;

    my $client = LINE::Bot::API::Client->new(%args);

    bless {
        client               => $client,
        channel_secret       => $args{channel_secret},
        channel_access_token => $args{channel_access_token},
        messaging_api_endpoint => $args{messaging_api_endpoint} // DEFAULT_MESSAGING_API_ENDPOINT,
        social_api_endpoint    => $args{social_api_endpoint} // DEFAULT_SOCIAL_API_ENDPOINT,
        content_api_endpoint => $args{content_api_endpoint} // DEFAULT_CONTENT_API_ENDPOINT,
        oauth_api_endpoint => $args{oauth_api_endpoint} // DEFAULT_OAUTH2_API_ENDPOINT,
    }, $class;
}

sub request {
    my ($self, $method, $path, @payload) = @_;

    return $self->{client}->$method(
        $self->{messaging_api_endpoint} .  $path,
        @payload,
    );
}

sub request_content {
    my ($self, $method, $path, @payload) = @_;

    return $self->{client}->$method(
        $self->{content_api_endpoint} .  $path,
        @payload,
    );
}

sub reply_message {
    my($self, $reply_token, $messages) = @_;

    my $res = $self->request(
        post => 'message/reply',
        +{
            replyToken => $reply_token,
            messages   => $messages,
        }
    );

    LINE::Bot::API::Response::Common->new(%{ $res });
}

sub push_message {
    my($self, $to_id, $messages, $options) = @_;

    my @headers = ();
    if ($options && defined($options->{'retry_key'})) {
        push @headers, 'X-Line-Retry-Key' => $options->{'retry_key'};
    }

    my $res = $self->request(
        post => 'message/push',
        \@headers,
        +{
            to       => $to_id,
            messages => $messages,
        }
    );
    LINE::Bot::API::Response::Common->new(%{ $res });
}

sub multicast {
    my($self, $to_ids, $messages, $options) = @_;

    my @headers = ();
    if ($options && defined($options->{'retry_key'})) {
        push @headers, 'X-Line-Retry-Key' => $options->{'retry_key'};
    }

    my $res = $self->request(
        post => 'message/multicast',
        \@headers,
        +{
            to       => $to_ids,
            messages => $messages,
        }
    );
    LINE::Bot::API::Response::Common->new(%{ $res });
}

sub broadcast {
    my($self, $messages, $options) = @_;

    my @headers = ();

lib/LINE/Bot/API.pm  view on Meta::CPAN

    my ($self, $rich_menu_id) = @_;
    my $res = $self->request(post => "user/all/richmenu/${rich_menu_id}", +{});
    LINE::Bot::API::Response::RichMenu->new(%{ $res });
}

sub get_default_rich_menu_id {
    my ($self) = @_;
    my $res = $self->request(get => "user/all/richmenu");
    LINE::Bot::API::Response::RichMenu->new(%{ $res });
}

sub cancel_default_rich_menu {
    my ($self) = @_;
    my $res = $self->request(delete => "user/all/richmenu");
    LINE::Bot::API::Response::RichMenu->new(%{ $res });
}

sub link_rich_menu_to_user {
    my ($self, $user_id, $rich_menu_id) = @_;
    my $res = $self->request(post => "user/${user_id}/richmenu/${rich_menu_id}", +{});
    LINE::Bot::API::Response::RichMenu->new(%{ $res });
}

sub link_rich_menu_to_multiple_users {
    my ($self, $user_ids, $rich_menu_id) = @_;
    my $res = $self->request(post => "richmenu/bulk/link", +{
        richMenuId => $rich_menu_id,
        userIds => $user_ids,
    });
    LINE::Bot::API::Response::RichMenu->new(%{ $res });
}

sub get_rich_menu_id_of_user {
    my ($self, $user_id, $rich_menu_id) = @_;
    my $res = $self->request(get => "user/${user_id}/richmenu");
    LINE::Bot::API::Response::RichMenu->new(%{ $res });
}

sub unlink_rich_menu_from_user {
    my ($self, $user_id) = @_;
    my $res = $self->request(delete => "user/${user_id}/richmenu");
    LINE::Bot::API::Response::RichMenu->new(%{ $res });
}

sub unlink_rich_menu_from_multiple_users {
    my ($self, $user_ids) = @_;
    my $res = $self->request(post => "richmenu/bulk/unlink", +{
        userIds => $user_ids,
    });
    LINE::Bot::API::Response::RichMenu->new(%{ $res });
}

sub upload_rich_menu_image {
    my ($self, $rich_menu_id, $content_type, $file_path) = @_;

    if (!$content_type) {
        croak 'Need content_type';
    }

    my $res = $self->{client}->post_image(
        $self->{content_api_endpoint} . "richmenu/$rich_menu_id/content",
        [
            'Content-Type' => $content_type,
        ],
        $file_path
    );

    if ($res->{http_status} eq '200') {
        return LINE::Bot::API::Response::Token->new(%{ $res });
    } else {
        return LINE::Bot::API::Response::Error->new(%{ $res });
    }

}

sub download_rich_menu_image {
    my ($self, $rich_menu_id) = @_;

    return $self->{client}->get_content(
        $self->{content_api_endpoint} . "richmenu/$rich_menu_id/content"
    );
}

sub validate_rich_menu_object {
    my ($self, $rich_menu) = @_;
    my $res = $self->request(post => "richmenu/validate", $rich_menu);
    LINE::Bot::API::Response::Common->new(%{ $res });
}

sub issue_channel_access_token {
    my ($self, $opts) = @_;

    my $res = $self->{client}->post_form(
        $self->{social_api_endpoint} . 'accessToken',
        [
            grant_type    => 'client_credentials',
            client_id     => $opts->{client_id},
            client_secret => $opts->{client_secret},
        ]
    );

    if ($res->{http_status} eq '200') {
        return LINE::Bot::API::Response::Token->new(%{ $res });
    } else {
        return LINE::Bot::API::Response::Error->new(%{ $res });
    }
}

sub issue_channel_access_token_v2_1 {
    my ($self, $opts) = @_;

    my $res = $self->{client}->post_form(
        $self->{oauth_api_endpoint} . 'token',
        undef,
        [
            grant_type              => 'client_credentials',
            client_assertion_type   => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
            client_assertion        => $opts->{jwt},
        ]
    );

    if ($res->{http_status} eq '200') {
        return LINE::Bot::API::Response::Token->new(%{ $res });
    } else {
        return LINE::Bot::API::Response::Error->new(%{ $res });
    }
}

sub get_valid_channel_access_token_v2_1 {
    my ($self, $opts) = @_;

    my $jwt = uri_escape($opts->{jwt});
    my $assertion_type = uri_escape('urn:ietf:params:oauth:client-assertion-type:jwt-bearer');

    my $res = $self->{client}->get(
        $self->{oauth_api_endpoint} . 'tokens/kid' . "?client_assertion_type=$assertion_type&client_assertion=$jwt",
    );

    if ($res->{http_status} eq '200') {
        return LINE::Bot::API::Response::Token->new(%{ $res });
    } else {
        return LINE::Bot::API::Response::Error->new(%{ $res });
    }
}

sub revoke_channel_access_token {
    my ($self, $opts) = @_;

    my $res = $self->{client}->post_form(
        $self->{social_api_endpoint} . 'revoke',
        [
            access_token => $opts->{access_token},
        ]
    );

    if ($res->{http_status} eq '200') {
        return LINE::Bot::API::Response::Common->new(%{ $res });
    } else {
        return LINE::Bot::API::Response::Error->new(%{ $res });
    }
}

sub get_number_of_followers {
    my ($self, $opts) = @_;
    my $date = $opts->{date} or croak "get_number_of_followers: Missing a `date` parameter.";

    my $res = $self->request(get => "insight/followers?date=${date}");
    LINE::Bot::API::Response::NumberOfFollowers->new(%{ $res });
}

sub get_user_interaction_statistics {
    my ($self, $opts) = @_;
    my $requestId = $opts->{requestId} or croak "get_user_interaction_statistics: Missing a `requestId` parameter.";

    my $res = $self->request(get => "insight/message/event?requestId=${requestId}");
    LINE::Bot::API::Response::UserInteractionStatistics->new(%{ $res });
}

sub get_bot_info {
    my ($self) = @_;
    my $res = $self->request(get => "info");
    LINE::Bot::API::Response::BotInfo->new(%{ $res });
}

sub set_webhook_url {
    my ($self, $opts) = @_;
    defined($opts->{endpoint}) or croak "set_webhook_url: Missing a mandatory parameter: `endpoint`";

    my $res = $self->request(
        'put' => "channel/webhook/endpoint",
        [],
        +{ endpoint => $opts->{endpoint} },
    );

    if ($res->{http_status} eq '200') {
        return LINE::Bot::API::Response::Common->new(%{ $res });
    } else {
        return LINE::Bot::API::Response::Error->new(%{ $res });
    }
}

sub get_webhook_endpoint_information {
    my ($self) = @_;
    my $res = $self->request(get => "channel/webhook/endpoint");
    LINE::Bot::API::Response::WebhookInformation->new(%{ $res });
}

sub test_webhook_endpoint {
    my ($self, $opts) = @_;

    my $req_body = {};
    if ($opts->{'endpoint'}) {
        $req_body->{'endpoint'} = $opts->{'endpoint'};
    }

    my $res = $self->request(
        'post' => "channel/webhook/endpoint",
        [],
        $req_body,
    );

    LINE::Bot::API::Response::WebhookTest->new(%{ $res });
}

sub validate_reply_message_objects {
    my($self, $messages) = @_;

    my $res = $self->request(
        post => 'message/validate/reply',
        +{
            messages   => $messages,
        }
    );

    LINE::Bot::API::Response::Common->new(%{ $res });
}

sub validate_push_message_objects {
    my($self, $messages) = @_;

    my $res = $self->request(
        post => 'message/validate/push',
        +{
            messages   => $messages,
        }
    );

    LINE::Bot::API::Response::Common->new(%{ $res });
}

sub validate_multicast_message_objects {
    my($self, $messages) = @_;

    my $res = $self->request(
        post => 'message/validate/multicast',
        +{
            messages   => $messages,
        }
    );

    LINE::Bot::API::Response::Common->new(%{ $res });
}

sub validate_narrowcast_message_objects {
    my($self, $messages) = @_;

    my $res = $self->request(
        post => 'message/validate/narrowcast',
        +{
            messages   => $messages,
        }
    );

    LINE::Bot::API::Response::Common->new(%{ $res });
}

sub validate_broadcast_message_objects {

lib/LINE/Bot/API.pm  view on Meta::CPAN

=head2 C<< get_group_member_profile($group_id, $user_id) >>

Get group user profile information.

    my $ret = $bot->get_group_member_profile($group_id, $user_id);
    if ($ret->is_success) {
        say $ret->display_name;
        say $ret->user_id;
        say $ret->picture_url;
    }

See also the LINE Developers API reference of this method:  L<https://developers.line.biz/en/reference/messaging-api/#get-group-member-profile>

=head2 C<< get_member_in_room_count($room_id) >>

Gets the count of members in a room. You can get the member in room count even if the user hasn't added the LINE Official Account as a friend or has blocked the LINE Official Account.

    my $ret = $bot->get_member_in_room_count($room_id);
    if ($ret->is_success) {
        say $ret->count;
    }

See also the LINE Developers API reference of this method:  L<https://developers.line.biz/en/reference/messaging-api/#get-members-room-count>


=head2 C<< get_member_in_group_count($group_id) >>

Gets the count of members in a group. You can get the member in group count even if the user hasn't added the LINE Official Account as a friend or has blocked the LINE Official Account.

    my $ret = $bot->get_member_in_group_count($group_id);
    if ($ret->is_success) {
        say $ret->count;
    }

See also the LINE Developers API reference of this method:  L<https://developers.line.biz/en/reference/messaging-api/#get-members-group-count>


=head2 C<< get_group_summary($group_id) >>

Gets the group ID, group name, and group icon URL of a group where the LINE Official Account is a member.

    my $ret = $bot->get_group_summary($group_id);
    if ($ret->is_success) {
        say $ret->group_id;
        say $ret->group_name;
        say $ret->picture_url;
    }

See also the LINE Developers API reference of this method:  L<https://developers.line.biz/en/reference/messaging-api/#get-group-summary>

=head2 C<< get_room_member_profile($room_id, $user_id) >>

Get room user profile information.
A room is like a group without a group name.
The response is similar to get_group_member_profile.

See also the LINE Developers API reference of this method:  L<https://developers.line.biz/en/reference/messaging-api/#get-room-member-profile>

=head2 C<< get_number_of_sent_reply_messages($date) >>

Gets the number of messages sent with the C<< /bot/message/reply >> endpoint.

The number of messages retrieved by this operation does not include
the number of messages sent from LINE@ Manager.

The C<< $date >> parameter is "yyyyMMdd" format.

=head2 C<< get_number_of_sent_push_messages($date) >>

Gets the number of messages sent with the C<< /bot/message/push >> endpoint.

The number of messages retrieved by this operation does not include the number of messages sent from LINE@ Manager.

=over 4

=item date

Date the messages were sent

    Format: yyyyMMdd (Example: 20191231)
    Timezone: UTC+9

=back

=head2 C<< get_number_of_sent_multicast_messages($date) >>

Gets the number of messages sent with the C<< /bot/message/multicast >> endpoint.

The number of messages retrieved by this operation does not include the number of messages sent from LINE@ Manager.

=over 4

=item date

Date the messages were sent

    Format: yyyyMMdd (Example: 20191231)
    Timezone: UTC+9

=back

=head2 C<< get_number_of_send_broadcast_messages($date) >>

Gets the number of messages sent with the C<< /bot/message/broadcast >> endpoint.

The number of messages retrieved by this operation does not include the number of messages sent from LINE Official Account Manager.

=over 4

=item date

Date the messages were sent

    Format: yyyyMMdd (Example: 20191231)
    Timezone: UTC+9

=back

=head2 C<< create_rich_menu( $rich_menu_object ) >>

This method corresponds to the API of L<Creating rich menu|https://developers.line.biz/en/reference/messaging-api/#create-rich-menu>

One argument is needed: C<$rich_menu_object>, which is a plain HashRef representing L<rich menu object|https://developers.line.biz/en/reference/messaging-api/#rich-menu-object>

=head2 C<< get_rich_menu( $rich_menu_id ) >>

This method corresponds to the API of L<Get rich menu|https://developers.line.biz/en/reference/messaging-api/#get-rich-menu>

One argument is needed: $rich_menu_id -- which correspond to the
richMenuId property of the object returned by C<create_rich_menu>
method.

=head2 C<< delete_rich_menu( $rich_menu_id ) >>

This method corresponds to the API of L<Delete rich menu|https://developers.line.biz/en/reference/messaging-api/#delete-rich-menu>

One argument is needed: $rich_menu_id -- which correspond to the
richMenuId property of the object returned by C<create_rich_menu>
method.

The return value is an empty RichMenu object -- only status code
matters. Upon successful deletion, status code 200 is returned.

=head2 C<< get_rich_menu_list >>

This method corresponds to the API of L<Get rich menu list|https://developers.line.biz/en/reference/messaging-api/#get-rich-menu-list>

No arguments are needed.

=head2 C<< set_default_rich_menu( $rich_menu_id ) >>

This method corresponds to the API of L<Set default rich menu|https://developers.line.biz/en/reference/messaging-api/#set-default-rich-menu>

One argument is needed: $rich_menu_id -- which correspond to the
richMenuId property of the object returned by C<create_rich_menu>
method.

=head2 C<< get_default_rich_menu_id >>

This method corresponds to the API of L<Get default rich menu ID|https://developers.line.biz/en/reference/messaging-api/#get-default-rich-menu-id>

No arguments are needed. The return value is a RichMenu object with only one property: richMenuId.

=head2 C<< cancel_default_rich_menu >>

lib/LINE/Bot/API.pm  view on Meta::CPAN

Otherwise, you may examine the "error" attribute and "error_description" attribute for more information about the error.

=head2 C<< get_valid_channel_access_token_v2_1({ jwt => '...' }) >>

This method corresponds to the API of: L<Get all valid channel access token key IDs v2.1|https://developers.line.biz/en/reference/messaging-api/#get-all-valid-channel-access-token-key-ids-v2-1>

The argument is a HashRef with a pair of mandatory key-values:

    {
        jwt => "...",
    }

This method is for getting all valid channel access token key IDs.

When a 200 OK HTTP response is returned, a new token is issued. In this case, you may want to store the values in "key_ids" attributes of the response object for future use.

Otherwise, you may examine the "error" attribute and "error_description" attribute for more information about the error.

=head2 C<< revoke_channel_access_token({ access_token => "..." }) >>

This method corresponds to the API of: L<Revoke channel access token|https://developers.line.biz/en/reference/messaging-api/#revoke-channel-access-token>

The argument is a HashRef with one pair of mandatory key-values;

    { access_token => "..." }

Upon successful revocation, a 200 OK HTTP response is returned. Otherwise, you my examine the "error" attribute and "error_description" attribute for more information about the error.

=head2 C<< get_number_of_followers({ date => "..." }) >>

This method corresponds to the API of: L<Get number of followers|https://developers.line.biz/en/reference/messaging-api/#get-number-of-followers>

The argument is a HashRef with one pair of mandatory key-values;

    { date => "20191231" }

The formate of date is "yyyyMMdd", that is, year in 4 digits, month in
2 digits, and date-of-month in 2 digits.

Upon successful invocation, a 200 OK HTTP response is
returned. Otherwise, you my examine the "error" attribute and
"error_description" attribute for more information about the error.

The return value C<$res> is a response object with the following read-only accessors
(see the API documentation for the meaning of each.)

    $res->status();          #=> Str, one of: "ready", "unready", "out_of_service"
    $res->followers();       #=> Num
    $res->targetedReaches(); #=> Num
    $res->blocks();          #=> Num

Notice that the "status" does not mean HTTP status. To inspect actual
HTTP status, invoke C<$res->http_status()>.

=head2 C<< get_user_interaction_statistics({ requestId => "..." }) >>

Returns statistics about how users interact with narrowcast messages or broadcast messages sent from your LINE Official Account.

See also the LINE Developers API reference of this method: L<https://developers.line.biz/en/reference/messaging-api/#get-message-event>

=head2 C<< set_webhook_url({ 'endpoint' => "https://example.com/webhook" }) >>

Sets the webhook endpoint to te given C<endpoint>, which should be an URL string.

See also the LINE Developer API reference of this method: L<https://developers.line.biz/en/reference/messaging-api/#set-webhook-endpoint-url>

=head2 C<< get_webhook_endpoint_information() >>

Return the information about webhook endpoint as an response object with following accessors:

    $res = $api->get_webhook_endpoint_information();

    $res->endpoint(); # URL as a string
    $res->active();   # true or false

See also the LINE Developer API reference of this method: L<https://developers.line.biz/en/reference/messaging-api/#get-webhook-endpoint-information>

=head2 C<< test_webhook_endpoint({ 'endpoint' => "https://example.com/webhook" }) >>

Checks if the configured webhook endpoint can receive a test webhook event.

See also the LINE Developer API reference of this method: L<https://developers.line.biz/en/reference/messaging-api/#get-webhook-endpoint-information>

=head2 C<< validate_reply_message_objects([ $message, ... ] ) >>

Validates that an array of message objects is valid as a value for the messages property of the request body for the Send reply message endpoint.

See also the LINE Developer API reference of this method: L<https://developers.line.biz/en/reference/messaging-api/#validate-message-objects-of-reply-message>

=head2 C<< validate_push_message_objects([ $message, ... ] ) >>

Validates that an array of message objects is valid as a value for the messages property of the request body for the Send push message endpoint.

See also the LINE Developer API reference of this method: L<https://developers.line.biz/en/reference/messaging-api/#validate-message-objects-of-push-message>

=head2 C<< validate_multicast_message_objects([ $message, ... ] ) >>

Validates that an array of message objects is valid as a value for the messages property of the request body for the Send multicast message endpoint.

See also the LINE Developer API reference of this method: L<https://developers.line.biz/en/reference/messaging-api/#validate-message-objects-of-multicast-message>

=head2 C<< validate_narrowcast_message_objects([ $message, ... ] ) >>

Validates that an array of message objects is valid as a value for the messages property of the request body for the Send narrowcast message endpoint.

See also the LINE Developer API reference of this method: L<https://developers.line.biz/en/reference/messaging-api/#validate-message-objects-of-narrowcast-message>

=head2 C<< validate_broadcast_message_objects([ $message, ... ] ) >>

Validates that an array of message objects is valid as a value for the messages property of the request body for the Send broadcast message endpoint.

See also the LINE Developer API reference of this method: L<https://developers.line.biz/en/reference/messaging-api/#validate-message-objects-of-broadcast-message>

=head2 C<< get_followers({ 'limit' => 100, 'start' => "..." }) >>

Gets the list of User IDs of users who have added LINE Official Account as a friend.

See also the LINE Developer API reference of this method: L<https://developers.line.biz/en/reference/messaging-api/#get-follower-ids>

=head1 How to build a send message object

See the LINE Developers API reference about L<Message objects|https://developers.line.biz/en/reference/messaging-api/#message-objects>

When the C<LINE::Bot::API::Builder::SendMessage> class is used, it is possible easily to build a send message object.
That class supports a fluent interface.

    my $messages = LINE::Bot::API::Builder::SendMessage->new(
    )->add_text(
        text => 'Closing the distance',
    )->add_image(
        image_url   => 'http://example.com/image.jpg',
        preview_url => 'http://example.com/image_preview.jpg',
    );
    $bot->reply_message($reply_token, $messages->build);

=head2 Text type

Build a text type object.

    my $messages = LINE::Bot::API::Builder::SendMessage->new(
    )->add_text(
        text => 'Closing the distance',
    );
    $bot->reply_message($reply_token, $messages->build);

Build a text message with emojis inside:

    my $message = LINE::Bot::API::Builder::SendMessage->new();
    $message->add_text(
        text => '$ LINE Emoji $',
        emojis => [
            +{
                "index" => 0,
                "productId" => "5ac1bfd5040ab15980c9b435",
                "emojiId" => "001"
            },
            +{
                "index" => 13,
                "productId" => "5ac1bfd5040ab15980c9b435",
                "emojiId" => "002"
            }
        ]
    );

Since 2020/04/16, text messages may contain LINE emojis. They are identified by (productId, emojiId). For more details about possible values as well as how to use these emojis, please read: L<https://developers.line.biz/en/reference/messaging-api/#te...

=head2 Image type

Build an image type object.



( run in 0.462 second using v1.01-cache-2.11-cpan-524268b4103 )