File Coverage

blib/lib/Bot/ChatBots/Telegram/LongPoll.pm
Criterion Covered Total %
statement 65 74 87.8
branch 9 22 40.9
condition 3 9 33.3
subroutine 16 17 94.1
pod 5 5 100.0
total 98 127 77.1


line stmt bran cond sub pod time code
1             package Bot::ChatBots::Telegram::LongPoll;
2 2     2   1926 use strict;
  2         6  
  2         48  
3 2     2   9 use warnings;
  2         3  
  2         68  
4             { our $VERSION = '0.012'; }
5              
6 2     2   9 use Ouch;
  2         3  
  2         12  
7 2     2   121 use Try::Tiny;
  2         12  
  2         106  
8 2     2   372 use Log::Any qw< $log >;
  2         6574  
  2         10  
9 2     2   1870 use Mojo::IOLoop ();
  2         5  
  2         24  
10 2     2   8 use IO::Socket::SSL (); # just to be sure to complain loudly in case
  2         3  
  2         70  
11 2     2   11 use List::Util qw< max >;
  2         4  
  2         120  
12              
13 2     2   12 use Moo;
  2         4  
  2         10  
14 2     2   626 use namespace::clean;
  2         4  
  2         13  
15              
16             with 'Bot::ChatBots::Telegram::Role::Source'; # normalize_record, token
17             with 'Bot::ChatBots::Role::Source'; # processor, typename
18              
19             has connect_timeout => (
20             is => 'ro',
21             default => sub { return 20 },
22             );
23              
24             has interval => (
25             is => 'ro',
26             default => sub { return 0.1 },
27             );
28              
29             has max_redirects => (
30             is => 'ro',
31             default => sub { return 5 },
32             );
33              
34             has _start => (
35             is => 'ro',
36             default => sub { return 1 },
37             init_arg => 'start',
38             );
39              
40             has update_timeout => (
41             is => 'ro',
42             default => sub { return 300 },
43             );
44              
45             sub BUILD {
46 1     1 1 5 my $self = shift;
47 1 50       9 $self->start if $self->_start;
48             }
49              
50             sub class_custom_pairs {
51 1     1 1 33 my $self = shift;
52 1         23 return (token => $self->token);
53             }
54              
55             sub parse_response {
56 1     1 1 127 my ($self, $res, $threshold_id) = @_;
57 1   50     7 my $data = $res->json // {};
58 1 50       94 if (!$data->{ok}) { # boolean flag from Telegram API
59             $log->error('getUpdates error: ' .
60 0   0     0 $data->{description} // 'unknown error');
61 0         0 return;
62             }
63              
64 1   50     2 return grep { $_->{update_id} >= $threshold_id } @{$data->{result}//[]};
  1         5  
  1         4  
65             }
66              
67             sub poller {
68 1     1 1 606 my $self = shift;
69 1 50 33     5 my $args = (@_ && ref($_[0])) ? $_[0] : {@_};
70              
71 1         5 my $update_timeout = $self->update_timeout;
72 1         5 my %query = (
73             offset => 0,
74             telegram_method => 'getUpdates',
75             timeout => $update_timeout,
76             );
77              
78 1         22 my $sender = $self->sender;
79 1         22 $sender->telegram->agent->connect_timeout($self->connect_timeout)
80             ->inactivity_timeout($update_timeout + 5)
81             ->max_redirects($self->max_redirects);
82              
83             # this flag tells us whether we're in a call already, avoiding
84             # duplicates. It is set before sending a request, and reset when the
85             # response is managed
86 1         460 my $is_busy;
87              
88             my $on_data = sub {
89 1     1   2869 my ($ua, $tx) = @_;
90              
91 1         4 my @updates;
92             try {
93 1         115 @updates = $self->parse_response($tx->res, $query{offset});
94             }
95             catch {
96 0         0 $log->error(bleep $_);
97 0 0       0 die $_ if $self->should_rethrow($args);
98 1         13 };
99              
100 1         31 my @retval = $self->process_updates(
101             refs => {
102             sender => $sender,
103             tx => $tx,
104             ua => $ua,
105             },
106             source_pairs => {
107             query => \%query,
108             },
109             updates => \@updates,
110             %$args, # may override it all!
111             );
112              
113 1         36 for my $item (@retval) {
114 1 50       4 next unless defined $item;
115 1 50       3 defined(my $record = $item->{record}) or next;
116 1 50       5 defined(my $outcome = $item->{outcome}) or next;
117 1 50       4 defined(my $message = $outcome->{send_response}) or next;
118 0         0 $sender->send_message($message, record => $record);
119             }
120              
121             # if we get here, somehow me managed to get past this call... Get
122             # ready for the next one. Just to be on the safe side, we will
123             # advance $query{offset} anyway
124 1 50       4 $query{offset} = 1 + max map { $_->{update_id} } @updates
  1         5  
125             if @updates;
126 1         6 $is_busy = 0;
127 1         5 };
128              
129             return sub {
130 1 50   1   835 return if $is_busy;
131 1         3 $is_busy = 1; # $on_data below will reset $is_busy when ready
132 1         5 $sender->send_message(\%query, callback => $on_data);
133 1         6 };
134             } ## end sub callback
135              
136             around process => sub {
137             my ($orig, $self, $record) = @_;
138             my $outcome = $orig->($self, $record);
139             $record->{source}{query}{offset} = $record->{update}{update_id} + 1;
140             return $outcome;
141             };
142              
143             sub start {
144 0     0 1   my $self = shift;
145 0           Mojo::IOLoop->recurring($self->interval, $self->poller(@_));
146 0 0         Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
147 0           return $self;
148             }
149              
150             1;