File Coverage

blib/lib/Net/VKontakte/Standalone.pm
Criterion Covered Total %
statement 21 95 22.1
branch 0 44 0.0
condition 0 12 0.0
subroutine 7 16 43.7
pod 8 8 100.0
total 36 175 20.5


line stmt bran cond sub pod time code
1             package Net::VKontakte::Standalone;
2              
3 1     1   58516 use 5.008000;
  1         4  
  1         41  
4 1     1   5 use strict;
  1         1  
  1         47  
5 1     1   5 use warnings;
  1         7  
  1         39  
6              
7 1     1   4046 use URI;
  1         14108  
  1         37  
8 1     1   1517 use WWW::Mechanize;
  1         1386853  
  1         52  
9 1     1   1339 use JSON;
  1         19130  
  1         5  
10 1     1   157 use Carp;
  1         2  
  1         1058  
11              
12             our $VERSION = '0.11';
13              
14             sub new {
15 0     0 1   my $class = shift;
16 0           my $self = bless {},$class;
17 0           $self->{browser} = WWW::Mechanize::->new(
18             agent => __PACKAGE__.$VERSION,
19             autocheck => 1,
20             );
21 0 0         if (@_ == 1) {
    0          
22 0           $self->{api_id} = $_[0];
23             } elsif (@_ % 2 == 0) { # smells like hash
24 0           my %opt = @_;
25 0           for my $key (qw/api_id errors_noauto captcha_handler/) {
26 0 0         $self->{$key} = $opt{$key} if defined $opt{$key};
27             }
28             } else {
29 0           croak "wrong number of arguments to constructor";
30             }
31 0 0         croak "api_id is required" unless $self->{api_id};
32 0           return $self;
33             }
34              
35             sub _request {
36 0     0     my ($self, $params, $base) = @_;
37 0           (my $uri = URI::->new($base))->query_form($params);
38 0           return $self->{browser}->get($uri);
39             }
40              
41             sub auth { # dirty hack
42 0     0 1   my ($self,$login,$password,$scope) = @_;
43 0           @{$self}{"login","password","scope"} = ($login, $password, $scope); # reuse in case of reauth
  0            
44 0           $self->{browser}->get($self->auth_uri($scope));
45 0           $self->{browser}->submit_form(
46             with_fields => {
47             email => $login,
48             pass => $password,
49             },
50             ); # log in
51 0 0         $self->{browser}->submit unless $self->{browser}->uri =~ m|^https://oauth.vk.com/blank.html|; # allow access if requested to
52 0           return $self->redirected($self->{browser}->uri);
53             }
54              
55             sub auth_uri {
56 0     0 1   my ($self, $scope, $display) = @_;
57 0           (my $uri = URI::->new("https://api.vkontakte.ru/oauth/authorize"))->query_form(
58             {
59             client_id => $self->{api_id},
60             redirect_uri => "blank.html",
61             scope => $scope,
62             response_type => "token",
63             display => $display,
64             }
65             );
66 0           return $uri->canonical;
67             }
68              
69             sub redirected {
70 0     0 1   my ($self, $uri) = @_;
71 0 0         my %params = map { split /=/,$_,2 } split /&/,$1 if $uri =~ m|https://oauth.vk.com/blank.html#(.*)|;
  0            
72 0 0         croak "No access_token returned (wrong login/password?)" unless defined $params{access_token};
73 0           $self->{access_token} = $params{access_token};
74 0 0         croak "No token expiration time returned" unless $params{expires_in};
75 0           $self->{auth_time} = time;
76 0           $self->{expires_in} = $params{expires_in};
77 0           return $self;
78             }
79              
80              
81             sub api {
82 0     0 1   my ($self,$method,$params) = @_;
83 0 0         croak "Cannot make API calls unless authentificated" unless defined $self->{access_token};
84 0 0         if (time - $self->{auth_time} > $self->{expires_in}) {
85 0 0 0       if ($self->{login} && $self->{password} && $self->{scope}) {
      0        
86 0           $self->auth($self->{"login","password","scope"});
87             } else {
88 0 0         if ($self->{errors_noauto}) {
89 0           $self->{error} = "access_token expired";
90 0 0 0       if (ref $self->{errors_noauto} and ref $self->{errors_noauto} eq "CODE") {
91 0           $self->{errors_noauto}->({error_code => "none", error_msg => "access_token expired"});
92             }
93 0           return;
94             } else {
95 0           croak "access_token expired";
96             }
97             }
98             }
99 0           $params->{access_token} = $self->{access_token};
100 0           REQUEST: {
101 0           my $response = decode_json $self->_request($params,"https://api.vk.com/method/$method")->decoded_content;
102 0 0         if ($response->{response}) {
    0          
103 0           return $response->{response};
104             } elsif ($response->{error}) {
105 0 0         if ($self->{errors_noauto}) {
106 0           $self->{error} = $response->{error};
107 0 0 0       if (ref $self->{errors_noauto} and ref $self->{errors_noauto} eq "CODE") {
108 0           $self->{errors_noauto}->($response->{error});
109             }
110 0           return;
111             } else {
112 0 0         if (6 == $response->{error}{error_code}) { # Too many requests per second.
    0          
113 0           sleep 1;
114 0           redo REQUEST;
115             } elsif (14 == $response->{error}{error_code}) { # Captcha is needed
116 0 0         if ($self->{captcha_handler}) {
117 0           $params->{captcha_key} = $self->{captcha_handler}->($response->{error}{captcha_img});
118 0           $params->{captcha_sid} = $response->{error}{captcha_sid};
119 0           redo REQUEST;
120             } else {
121 0           croak "Captcha is needed and no captcha handler specified";
122             }
123             } else {
124 0           croak "API call returned error ".$response->{error}{error_msg};
125             }
126             }
127             } else {
128             croak "API call didn't return response or error".
129 0 0         $Carp::Verbose ? eval { require Data::Dumper; Data::Dumper::Dumper($response) }
  0            
  0            
130             : "";
131             }
132             }
133             }
134              
135             sub captcha_handler {
136 0     0 1   my ($self, $handler) = @_;
137 0 0         croak "\$handler is not a subroutine reference" unless ref $handler eq "CODE";
138 0           $self->{captcha_handler} = $handler;
139 0           return $self;
140             }
141              
142             sub error {
143 0     0 1   return shift->{error};
144             }
145              
146             sub errors_noauto {
147 0     0 1   my ($self, $noauto) = @_;
148 0           $self->{errors_noauto} = $noauto; # whatever this means
149 0           return $self;
150             }
151              
152             1;
153             __END__