Bot-Telegram

 view release on metacpan or  search on metacpan

lib/Bot/Telegram.pm  view on Meta::CPAN

  $self -> log -> warn("Polling failed (error type: $type): $message");
};

use constant DEFAULT_CALLBACK_ERROR_CB => sub {
  my ($self, undef, $err) = @_;
  $self -> log -> warn("Update processing failed: $err");
};

has [qw/api current_update polling_config/];
has [qw/_polling
        _polling_timer
        _polling_interval
        _polling_request_id/
    ];

has callbacks => sub { {} };
has ioloop => sub { Mojo::IOLoop -> new };
has log => sub { Mojo::Log -> new -> level('info') };

sub new {
  my $self = shift -> SUPER::new(@_);

lib/Bot/Telegram.pm  view on Meta::CPAN

  return $self unless $self -> is_polling;

  for (my $agent = $self -> api -> agent) {
    $self -> _polling(undef);

    # In synchronous mode, it's enough to simply clear state
    return $self -> _polling_interval(undef)
      unless $agent -> isa('Mojo::UserAgent')
      and $self -> is_async;

    # In asynchronous mode, we also need to cancel existing timers
    for (my $loop = Mojo::IOLoop -> singleton) {
      $loop -> remove($self -> _polling_request_id);
      $loop -> remove($self -> _polling_timer)
        if $self -> _polling_timer; # if another request is scheduled, cancel it
    }

    # Reset state
    $self -> _polling_request_id(undef)
          -> _polling_interval(undef)
          -> _polling_timer(undef);
  }

  $self
}

sub is_polling { !! shift -> _polling }

# In asynchronous mode: process getUpdates response or handle errors, if any
# In synchronous mode, WWW::Telegram::BotAPI::parse_error takes care of error handling for us.
sub _process_getUpdates_results {

lib/Bot/Telegram.pm  view on Meta::CPAN

      $self -> process_update($_)
            -> shift_offset
        for @$result;
    }
  }

  return unless $self -> is_polling;
  $self -> log -> trace('still polling, scheduling another iteration...');

  if ($async) {
    my $tid = Mojo::IOLoop -> timer(
      $retry_after // $self -> _polling_interval,
      sub { $self -> tap(sub { $self -> log -> trace("it's polling time!") })
                  -> _poll });

    $self -> _polling_timer($tid);
  } else {
    my $d = $retry_after // $self -> _polling_interval;

    # Sleep
    $self -> ioloop -> timer($d, sub { $self -> ioloop -> stop });
    $self -> ioloop -> start;
    $self -> log -> trace("it's polling time!");

    $self -> _poll;
  }
}

sub _poll {
  my $self = shift;

lib/Bot/Telegram.pm  view on Meta::CPAN

  { timeout => 20, offset => 0 }

=item restart

Set to true if the loop is already running, otherwise an exception will be thrown.

=item interval

Interval in seconds between polling requests.

Floating point values are accepted (timers are set using L<Mojo::IOLoop/"timer">).

Default value is 0.3 (300ms).

=back

=head3 Exceptions

=over 4

=item C<Bot::Telegram::X::InvalidStateError>

t/01-polling.t  view on Meta::CPAN


  $bot = $bot = Bot::Telegram -> new -> api($api);
  my $messages = $bot -> log -> capture('info');
  my $polling_interval_after_rate_limit;

  my $stop = sub {
    $bot -> stop_polling;
    Mojo::IOLoop -> stop;
  };

  my $t = Mojo::IOLoop -> timer(3, $stop);

  $bot -> set_callbacks(message => sub {
    return unless pop -> {update_id} == 2;

    $polling_interval_after_rate_limit = $bot -> _polling_interval;

    $stop -> ();
    Mojo::IOLoop -> remove($t);
  });

t/01-polling.t  view on Meta::CPAN


  $bot -> start_polling(interval => 0.1);
  Mojo::IOLoop -> start;

  my $time_after = steady_time;

  note explain \@$messages;
  my ($message) = grep { /Rate limit exceeded/ } @$messages;
  like $message, qr/Rate limit exceeded, waiting 1s before polling again/, 'rate limit log message exists';

  is $polling_interval_after_rate_limit, 0.1, 'polling interval was not affected by the rate limit timer';

  note "time_before: $time_before, time_after: $time_after";
  ok $time_after - $time_before >= 1, 'was on standby for (roughly) one second';
};

# config persistence
$bot = Bot::Telegram -> new -> api(bot_api random_valid_polling_response);

$bot -> start_polling;
my $config = $bot -> polling_config;

t/02-webhook.t  view on Meta::CPAN


  eval { $bot -> set_webhook };
  is ref($@), 'Bot::Telegram::X::InvalidArgumentsError', 'error type';
  is $@ -> message, 'No config provided', 'error message';
};

eval { $bot -> set_webhook({ url => 'http://localhost/' }) };
ok !$@, 'set w/o callback';

$bot -> set_webhook(sub { $set_f = 1 });
timer { ok $set_f, 'set w/callback' } 0.25;

Mojo::IOLoop -> start;
done_testing;

t/lib/Bot/Telegram/Test.pm  view on Meta::CPAN

use Test::MockObject::Extends;

use Mojo::JSON qw/encode_json decode_json/;
use Mojo::UserAgent;
use Mojo::Transaction::HTTP;

use WWW::Telegram::BotAPI;

use base 'Exporter';
our @EXPORT = qw {
  timer
  update
  loop_for_a_second
  random_valid_polling_response
  json_response
  bot_api
};

my @UPDATES = qw/message edited_message edited_channel_post callback_query/;

sub timer(&$) { Mojo::IOLoop -> timer(pop, pop) } ## no critic

sub loop_for_a_second {
  timer { Mojo::IOLoop -> stop } 1;
  Mojo::IOLoop -> start;
}

sub update {
  my ($type, $id) = @_;

  return {
    $type => {
      foo => 'bar',
      baz => 'qux',

t/lib/Bot/Telegram/Test.pm  view on Meta::CPAN


      $hook -> ($method, $postdata)
        if ref $hook eq 'CODE';

      my $tx = Mojo::Transaction::HTTP -> new;
      $tx -> res($res);

      $tx -> res($res);

      return $self -> {async}
        ? timer { return $cb -> ($self -> {agent}, $tx) if ref $cb eq 'CODE' } 0.1
        : _handle_error_sync $tx;
    }
  );

  return $api;
}

1

__END__

=encoding utf8

=head1 DESCRIPTION

General-purpose testing functions

=head1 FUNCTIONS

=head2 timer

  timer { say 'Tick' } 1;

Registers a timer for the Mojo::IOLoop global singleton. Takes a coderef to execute and a delay (in seconds) before execution.

=head2 loop_for_a_second

  loop_for_a_second;

Run Mojo::IOLoop just for one second, then shut it back down.

=head2 json_response

  my $res = json_response { ok => \1, message => 'dummy' };

t/lib/Bot/Telegram/Test.pm  view on Meta::CPAN


  $fake_api -> setWebhook({...}, sub {
    my ($ua, $tx) = @_;

    say $tx -> res -> json -> {url}; # says: https://foo.bar/webhook
  });

It is possible to pass multiple fake responses, in which case a new response will be shift()ed
 from the responses array every time for subsequent requests to the fake api.

Once the fake responses pool is depleted, no more timers will be scheduled when you call C<api_request>.

  my $fake_api = bot_api $res1, $res2;

  $fake_api -> api_request('doStuff', {...}, sub ($ua, $tx) {
    is_deeply $tx -> res, $res1; # okay
  });

  $fake_api -> api_request('doMoreStuff', {...}, sub ($ua, $tx) {
    is_deeply $tx -> res, $res2; # okay
  });



( run in 0.633 second using v1.01-cache-2.11-cpan-49f99fa48dc )