File Coverage

blib/lib/App/Maisha/Plugin/Twitter.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package App::Maisha::Plugin::Twitter;
2              
3 7     7   165698 use strict;
  7         19  
  7         378  
4 7     7   40 use warnings;
  7         17  
  7         427  
5              
6             our $VERSION = '0.21';
7              
8             #----------------------------------------------------------------------------
9             # Library Modules
10              
11 7     7   43 use base qw(App::Maisha::Plugin::Base);
  7         13  
  7         2801  
12 7     7   41 use base qw(Class::Accessor::Fast);
  7         11  
  7         1774  
13 7     7   5247 use File::Path;
  7         17  
  7         499  
14 7     7   22321 use Net::Twitter;
  0            
  0            
15             use Storable;
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             my $class = shift;
38             my $self = {
39             consumer_key => 'ifCuNOQXA5KTnKVXVcZg',
40             consumer_secret => 'OXyHE1PSgfy66gbCu3QshXgP9RNA1fOVLdqv4afPDug',
41             };
42              
43             bless $self, $class;
44             return $self;
45             }
46              
47             sub login {
48             my ($self,$config) = @_;
49             my $api;
50              
51             unless($config->{username}) { warn "No username supplied\n"; return }
52              
53             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             ssl => 1
59             );
60             };
61              
62             unless($api) {
63             warn "Unable to establish connection to Twitter API\n";
64             return 0;
65             }
66              
67             # for testing purposes we don't want to login
68             if(!$config->{test}) {
69             eval {
70             my $datafile = $config->{home} . '/.maisha/twitter.dat';
71             my $access_tokens = eval { retrieve($datafile) } || {};
72              
73             if ( $access_tokens && $access_tokens->{$config->{username}}) {
74             $api->access_token($access_tokens->{$config->{username}}->[0]);
75             $api->access_token_secret($access_tokens->{$config->{username}}->[1]);
76             } else {
77             my $auth_url = $api->get_authorization_url;
78             print " Authorize this application at: $auth_url\nThen, enter the PIN# provided to continue: ";
79              
80             my $pin = <STDIN>; # wait for input
81             chomp $pin;
82              
83             unless($pin) {
84             warn "No PIN provided, Maisha will not be able to access Twitter account until authorized to do so\n";
85             return 0;
86             }
87              
88             # request_access_token stores the tokens in $nt AND returns them
89             my $access_tokens = {};
90             my @access_tokens;
91             eval { @access_tokens = $api->request_access_token(verifier => $pin) };
92             unless(@access_tokens) {
93             warn "Invalid PIN provided, Maisha will not be able to access your Twitter account until authorized to do so\n";
94             return 0;
95             }
96             $access_tokens->{$config->{username}} = \@access_tokens;
97              
98             mkpath( $config->{home} . '/.maisha' );
99              
100             # save the access tokens
101             store $access_tokens, $datafile;
102             chmod 0640, $datafile; # make sure it has reasonable permissions
103             }
104             };
105              
106             if($@) {
107             warn "Unable to login to Twitter\n";
108             return 0;
109             }
110             }
111              
112             $self->api($api);
113             $self->config($config);
114              
115             if(!$config->{test}) {
116             print "...building user cache for Twitter\n";
117             $self->_build_users();
118             }
119              
120             return 1;
121             }
122              
123             sub _build_users {
124             my $self = shift;
125             my %users;
126              
127             eval {
128             my $f = $self->api->friends();
129             if($f && @$f) { for(@$f) { next unless($_); $users{$_->{screen_name}} = 1 } }
130             $f = $self->api->followers();
131             if($f && @$f) { for(@$f) { next unless($_); $users{$_->{screen_name}} = 1 } }
132             };
133              
134             if($@) {
135             warn "Error retrieving friends/followers from Twitter: $@\n";
136             return;
137             }
138              
139             $self->users(\%users);
140             }
141              
142             sub api_reauthorize {
143             my $self = shift;
144             my $config = $self->config;
145             my $api = $self->api;
146              
147             # for testing purposes we don't want to login
148             if(!$config->{test}) {
149             eval {
150             my $datafile = $config->{home} . '/.maisha/twitter.dat';
151              
152             my $auth_url = $api->get_authorization_url;
153             print "Please re-authorize this application at: $auth_url\nThen, enter the PIN# provided to continue: ";
154              
155             my $pin = <STDIN>; # wait for input
156             chomp $pin;
157              
158             unless($pin) {
159             warn "No PIN provided, Maisha will not be able to access direct messages until reauthorization is completed\n";
160             return 0;
161             }
162              
163             # request_access_token stores the tokens in $nt AND returns them
164             my $access_tokens = {};
165             my @access_tokens;
166             eval { @access_tokens = $api->request_access_token(verifier => $pin) };
167             unless(@access_tokens) {
168             warn "Invalid PIN provided, Maisha will not be able to access direct messages until reauthorization is completed\n";
169             return 0;
170             }
171             $access_tokens->{$config->{username}} = \@access_tokens;
172              
173             unlink $datafile;
174             mkpath( $config->{home} . '/.maisha' );
175              
176             # save the access tokens
177             store $access_tokens, $datafile;
178             chmod 0640, $datafile; # make sure it has reasonable permissions
179             };
180              
181             if($@) {
182             warn "Unable to login to Twitter\n";
183             return 0;
184             }
185             }
186              
187             return 1;
188             }
189              
190             sub api_follow {
191             my $self = shift;
192             $self->api->create_friend(@_);
193             }
194              
195             sub api_unfollow {
196             my $self = shift;
197             $self->api->destroy_friend(@_);
198             }
199              
200             sub api_user {
201             my $self = shift;
202             $self->api->show_user(@_);
203             }
204              
205             sub api_user_timeline {
206             my $self = shift;
207             $self->api->user_timeline(@_);
208             }
209              
210             sub api_friends {
211             my $self = shift;
212             $self->api->following(@_);
213             }
214              
215             sub api_friends_timeline {
216             my $self = shift;
217             $self->api->following_timeline(@_);
218             }
219              
220             sub api_public_timeline {
221             my $self = shift;
222             $self->api->public_timeline(@_);
223             }
224              
225             sub api_followers {
226             my $self = shift;
227             $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             my $self = shift;
235             $self->api->update(@_);
236             }
237              
238             sub api_replies {
239             my $self = shift;
240             $self->api->replies(@_);
241             }
242              
243             sub api_send_message {
244             my $self = shift;
245             $self->api->new_direct_message(@_);
246             }
247              
248             sub api_direct_messages_to {
249             my $self = shift;
250             $self->api->direct_messages(@_);
251             }
252              
253             sub api_direct_messages_from {
254             my $self = shift;
255             $self->api->sent_direct_messages(@_);
256             }
257              
258             sub api_search {
259             my $self = shift;
260             $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-2014 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