Group
Extension

Perlgram/blib/lib/Perlgram.pm

package Perlgram;
use strict;
use warnings;
use HTTP::Tiny;
use JSON qw(encode_json decode_json);
use URI::Escape;
use Log::Log4perl qw(:easy);
use Perlgram::Error;

our $VERSION = '0.03';
our $AUTHORITY = 'cpan:AMIRCANDY'; # Replace with your CPAN ID

sub new {
    my ($class, %args) = @_;
    unless ($args{token}) {
        Perlgram::Error->new(message => "Token required")->throw;
    }
    my $self = {
        token      => $args{token},
        api_url    => $args{api_url} || 'https://api.telegram.org/bot',
        ua         => HTTP::Tiny->new(timeout => 30),
        logger     => Log::Log4perl->get_logger(__PACKAGE__),
        on_error   => $args{on_error}, # Optional error callback
    };
    bless $self, $class;
    $self->_init_logger unless Log::Log4perl::initialized();
    return $self;
}

sub _init_logger {
    Log::Log4perl->easy_init($Log::Log4perl::DEBUG);
}

# In Perlgram.pm, replace the api_request method with this debug version:
sub api_request {
    my ($self, $method, $params) = @_;
    my $url = $self->{api_url} . $self->{token} . "/$method";

    #print "DEBUG: Attempting to call $url\n";  # Add this debug line

    my $response;
    eval {
        if ($params && ref($params) eq 'HASH') {
            # For GET methods like getUpdates
            if ($method eq 'getUpdates') {
                my $query_string = join '&', map { 
                    uri_escape($_) . '=' . uri_escape($params->{$_}) 
                } keys %$params;
                $url .= '?' . $query_string;
                #print "DEBUG: GET URL: $url\n";  # Debug the final URL
                $response = $self->{ua}->get($url);
            } 
            # For POST methods
            else {
                my $content = encode_json($params);
                #print "DEBUG: POST Content: $content\n";  # Debug POST data
                $response = $self->{ua}->post(
                    $url,
                    {
                        headers => { 'Content-Type' => 'application/json' },
                        content => $content
                    }
                );
            }
        } else {
            #print "DEBUG: Simple GET: $url\n";  # Debug simple GET
            $response = $self->{ua}->get($url);
        }

    };

    if ($@) {
        print "ERROR: $@\n";
        return undef;
    }

    unless ($response->{success}) {
        print "HTTP ERROR: Status $response->{status}\n";
        print "Content: $response->{content}\n";
        return undef;
    }

    my $data = eval { decode_json($response->{content}) };
    if ($@) {
        print "JSON DECODE ERROR: $@\n";
        return undef;
    }

    return $data->{result};
}

# General Methods
sub getMe { shift->api_request('getMe'); }
sub logOut { shift->api_request('logOut'); }
sub close { shift->api_request('close'); }

# Update Methods
sub getUpdates { shift->api_request('getUpdates', {@_}); }
sub setWebhook { shift->api_request('setWebhook', {@_}); }
sub deleteWebhook { shift->api_request('deleteWebhook', {@_}); }
sub getWebhookInfo { shift->api_request('getWebhookInfo'); }

# Message Methods
sub sendMessage { shift->api_request('sendMessage', {@_}); }
sub forwardMessage { shift->api_request('forwardMessage', {@_}); }
sub copyMessage { shift->api_request('copyMessage', {@_}); }
sub sendPhoto { shift->api_request('sendPhoto', {@_}, 1); }
sub sendAudio { shift->api_request('sendAudio', {@_}, 1); }
sub sendDocument { shift->api_request('sendDocument', {@_}, 1); }
sub sendVideo { shift->api_request('sendVideo', {@_}, 1); }
sub sendAnimation { shift->api_request('sendAnimation', {@_}, 1); }
sub sendVoice { shift->api_request('sendVoice', {@_}, 1); }
sub sendVideoNote { shift->api_request('sendVideoNote', {@_}, 1); }
sub sendMediaGroup { shift->api_request('sendMediaGroup', {@_}); }
sub sendLocation { shift->api_request('sendLocation', {@_}); }
sub editMessageLiveLocation { shift->api_request('editMessageLiveLocation', {@_}); }
sub stopMessageLiveLocation { shift->api_request('stopMessageLiveLocation', {@_}); }
sub sendVenue { shift->api_request('sendVenue', {@_}); }
sub sendContact { shift->api_request('sendContact', {@_}); }
sub sendPoll { shift->api_request('sendPoll', {@_}); }
sub sendDice { shift->api_request('sendDice', {@_}); }
sub sendChatAction { shift->api_request('sendChatAction', {@_}); }

# Inline Mode
sub answerInlineQuery { shift->api_request('answerInlineQuery', {@_}); }
sub answerWebAppQuery { shift->api_request('answerWebAppQuery', {@_}); }

# Message Editing
sub editMessageText { shift->api_request('editMessageText', {@_}); }
sub editMessageCaption { shift->api_request('editMessageCaption', {@_}); }
sub editMessageMedia { shift->api_request('editMessageMedia', {@_}, 1); }
sub editMessageReplyMarkup { shift->api_request('editMessageReplyMarkup', {@_}); }
sub stopPoll { shift->api_request('stopPoll', {@_}); }
sub deleteMessage { shift->api_request('deleteMessage', {@_}); }

# Chat Management
sub banChatMember { shift->api_request('banChatMember', {@_}); }
sub unbanChatMember { shift->api_request('unbanChatMember', {@_}); }
sub restrictChatMember { shift->api_request('restrictChatMember', {@_}); }
sub promoteChatMember { shift->api_request('promoteChatMember', {@_}); }
sub setChatAdministratorCustomTitle { shift->api_request('setChatAdministratorCustomTitle', {@_}); }
sub banChatSenderChat { shift->api_request('banChatSenderChat', {@_}); }
sub unbanChatSenderChat { shift->api_request('unbanChatSenderChat', {@_}); }
sub setChatPermissions { shift->api_request('setChatPermissions', {@_}); }
sub exportChatInviteLink { shift->api_request('exportChatInviteLink', {@_}); }
sub createChatInviteLink { shift->api_request('createChatInviteLink', {@_}); }
sub editChatInviteLink { shift->api_request('editChatInviteLink', {@_}); }
sub revokeChatInviteLink { shift->api_request('revokeChatInviteLink', {@_}); }
sub approveChatJoinRequest { shift->api_request('approveChatJoinRequest', {@_}); }
sub declineChatJoinRequest { shift->api_request('declineChatJoinRequest', {@_}); }
sub setChatPhoto { shift->api_request('setChatPhoto', {@_}, 1); }
sub deleteChatPhoto { shift->api_request('deleteChatPhoto', {@_}); }
sub setChatTitle { shift->api_request('setChatTitle', {@_}); }
sub setChatDescription { shift->api_request('setChatDescription', {@_}); }
sub pinChatMessage { shift->api_request('pinChatMessage', {@_}); }
sub unpinChatMessage { shift->api_request('unpinChatMessage', {@_}); }
sub unpinAllChatMessages { shift->api_request('unpinAllChatMessages', {@_}); }
sub leaveChat { shift->api_request('leaveChat', {@_}); }
sub getChat { shift->api_request('getChat', {@_}); }
sub getChatAdministrators { shift->api_request('getChatAdministrators', {@_}); }
sub getChatMemberCount { shift->api_request('getChatMemberCount', {@_}); }
sub getChatMember { shift->api_request('getChatMember', {@_}); }
sub setChatStickerSet { shift->api_request('setChatStickerSet', {@_}); }
sub deleteChatStickerSet { shift->api_request('deleteChatStickerSet', {@_}); }

# Callback Queries
sub answerCallbackQuery { shift->api_request('answerCallbackQuery', {@_}); }

# Payments
sub sendInvoice { shift->api_request('sendInvoice', {@_}); }
sub createInvoiceLink { shift->api_request('createInvoiceLink', {@_}); }
sub answerShippingQuery { shift->api_request('answerShippingQuery', {@_}); }
sub answerPreCheckoutQuery { shift->api_request('answerPreCheckoutQuery', {@_}); }

# Stickers
sub sendSticker { shift->api_request('sendSticker', {@_}, 1); }
sub getStickerSet { shift->api_request('getStickerSet', {@_}); }
sub uploadStickerFile { shift->api_request('uploadStickerFile', {@_}, 1); }
sub createNewStickerSet { shift->api_request('createNewStickerSet', {@_}, 1); }
sub addStickerToSet { shift->api_request('addStickerToSet', {@_}, 1); }
sub setStickerPositionInSet { shift->api_request('setStickerPositionInSet', {@_}); }
sub deleteStickerFromSet { shift->api_request('deleteStickerFromSet', {@_}); }
sub setStickerSetThumb { shift->api_request('setStickerSetThumb', {@_}, 1); }

# Games
sub sendGame { shift->api_request('sendGame', {@_}); }
sub setGameScore { shift->api_request('setGameScore', {@_}); }
sub getGameHighScores { shift->api_request('getGameHighScores', {@_}); }

# Bot Commands
sub setMyCommands { shift->api_request('setMyCommands', {@_}); }
sub deleteMyCommands { shift->api_request('deleteMyCommands', {@_}); }
sub getMyCommands { shift->api_request('getMyCommands', {@_}); }
sub setChatMenuButton { shift->api_request('setChatMenuButton', {@_}); }
sub getChatMenuButton { shift->api_request('getChatMenuButton', {@_}); }
sub setMyDefaultAdministratorRights { shift->api_request('setMyDefaultAdministratorRights', {@_}); }
sub getMyDefaultAdministratorRights { shift->api_request('getMyDefaultAdministratorRights', {@_}); }

1;
__END__


=head1 NAME

Perlgram - Comprehensive Perl interface to the Telegram Bot API

=head1 VERSION

Version 0.02

=head1 SYNOPSIS

    use Perlgram;
    my $bot = Perlgram->new(token => 'YOUR_BOT_TOKEN');
    my $user = $bot->getMe();
    print "Bot username: $user->{username}\n";

    # Send a message with a reply keyboard
    $bot->sendMessage(
        chat_id => 123456789,
        text    => 'Choose an option',
        reply_markup => {
            keyboard => [[{ text => 'Option 1' }], [{ text => 'Option 2' }]],
            one_time_keyboard => JSON::true,
        },
    );

=head1 DESCRIPTION

C<Perlgram> is a Perl module for interacting with the Telegram Bot API. It supports all major API methods, including messaging, inline queries, payments, stickers, and games. The module can operate in webhook (real-time updates via HTTPS) or CLI (polling via C<getUpdates>) modes, making it suitable for both production and development environments.

This module is designed to be CPAN-compliant and can be installed via C<cpan> or C<cpanm>. It includes scripts for running bots and examples for quick setup.

=head1 METHODS

See the Telegram Bot API documentation (L<https://core.telegram.org/bots/api>) for parameter details.

=over

=item new(token => $token, [api_url => $url])

Creates a new bot instance. Requires a bot token from @BotFather.

=item getMe, logOut, close

General bot methods.

=item getUpdates, setWebhook, deleteWebhook, getWebhookInfo

Update-related methods.

=item sendMessage, sendPhoto, sendAudio, sendDocument, sendVideo, sendAnimation, sendVoice, sendVideoNote, sendMediaGroup, sendLocation, sendVenue, sendContact, sendPoll, sendDice, sendChatAction

Messaging methods.

=item answerInlineQuery, answerWebAppQuery

Inline mode methods.

=item editMessageText, editMessageCaption, editMessageMedia, editMessageReplyMarkup, stopPoll, deleteMessage

Message editing methods.

=item banChatMember, unbanChatMember, restrictChatMember, promoteChatMember, setChatPermissions, exportChatInviteLink, getChat, getChatAdministrators

Chat management methods.

=item answerCallbackQuery

Callback query handling.

=item sendInvoice, answerShippingQuery, answerPreCheckoutQuery

Payment methods.

=item sendSticker, getStickerSet, createNewStickerSet

Sticker methods.

=item sendGame, setGameScore, getGameHighScores

Game methods.

=item setMyCommands, getMyCommands

Bot command methods.

=back

=head1 DEPENDENCIES

=over

=item * LWP::UserAgent

=item * JSON

=item * Mojolicious (for webhook mode)

=item * Log::Log4perl

=back

=head1 INSTALLATION

To install via CPAN:

    cpan Perlgram

Or, clone from GitHub and build manually:

    git clone https://github.com/AmiRCandy/Perlgram.git
    cd Perlgram
    perl Makefile.PL
    make
    make test
    make install

=head1 AUTHOR

AmiRCandy, E<lt>amirhosen.1385.cmo@gmail.comE<gt>

=head1 BUGS

Please report bugs to the GitHub issue tracker: L<https://github.com/AmiRCandy/Perlgram/issues>

=head1 LICENSE

This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.

=head1 SEE ALSO

L<https://core.telegram.org/bots/api>, L<Perlgram::Webhook>, L<Perlgram::CLI>

Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.