source: lib/BarnOwl/Module/Twitter/Handle.pm @ 82fd1e6

release-1.10release-1.7release-1.8release-1.9
Last change on this file since 82fd1e6 was 82fd1e6, checked in by Nelson Elhage <nelhage@mit.edu>, 15 years ago
Default since_id to 1, not 0. For some reason, {since_id => 0} makes friends_timeline return no results.
  • Property mode set to 100644
File size: 6.5 KB
Line 
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
16use Net::Twitter;
17use HTML::Entities;
18
19use BarnOwl;
20use BarnOwl::Message::Twitter;
21
22sub fail {
23    my $self = shift;
24    my $msg = shift;
25    undef $self->{twitter};
26    my $nickname = $self->{cfg}->{account_nickname} || "";
27    die("[Twitter $nickname] Error: $msg\n");
28}
29
30my $use_reply_to = 0;
31if($Net::Twitter::VERSION >= 2.06) {
32    $use_reply_to = 1;
33}
34
35sub new {
36
37    my $class = shift;
38    my $cfg = shift;
39
40    my $self = {
41        'user' => undef,
42        'cfg'  => $cfg,
43        'twitter' => undef,
44        'last_poll' => 0,
45        'last_direct_poll' => 0,
46        'last_id' => undef,
47        'last_direct' => undef,
48    };
49
50    bless($self, $class);
51
52    my %twitter_args = @_;
53
54    $self->{twitter}  = Net::Twitter->new(%twitter_args);
55
56    if(!defined($self->{twitter}->verify_credentials())) {
57        $self->fail("Invalid credentials");
58    }
59
60    unless(defined($self->{last_id})) {
61        eval {
62            $self->{last_id} = $self->{twitter}->friends_timeline({count => 1})->[0]{id};
63        };
64        $self->{last_id} = 1 unless defined($self->{last_id});
65    }
66
67    unless(defined($self->{last_direct})) {
68        eval {
69            $self->{last_direct} = $self->{twitter}->direct_messages()->[0]{id};
70        };
71        $self->{last_direct} = 1 unless defined($self->{last_direct});
72    }
73
74    eval {
75        $self->{twitter}->{ua}->timeout(1);
76    };
77
78    return $self;
79}
80
81sub twitter_error {
82    my $self = shift;
83
84    my $ratelimit = $self->{twitter}->rate_limit_status;
85    unless(defined($ratelimit) && ref($ratelimit) eq 'HASH') {
86        # Twitter's just sucking, sleep for 5 minutes
87        $self->{last_direct_poll} = $self->{last_poll} = time + 60*5;
88        # die("Twitter seems to be having problems.\n");
89        return;
90    }
91    if(exists($ratelimit->{remaining_hits})
92       && $ratelimit->{remaining_hits} <= 0) {
93        $self->{last_direct_poll} = $self->{last_poll} = $ratelimit->{reset_time_in_seconds};
94        die("Twitter: ratelimited until " . $ratelimit->{reset_time} . "\n");
95    } elsif(exists($ratelimit->{error})) {
96        die("Twitter: ". $ratelimit->{error} . "\n");
97        $self->{last_direct_poll} = $self->{last_poll} = time + 60*20;
98    }
99}
100
101sub poll_twitter {
102    my $self = shift;
103
104    return unless ( time - $self->{last_poll} ) >= 60;
105    $self->{last_poll} = time;
106    return unless BarnOwl::getvar('twitter:poll') eq 'on';
107
108    my $timeline = $self->{twitter}->friends_timeline( { since_id => $self->{last_id} } );
109    unless(defined($timeline) && ref($timeline) eq 'ARRAY') {
110        $self->twitter_error();
111        return;
112    };
113    if ( scalar @$timeline ) {
114        for my $tweet ( reverse @$timeline ) {
115            if ( $tweet->{id} <= $self->{last_id} ) {
116                next;
117            }
118            my $msg = BarnOwl::Message->new(
119                type      => 'Twitter',
120                sender    => $tweet->{user}{screen_name},
121                recipient => $self->{cfg}->{user} || $self->{user},
122                direction => 'in',
123                source    => decode_entities($tweet->{source}),
124                location  => decode_entities($tweet->{user}{location}||""),
125                body      => decode_entities($tweet->{text}),
126                status_id => $tweet->{id},
127                service   => $self->{cfg}->{service},
128                account   => $self->{cfg}->{account_nickname},
129               );
130            BarnOwl::queue_message($msg);
131        }
132        $self->{last_id} = $timeline->[0]{id} if $timeline->[0]{id} > $self->{last_id};
133    } else {
134        # BarnOwl::message("No new tweets...");
135    }
136}
137
138sub poll_direct {
139    my $self = shift;
140
141    return unless ( time - $self->{last_direct_poll}) >= 120;
142    $self->{last_direct_poll} = time;
143    return unless BarnOwl::getvar('twitter:poll') eq 'on';
144
145    my $direct = $self->{twitter}->direct_messages( { since_id => $self->{last_direct} } );
146    unless(defined($direct) && ref($direct) eq 'ARRAY') {
147        $self->twitter_error();
148        return;
149    };
150    if ( scalar @$direct ) {
151        for my $tweet ( reverse @$direct ) {
152            if ( $tweet->{id} <= $self->{last_direct} ) {
153                next;
154            }
155            my $msg = BarnOwl::Message->new(
156                type      => 'Twitter',
157                sender    => $tweet->{sender}{screen_name},
158                recipient => $self->{cfg}->{user} || $self->{user},
159                direction => 'in',
160                location  => decode_entities($tweet->{sender}{location}||""),
161                body      => decode_entities($tweet->{text}),
162                isprivate => 'true',
163                service   => $self->{cfg}->{service},
164                account   => $self->{cfg}->{account_nickname},
165               );
166            BarnOwl::queue_message($msg);
167        }
168        $self->{last_direct} = $direct->[0]{id} if $direct->[0]{id} > $self->{last_direct};
169    } else {
170        # BarnOwl::message("No new tweets...");
171    }
172}
173
174sub twitter {
175    my $self = shift;
176
177    my $msg = shift;
178    my $reply_to = shift;
179
180    if($msg =~ m{\Ad\s+([^\s])+(.*)}sm) {
181        $self->twitter_direct($1, $2);
182    } elsif(defined $self->{twitter}) {
183        if($use_reply_to && defined($reply_to)) {
184            $self->{twitter}->update({
185                status => $msg,
186                in_reply_to_status_id => $reply_to
187               });
188        } else {
189            $self->{twitter}->update($msg);
190        }
191    }
192}
193
194sub twitter_direct {
195    my $self = shift;
196
197    my $who = shift;
198    my $msg = shift;
199    if(defined $self->{twitter}) {
200        $self->{twitter}->new_direct_message({
201            user => $who,
202            text => $msg
203           });
204        if(BarnOwl::getvar("displayoutgoing") eq 'on') {
205            my $tweet = BarnOwl::Message->new(
206                type      => 'Twitter',
207                sender    => $self->{cfg}->{user} || $self->{user},
208                recipient => $who, 
209                direction => 'out',
210                body      => $msg,
211                isprivate => 'true',
212                service   => $self->{cfg}->{service},
213               );
214            BarnOwl::queue_message($tweet);
215        }
216    }
217}
218
219sub twitter_atreply {
220    my $self = shift;
221
222    my $to  = shift;
223    my $id  = shift;
224    my $msg = shift;
225    if(defined($id)) {
226        $self->twitter("@".$to." ".$msg, $id);
227    } else {
228        $self->twitter("@".$to." ".$msg);
229    }
230}
231
2321;
Note: See TracBrowser for help on using the repository browser.