source: lib/BarnOwl/Module/Twitter/Handle.pm @ 513da71

release-1.10release-1.7release-1.8release-1.9
Last change on this file since 513da71 was 513da71, checked in by Kevin Riggle <kevinr@free-dissociation.com>, 15 years ago
Add :twitter-follow and :twitter-unfollow commands
  • Property mode set to 100644
File size: 7.8 KB
RevLine 
[159aaad]1use warnings;
2use strict;
3
4=head1 NAME
5
6BarnOwl::Module::Twitter::Handle
7
8=head1 DESCRIPTION
9
10Contains everything needed to send and receive messages from a Twitter-like service.
11
12=cut
13
14package BarnOwl::Module::Twitter::Handle;
15
[0b13bbc]16use Net::Twitter::Lite;
[159aaad]17use HTML::Entities;
18
19use BarnOwl;
20use BarnOwl::Message::Twitter;
[7430aa4]21
[159aaad]22sub fail {
23    my $self = shift;
24    my $msg = shift;
25    undef $self->{twitter};
[7430aa4]26    my $nickname = $self->{cfg}->{account_nickname} || "";
27    die("[Twitter $nickname] Error: $msg\n");
[159aaad]28}
29
30sub new {
31    my $class = shift;
32    my $cfg = shift;
33
[efcd223]34    $cfg = {
35        account_nickname => '',
36        default_sender   => 0,
37        poll_for_tweets  => 1,
38        poll_for_dms     => 1,
39        publish_tweets   => 1,
[5da6ed8]40        show_unsubscribed_replies => 1,
[efcd223]41        %$cfg
42       };
43
[7430aa4]44    my $self = {
[159aaad]45        'cfg'  => $cfg,
46        'twitter' => undef,
47        'last_poll' => 0,
48        'last_direct_poll' => 0,
49        'last_id' => undef,
50        'last_direct' => undef,
[7430aa4]51    };
52
53    bless($self, $class);
[159aaad]54
55    my %twitter_args = @_;
56
[0b13bbc]57    $self->{twitter}  = Net::Twitter::Lite->new(%twitter_args);
[159aaad]58
[82e0f26]59    my $timeline = eval { $self->{twitter}->friends_timeline({count => 1}) };
60    warn "$@" if $@;
[a8a0b0a]61
62    if(!defined($timeline)) {
[7430aa4]63        $self->fail("Invalid credentials");
[159aaad]64    }
65
[a8a0b0a]66    eval {
67        $self->{last_id} = $timeline->[0]{id};
68    };
69    $self->{last_id} = 1 unless defined($self->{last_id});
[159aaad]70
[a8a0b0a]71    eval {
72        $self->{last_direct} = $self->{twitter}->direct_messages()->[0]{id};
73    };
[82e0f26]74    warn "$@" if $@;
[a8a0b0a]75    $self->{last_direct} = 1 unless defined($self->{last_direct});
[159aaad]76
77    eval {
[7430aa4]78        $self->{twitter}->{ua}->timeout(1);
[159aaad]79    };
[82e0f26]80    warn "$@" if $@;
[159aaad]81
[7430aa4]82    return $self;
[159aaad]83}
84
85sub twitter_error {
86    my $self = shift;
87
[82e0f26]88    my $ratelimit = eval { $self->{twitter}->rate_limit_status };
89    warn "$@" if $@;
[159aaad]90    unless(defined($ratelimit) && ref($ratelimit) eq 'HASH') {
91        # Twitter's just sucking, sleep for 5 minutes
92        $self->{last_direct_poll} = $self->{last_poll} = time + 60*5;
93        # die("Twitter seems to be having problems.\n");
94        return;
95    }
96    if(exists($ratelimit->{remaining_hits})
97       && $ratelimit->{remaining_hits} <= 0) {
98        $self->{last_direct_poll} = $self->{last_poll} = $ratelimit->{reset_time_in_seconds};
99        die("Twitter: ratelimited until " . $ratelimit->{reset_time} . "\n");
100    } elsif(exists($ratelimit->{error})) {
101        die("Twitter: ". $ratelimit->{error} . "\n");
102        $self->{last_direct_poll} = $self->{last_poll} = time + 60*20;
103    }
104}
105
106sub poll_twitter {
107    my $self = shift;
108
109    return unless ( time - $self->{last_poll} ) >= 60;
110    $self->{last_poll} = time;
111    return unless BarnOwl::getvar('twitter:poll') eq 'on';
112
[82e0f26]113    my $timeline = eval { $self->{twitter}->friends_timeline( { since_id => $self->{last_id} } ) };
114    warn "$@" if $@;
[159aaad]115    unless(defined($timeline) && ref($timeline) eq 'ARRAY') {
116        $self->twitter_error();
117        return;
118    };
[5da6ed8]119
120    if ($self->{cfg}->{show_unsubscribed_replies}) {
[82e0f26]121        my $mentions = eval { $self->{twitter}->mentions( { since_id => $self->{last_id} } ) };
122        warn "$@" if $@;
[5da6ed8]123        unless (defined($mentions) && ref($mentions) eq 'ARRAY') {
124            $self->twitter_error();
125            return;
126        };
127        #combine, sort by id, and uniq
128        push @$timeline, @$mentions;
129        @$timeline = sort { $b->{id} <=> $a->{id} } @$timeline;
130        my $prev = { id => 0 };
131        @$timeline = grep($_->{id} != $prev->{id} && (($prev) = $_), @$timeline);
132    }
133
[159aaad]134    if ( scalar @$timeline ) {
135        for my $tweet ( reverse @$timeline ) {
136            if ( $tweet->{id} <= $self->{last_id} ) {
137                next;
138            }
139            my $msg = BarnOwl::Message->new(
140                type      => 'Twitter',
141                sender    => $tweet->{user}{screen_name},
142                recipient => $self->{cfg}->{user} || $self->{user},
143                direction => 'in',
144                source    => decode_entities($tweet->{source}),
145                location  => decode_entities($tweet->{user}{location}||""),
146                body      => decode_entities($tweet->{text}),
147                status_id => $tweet->{id},
148                service   => $self->{cfg}->{service},
149                account   => $self->{cfg}->{account_nickname},
150               );
151            BarnOwl::queue_message($msg);
152        }
153        $self->{last_id} = $timeline->[0]{id} if $timeline->[0]{id} > $self->{last_id};
154    } else {
155        # BarnOwl::message("No new tweets...");
156    }
157}
158
159sub poll_direct {
160    my $self = shift;
161
162    return unless ( time - $self->{last_direct_poll}) >= 120;
163    $self->{last_direct_poll} = time;
164    return unless BarnOwl::getvar('twitter:poll') eq 'on';
165
[82e0f26]166    my $direct = eval { $self->{twitter}->direct_messages( { since_id => $self->{last_direct} } ) };
167    warn "$@" if $@;
[159aaad]168    unless(defined($direct) && ref($direct) eq 'ARRAY') {
169        $self->twitter_error();
170        return;
171    };
172    if ( scalar @$direct ) {
173        for my $tweet ( reverse @$direct ) {
174            if ( $tweet->{id} <= $self->{last_direct} ) {
175                next;
176            }
177            my $msg = BarnOwl::Message->new(
178                type      => 'Twitter',
179                sender    => $tweet->{sender}{screen_name},
180                recipient => $self->{cfg}->{user} || $self->{user},
181                direction => 'in',
182                location  => decode_entities($tweet->{sender}{location}||""),
183                body      => decode_entities($tweet->{text}),
184                isprivate => 'true',
185                service   => $self->{cfg}->{service},
186                account   => $self->{cfg}->{account_nickname},
187               );
188            BarnOwl::queue_message($msg);
189        }
190        $self->{last_direct} = $direct->[0]{id} if $direct->[0]{id} > $self->{last_direct};
191    } else {
192        # BarnOwl::message("No new tweets...");
193    }
194}
195
196sub twitter {
197    my $self = shift;
198
199    my $msg = shift;
200    my $reply_to = shift;
201
202    if($msg =~ m{\Ad\s+([^\s])+(.*)}sm) {
203        $self->twitter_direct($1, $2);
204    } elsif(defined $self->{twitter}) {
[0b13bbc]205        if(defined($reply_to)) {
[159aaad]206            $self->{twitter}->update({
207                status => $msg,
208                in_reply_to_status_id => $reply_to
209               });
210        } else {
211            $self->{twitter}->update($msg);
212        }
213    }
214}
215
216sub twitter_direct {
217    my $self = shift;
218
219    my $who = shift;
220    my $msg = shift;
221    if(defined $self->{twitter}) {
222        $self->{twitter}->new_direct_message({
223            user => $who,
224            text => $msg
225           });
226        if(BarnOwl::getvar("displayoutgoing") eq 'on') {
227            my $tweet = BarnOwl::Message->new(
228                type      => 'Twitter',
229                sender    => $self->{cfg}->{user} || $self->{user},
230                recipient => $who, 
231                direction => 'out',
232                body      => $msg,
233                isprivate => 'true',
234                service   => $self->{cfg}->{service},
235               );
236            BarnOwl::queue_message($tweet);
237        }
238    }
239}
240
241sub twitter_atreply {
242    my $self = shift;
243
244    my $to  = shift;
245    my $id  = shift;
246    my $msg = shift;
247    if(defined($id)) {
248        $self->twitter("@".$to." ".$msg, $id);
249    } else {
250        $self->twitter("@".$to." ".$msg);
251    }
252}
253
[513da71]254sub twitter_follow {
255    my $self = shift;
256
257    my $who = shift;
258
259    my $user = $self->{twitter}->create_friend($who);
260    # returns a string on error
261    if (defined $user && !ref $user) {
262        BarnOwl::message($user);
263    } else {
264        BarnOwl::message("Following " . $who);
265    }
266}
267
268sub twitter_unfollow {
269    my $self = shift;
270
271    my $who = shift;
272
273    my $user = $self->{twitter}->destroy_friend($who);
274    # returns a string on error
275    if (defined $user && !ref $user) {
276        BarnOwl::message($user);
277    } else {
278        BarnOwl::message("No longer following " . $who);
279    }
280}
281
[159aaad]2821;
Note: See TracBrowser for help on using the repository browser.