File Coverage

blib/lib/App/Maisha/Plugin/Twitter.pm
Criterion Covered Total %
statement 36 143 25.1
branch 4 34 11.7
condition 0 11 0.0
subroutine 9 25 36.0
pod 17 17 100.0
total 66 230 28.7


line stmt bran cond sub pod time code
1             package App::Maisha::Plugin::Twitter;
2              
3 7     7   159611 use strict;
  7         17  
  7         190  
4 7     7   31 use warnings;
  7         12  
  7         266  
5              
6             our $VERSION = '0.22';
7              
8             #----------------------------------------------------------------------------
9             # Library Modules
10              
11 7     7   35 use base qw(App::Maisha::Plugin::Base);
  7         11  
  7         1152  
12 7     7   36 use base qw(Class::Accessor::Fast);
  7         21  
  7         806  
13 7     7   2881 use File::Path;
  7         18  
  7         370  
14 7     7   1095 use Net::Twitter;
  7         2825982  
  7         203  
15 7     7   38 use Storable;
  7         14  
  7         7671  
16              
17             #----------------------------------------------------------------------------
18             # Accessors
19              
20             __PACKAGE__->mk_accessors($_) for qw(api users config);
21              
22             #----------------------------------------------------------------------------
23             # Public API
24              
25             #Request token URL
26             #https://api.twitter.com/oauth/request_token
27              
28             #Access token URL
29             #https://api.twitter.com/oauth/access_token
30              
31             #Authorize URL
32             #https://api.twitter.com/oauth/authorize
33              
34             # http://dev.twitter.com/apps/347040
35              
36             sub new {
37 1     1 1 14 my $class = shift;
38 1         3 my $self = {
39             consumer_key => 'ifCuNOQXA5KTnKVXVcZg',
40             consumer_secret => 'OXyHE1PSgfy66gbCu3QshXgP9RNA1fOVLdqv4afPDug',
41             };
42              
43 1         2 bless $self, $class;
44 1         2 return $self;
45             }
46              
47             sub login {
48 1     1 1 3 my ($self,$config) = @_;
49 1         1 my $api;
50              
51 1 50       3 unless($config->{username}) { warn "No username supplied\n"; return }
  0         0  
  0         0  
52              
53 1         2 eval {
54             $api = Net::Twitter->new(
55             traits => [qw/API::Search API::REST OAuth/],
56             consumer_key => $self->{consumer_key},
57             consumer_secret => $self->{consumer_secret},
58 1         9 ssl => 1
59             );
60             };
61              
62 1 50       884741 unless($api) {
63 0         0 warn "Unable to establish connection to Twitter API\n";
64 0         0 return 0;
65             }
66              
67             # for testing purposes we don't want to login
68 1 50       5 if(!$config->{test}) {
69 0         0 eval {
70 0         0 my $datafile = $config->{home} . '/.maisha/twitter.dat';
71 0   0     0 my $access_tokens = eval { retrieve($datafile) } || {};
72              
73 0 0 0     0 if ( $access_tokens && $access_tokens->{$config->{username}}) {
74 0         0 $api->access_token($access_tokens->{$config->{username}}->[0]);
75 0         0 $api->access_token_secret($access_tokens->{$config->{username}}->[1]);
76             } else {
77 0         0 my $auth_url = $api->get_authorization_url;
78 0         0 print " Authorize this application at: $auth_url\nThen, enter the PIN# provided to continue: ";
79              
80 0         0 my $pin = <STDIN>; # wait for input
81 0         0 chomp $pin;
82              
83 0 0       0 unless($pin) {
84 0         0 warn "No PIN provided, Maisha will not be able to access Twitter account until authorized to do so\n";
85 0         0 return 0;
86             }
87              
88             # request_access_token stores the tokens in $nt AND returns them
89 0         0 my $access_tokens = {};
90 0         0 my @access_tokens;
91 0         0 eval { @access_tokens = $api->request_access_token(verifier => $pin) };
  0         0  
92 0 0       0 unless(@access_tokens) {
93 0         0 warn "Invalid PIN provided, Maisha will not be able to access your Twitter account until authorized to do so\n";
94 0         0 return 0;
95             }
96 0         0 $access_tokens->{$config->{username}} = \@access_tokens;
97              
98 0         0 mkpath( $config->{home} . '/.maisha' );
99              
100             # save the access tokens
101 0         0 store $access_tokens, $datafile;
102 0         0 chmod 0640, $datafile; # make sure it has reasonable permissions
103             }
104             };
105              
106 0 0       0 if($@) {
107 0         0 warn "Unable to login to Twitter\n";
108 0         0 return 0;
109             }
110             }
111              
112 1         21 $self->api($api);
113 1         24 $self->config($config);
114              
115 1 50       10 if(!$config->{test}) {
116 0         0 print "...building user cache for Twitter\n";
117 0         0 $self->_build_users();
118             }
119              
120 1         5 return 1;
121             }
122              
123             sub _build_users {
124 0     0     my $self = shift;
125 0           my %users;
126              
127 0           eval {
128 0           my $f = $self->api->friends();
129 0 0 0       if($f && @$f) { for(@$f) { next unless($_); $users{$_->{screen_name}} = 1 } }
  0 0          
  0            
  0            
130 0           $f = $self->api->followers();
131 0 0 0       if($f && @$f) { for(@$f) { next unless($_); $users{$_->{screen_name}} = 1 } }
  0 0          
  0            
  0            
132             };
133              
134 0 0         if($@) {
135 0           warn "Error retrieving friends/followers from Twitter: $@\n";
136 0           return;
137             }
138              
139 0           $self->users(\%users);
140             }
141              
142             sub api_reauthorize {
143 0     0 1   my $self = shift;
144 0           my $config = $self->config;
145 0           my $api = $self->api;
146              
147             # for testing purposes we don't want to login
148 0 0         if(!$config->{test}) {
149 0           eval {
150 0           my $datafile = $config->{home} . '/.maisha/twitter.dat';
151              
152 0           my $auth_url = $api->get_authorization_url;
153 0           print "Please re-authorize this application at: $auth_url\nThen, enter the PIN# provided to continue: ";
154              
155 0           my $pin = <STDIN>; # wait for input
156 0           chomp $pin;
157              
158 0 0         unless($pin) {
159 0           warn "No PIN provided, Maisha will not be able to access direct messages until reauthorization is completed\n";
160 0           return 0;
161             }
162              
163             # request_access_token stores the tokens in $nt AND returns them
164 0           my $access_tokens = {};
165 0           my @access_tokens;
166 0           eval { @access_tokens = $api->request_access_token(verifier => $pin) };
  0            
167 0 0         unless(@access_tokens) {
168 0           warn "Invalid PIN provided, Maisha will not be able to access direct messages until reauthorization is completed\n";
169 0           return 0;
170             }
171 0           $access_tokens->{$config->{username}} = \@access_tokens;
172              
173 0           unlink $datafile;
174 0           mkpath( $config->{home} . '/.maisha' );
175              
176             # save the access tokens
177 0           store $access_tokens, $datafile;
178 0           chmod 0640, $datafile; # make sure it has reasonable permissions
179             };
180              
181 0 0         if($@) {
182 0           warn "Unable to login to Twitter\n";
183 0           return 0;
184             }
185             }
186              
187 0           return 1;
188             }
189              
190             sub api_follow {
191 0     0 1   my $self = shift;
192 0           $self->api->create_friend(@_);
193             }
194              
195             sub api_unfollow {
196 0     0 1   my $self = shift;
197 0           $self->api->destroy_friend(@_);
198             }
199              
200             sub api_user {
201 0     0 1   my $self = shift;
202 0           $self->api->show_user(@_);
203             }
204              
205             sub api_user_timeline {
206 0     0 1   my $self = shift;
207 0           $self->api->user_timeline(@_);
208             }
209              
210             sub api_friends {
211 0     0 1   my $self = shift;
212 0           $self->api->following(@_);
213             }
214              
215             sub api_friends_timeline {
216 0     0 1   my $self = shift;
217 0           $self->api->following_timeline(@_);
218             }
219              
220             sub api_public_timeline {
221 0     0 1   my $self = shift;
222 0           $self->api->public_timeline(@_);
223             }
224              
225             sub api_followers {
226 0     0 1   my $self = shift;
227 0           $self->api->followers(@_);
228              
229             # below is meant to be the same as the above, but it isn't :(
230             #$self->api->lookup_users( { user_id => $self->api->followers_ids() } );
231             }
232              
233             sub api_update {
234 0     0 1   my $self = shift;
235 0           $self->api->update(@_);
236             }
237              
238             sub api_replies {
239 0     0 1   my $self = shift;
240 0           $self->api->replies(@_);
241             }
242              
243             sub api_send_message {
244 0     0 1   my $self = shift;
245 0           $self->api->new_direct_message(@_);
246             }
247              
248             sub api_direct_messages_to {
249 0     0 1   my $self = shift;
250 0           $self->api->direct_messages(@_);
251             }
252              
253             sub api_direct_messages_from {
254 0     0 1   my $self = shift;
255 0           $self->api->sent_direct_messages(@_);
256             }
257              
258             sub api_search {
259 0     0 1   my $self = shift;
260 0           $self->api->search(@_);
261             }
262              
263             1;
264              
265             __END__
266              
267             =head1 NAME
268              
269             App::Maisha::Plugin::Twitter - Maisha interface to Twitter
270              
271             =head1 SYNOPSIS
272              
273             maisha
274             maisha> use Twitter
275             use ok
276              
277             =head1 DESCRIPTION
278              
279             App::Maisha::Plugin::Twitter is the gateway for Maisha to access the Twitter
280             API.
281              
282             =head1 METHODS
283              
284             =head2 Constructor
285              
286             =over 4
287              
288             =item * new
289              
290             =back
291              
292             =head2 Process Methods
293              
294             =over 4
295              
296             =item * login
297              
298             Login to the service. See Authentication below.
299              
300             =back
301              
302             =head2 API Methods
303              
304             The API methods are used to interface to with the Twitter API.
305              
306             =over 4
307              
308             =item * api_reauthorize
309              
310             =item * api_follow
311              
312             =item * api_unfollow
313              
314             =item * api_user
315              
316             =item * api_user_timeline
317              
318             =item * api_friends
319              
320             =item * api_friends_timeline
321              
322             =item * api_public_timeline
323              
324             =item * api_followers
325              
326             =item * api_update
327              
328             =item * api_replies
329              
330             =item * api_send_message
331              
332             =item * api_direct_messages_to
333              
334             =item * api_direct_messages_from
335              
336             =item * api_search
337              
338             =back
339              
340             =head1 AUTHENTICATION
341              
342             On 31st August 2010, Twitter disabled Basic Authentication to access their API.
343             Instead they have introduce the OAuth method of authrntication, which now
344             requires application developers to request the user to authenticate themselves
345             and provide a PIN (Personal Identification Number) to allow the application to
346             retrieve access tokens.
347              
348             With this new method of authentication, the application will provide a URL,
349             which the user needs to cut-n-paste into a browser to logging in to the
350             service, using your regular username/password, then 'Allow' Maisha to access
351             your account. This will then allow Maisha to post to your account. You will
352             then be given a PIN, which should then be entered at the prompt on the Maisha
353             command line.
354              
355             Once you have completed authentication, the application will then store your
356             access tokens permanently under your profile on your computer. Then when you
357             next use the application it will retrieve these access tokens automatically and
358             you will no longer need to register the application.
359              
360             =head1 SEE ALSO
361              
362             For further information regarding the commands and configuration, please see
363             the 'maisha' script included with this distribution.
364              
365             L<App::Maisha>
366              
367             L<Net::Twitter>
368              
369             =head1 WEBSITES
370              
371             =over 4
372              
373             =item * Main Site: L<http://maisha.grango.org>
374              
375             =item * Git Repo: L<http://github.com/barbie/maisha/tree/master>
376              
377             =item * RT Queue: L<RT: http://rt.cpan.org/Public/Dist/Display.html?Name=App-Maisha>
378              
379             =back
380              
381             =head1 AUTHOR
382              
383             Barbie, <barbie@cpan.org>
384             for Miss Barbell Productions <http://www.missbarbell.co.uk>.
385              
386             =head1 COPYRIGHT AND LICENSE
387              
388             Copyright (C) 2009-2019 by Barbie
389              
390             This distribution is free software; you can redistribute it and/or
391             modify it under the Artistic License v2.
392              
393             =cut