Changeset 159aaad for lib/BarnOwl/Module/Twitter.pm
- Timestamp:
- Jul 21, 2009, 9:27:32 PM (15 years ago)
- Branches:
- master, release-1.10, release-1.7, release-1.8, release-1.9
- Children:
- 7430aa4
- Parents:
- f93b81b
- git-author:
- Kevin Riggle <kevinr@free-dissociation.com> (07/21/09 21:27:02)
- git-committer:
- Nelson Elhage <nelhage@mit.edu> (07/21/09 21:27:32)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
lib/BarnOwl/Module/Twitter.pm
rf93b81b r159aaad 14 14 package BarnOwl::Module::Twitter; 15 15 16 our $VERSION = 0. 1;16 our $VERSION = 0.2; 17 17 18 18 use Net::Twitter; … … 21 21 use BarnOwl; 22 22 use BarnOwl::Hooks; 23 use BarnOwl::M essage::Twitter;24 use HTML::Entities; 25 26 our $ twitter;23 use BarnOwl::Module::Twitter::Handle; 24 25 our @twitter_handles; 26 our $default_handle; 27 27 my $user = BarnOwl::zephyr_getsender(); 28 28 my ($class) = ($user =~ /(^[^@]+)/); … … 30 30 my $opcode = "twitter"; 31 31 my $use_reply_to = 0; 32 33 sub fail { 34 my $msg = shift; 35 undef $twitter; 36 BarnOwl::admin_message('Twitter Error', $msg); 37 die("Twitter Error: $msg\n"); 38 } 39 40 if($Net::Twitter::VERSION >= 2.06) { 41 $use_reply_to = 1; 42 } 32 my $next_service_to_poll = 0; 43 33 44 34 my $desc = <<'END_DESC'; … … 85 75 ); 86 76 77 sub fail { 78 my $msg = shift; 79 undef @twitter_handles; 80 BarnOwl::admin_message('Twitter Error', $msg); 81 die("Twitter Error: $msg\n"); 82 } 83 87 84 my $conffile = BarnOwl::get_config_dir() . "/twitter"; 88 85 open(my $fh, "<", "$conffile") || fail("Unable to read $conffile"); 89 my $ cfg = do {local $/; <$fh>};86 my $raw_cfg = do {local $/; <$fh>}; 90 87 close($fh); 91 88 eval { 92 $ cfg = from_json($cfg);89 $raw_cfg = from_json($raw_cfg); 93 90 }; 94 91 if($@) { 95 fail("Unable to parse ~/.owl/twitter: $@"); 96 } 97 98 my $twitter_args = { username => $cfg->{user} || $user, 99 password => $cfg->{password}, 100 source => 'barnowl', 101 }; 102 if (defined $cfg->{service}) { 103 my $service = $cfg->{service}; 104 $twitter_args->{apiurl} = $service; 105 my $apihost = $service; 106 $apihost =~ s/^\s*http:\/\///; 107 $apihost =~ s/\/.*$//; 108 $apihost .= ':80' unless $apihost =~ /:\d+$/; 109 $twitter_args->{apihost} = $cfg->{apihost} || $apihost; 110 my $apirealm = "Laconica API"; 111 $twitter_args->{apirealm} = $cfg->{apirealm} || $apirealm; 112 } 113 114 $twitter = Net::Twitter->new(%$twitter_args); 115 116 if(!defined($twitter->verify_credentials())) { 117 fail("Invalid twitter credentials"); 118 } 119 120 our $last_poll = 0; 121 our $last_direct_poll = 0; 122 our $last_id = undef; 123 our $last_direct = undef; 124 125 unless(defined($last_id)) { 126 eval { 127 $last_id = $twitter->friends_timeline({count => 1})->[0]{id}; 128 }; 129 $last_id = 0 unless defined($last_id); 130 } 131 132 unless(defined($last_direct)) { 133 eval { 134 $last_direct = $twitter->direct_messages()->[0]{id}; 135 }; 136 $last_direct = 0 unless defined($last_direct); 137 } 138 139 eval { 140 $twitter->{ua}->timeout(1); 141 }; 92 fail("Unable to parse $conffile: $@"); 93 } 94 95 $raw_cfg = [$raw_cfg] unless UNIVERSAL::isa $raw_cfg, "ARRAY"; 96 97 for my $cfg (@$raw_cfg) { 98 my $twitter_args = { username => $cfg->{user} || $user, 99 password => $cfg->{password}, 100 source => 'barnowl', 101 }; 102 if (defined $cfg->{service}) { 103 my $service = $cfg->{service}; 104 $twitter_args->{apiurl} = $service; 105 my $apihost = $service; 106 $apihost =~ s/^\s*http:\/\///; 107 $apihost =~ s/\/.*$//; 108 $apihost .= ':80' unless $apihost =~ /:\d+$/; 109 $twitter_args->{apihost} = $cfg->{apihost} || $apihost; 110 my $apirealm = "Laconica API"; 111 $twitter_args->{apirealm} = $cfg->{apirealm} || $apirealm; 112 } else { 113 $cfg->{service} = 'http://twitter.com'; 114 } 115 116 my $twitter_handle = BarnOwl::Module::Twitter::Handle->new($cfg, %$twitter_args); 117 push @twitter_handles, $twitter_handle; 118 $default_handle = $twitter_handle if (!defined $twitter_handle && exists $cfg->{default_sender} && $cfg->{default_sender}); 119 } 142 120 143 121 sub match { … … 155 133 && match($m->opcode, $opcode) 156 134 && $m->auth eq 'YES') { 157 twitter($m->body); 135 for my $handle (@twitter_handles) { 136 $handle->twitter($m->body); 137 } 158 138 } 159 139 } 160 140 161 141 sub poll_messages { 162 poll_twitter(); 163 poll_direct(); 164 } 165 166 sub twitter_error { 167 my $ratelimit = $twitter->rate_limit_status; 168 unless(defined($ratelimit) && ref($ratelimit) eq 'HASH') { 169 # Twitter's just sucking, sleep for 5 minutes 170 $last_direct_poll = $last_poll = time + 60*5; 171 # die("Twitter seems to be having problems.\n"); 172 return; 173 } 174 if(exists($ratelimit->{remaining_hits}) 175 && $ratelimit->{remaining_hits} <= 0) { 176 $last_direct_poll = $last_poll = $ratelimit->{reset_time_in_seconds}; 177 die("Twitter: ratelimited until " . $ratelimit->{reset_time} . "\n"); 178 } elsif(exists($ratelimit->{error})) { 179 die("Twitter: ". $ratelimit->{error} . "\n"); 180 $last_direct_poll = $last_poll = time + 60*20; 181 } 182 } 183 184 sub poll_twitter { 185 return unless ( time - $last_poll ) >= 60; 186 $last_poll = time; 187 return unless BarnOwl::getvar('twitter:poll') eq 'on'; 188 189 my $timeline = $twitter->friends_timeline( { since_id => $last_id } ); 190 unless(defined($timeline) && ref($timeline) eq 'ARRAY') { 191 twitter_error(); 192 return; 193 }; 194 if ( scalar @$timeline ) { 195 for my $tweet ( reverse @$timeline ) { 196 if ( $tweet->{id} <= $last_id ) { 197 next; 142 my $handle = $twitter_handles[$next_service_to_poll]; 143 $next_service_to_poll = ($next_service_to_poll + 1) % scalar(@twitter_handles); 144 145 $handle->poll_twitter() if (!exists $handle->{cfg}->{poll_for_tweets} || $handle->{cfg}->{poll_for_tweets}); 146 $handle->poll_direct() if (!exists $handle->{cfg}->{poll_for_dms} || $handle->{cfg}->{poll_for_dms}); 147 } 148 149 sub twitter { 150 my $account = shift; 151 152 my $sent = 0; 153 if (defined $account) { 154 for my $handle (@twitter_handles) { 155 if (defined $handle->{cfg}->{account_nickname} && $account eq $handle->{cfg}->{account_nickname}) { 156 $handle->twitter(@_); 157 $sent = 1; 158 last; 198 159 } 199 my $msg = BarnOwl::Message->new( 200 type => 'Twitter', 201 sender => $tweet->{user}{screen_name}, 202 recipient => $cfg->{user} || $user, 203 direction => 'in', 204 source => decode_entities($tweet->{source}), 205 location => decode_entities($tweet->{user}{location}||""), 206 body => decode_entities($tweet->{text}), 207 status_id => $tweet->{id}, 208 service => $cfg->{service}, 209 ); 210 BarnOwl::queue_message($msg); 211 } 212 $last_id = $timeline->[0]{id}; 213 } else { 214 # BarnOwl::message("No new tweets..."); 215 } 216 } 217 218 sub poll_direct { 219 return unless ( time - $last_direct_poll) >= 120; 220 $last_direct_poll = time; 221 return unless BarnOwl::getvar('twitter:poll') eq 'on'; 222 223 my $direct = $twitter->direct_messages( { since_id => $last_direct } ); 224 unless(defined($direct) && ref($direct) eq 'ARRAY') { 225 twitter_error(); 226 return; 227 }; 228 if ( scalar @$direct ) { 229 for my $tweet ( reverse @$direct ) { 230 if ( $tweet->{id} <= $last_direct ) { 231 next; 160 } 161 BarnOwl::message("No Twitter account named " . $account) unless $sent == 1 162 } 163 else { 164 # broadcast 165 for my $handle (@twitter_handles) { 166 $handle->twitter(@_) if (!exists $handle->{cfg}->{publish_tweets} || $handle->{cfg}->{publish_tweets}); 167 } 168 } 169 } 170 171 sub twitter_direct { 172 my $account = shift; 173 174 my $sent = 0; 175 if (defined $account) { 176 for my $handle (@twitter_handles) { 177 if (defined $handle->{cfg}->{account_nickname} && $account eq $handle->{cfg}->{account_nickname}) { 178 $handle->twitter_direct(@_); 179 $sent = 1; 180 last; 232 181 } 233 my $msg = BarnOwl::Message->new( 234 type => 'Twitter', 235 sender => $tweet->{sender}{screen_name}, 236 recipient => $cfg->{user} || $user, 237 direction => 'in', 238 location => decode_entities($tweet->{sender}{location}||""), 239 body => decode_entities($tweet->{text}), 240 isprivate => 'true', 241 service => $cfg->{service}, 242 ); 243 BarnOwl::queue_message($msg); 244 } 245 $last_direct = $direct->[0]{id}; 246 } else { 247 # BarnOwl::message("No new tweets..."); 248 } 249 } 250 251 sub twitter { 252 my $msg = shift; 253 my $reply_to = shift; 254 255 if($msg =~ m{\Ad\s+([^\s])+(.*)}sm) { 256 twitter_direct($1, $2); 257 } elsif(defined $twitter) { 258 if($use_reply_to && defined($reply_to)) { 259 $twitter->update({ 260 status => $msg, 261 in_reply_to_status_id => $reply_to 262 }); 263 } else { 264 $twitter->update($msg); 265 } 266 } 267 } 268 269 sub twitter_direct { 270 my $who = shift; 271 my $msg = shift; 272 if(defined $twitter) { 273 $twitter->new_direct_message({ 274 user => $who, 275 text => $msg 276 }); 277 if(BarnOwl::getvar("displayoutgoing") eq 'on') { 278 my $tweet = BarnOwl::Message->new( 279 type => 'Twitter', 280 sender => $cfg->{user} || $user, 281 recipient => $who, 282 direction => 'out', 283 body => $msg, 284 isprivate => 'true', 285 service => $cfg->{service}, 286 ); 287 BarnOwl::queue_message($tweet); 288 } 182 } 183 BarnOwl::message("No Twitter account named " . $account) unless $sent == 1 184 } 185 elsif (defined $default_handle) { 186 $default_handle->twitter_direct(@_); 187 } 188 else { 189 $twitter_handles[0]->twitter_direct(@_); 289 190 } 290 191 } 291 192 292 193 sub twitter_atreply { 293 my $to = shift; 294 my $id = shift; 295 my $msg = shift; 296 if(defined($id)) { 297 twitter("@".$to." ".$msg, $id); 298 } else { 299 twitter("@".$to." ".$msg); 194 my $account = shift; 195 196 my $sent = 0; 197 if (defined $account) { 198 for my $handle (@twitter_handles) { 199 if (defined $handle->{cfg}->{account_nickname} && $account eq $handle->{cfg}->{account_nickname}) { 200 $handle->twitter_atreply(@_); 201 $sent = 1; 202 last; 203 } 204 } 205 BarnOwl::message("No Twitter account named " . $account) unless $sent == 1 206 } 207 elsif (defined $default_handle) { 208 $default_handle->twitter_atreply(@_); 209 } 210 else { 211 $twitter_handles[0]->twitter_atreply(@_); 300 212 } 301 213 } … … 303 215 BarnOwl::new_command(twitter => \&cmd_twitter, { 304 216 summary => 'Update Twitter from BarnOwl', 305 usage => 'twitter [message]', 306 description => 'Update Twitter. If MESSAGE is provided, use it as your status.' 217 usage => 'twitter [ACCOUNT] [MESSAGE]', 218 description => 'Update Twitter on ACCOUNT. If MESSAGE is provided, use it as your status.' 219 . "\nIf no ACCOUNT is provided, update all services which have publishing enabled." 307 220 . "\nOtherwise, prompt for a status message to use." 308 221 }); … … 310 223 BarnOwl::new_command('twitter-direct' => \&cmd_twitter_direct, { 311 224 summary => 'Send a Twitter direct message', 312 usage => 'twitter-direct USER', 313 description => 'Send a Twitter Direct Message to USER' 225 usage => 'twitter-direct USER [ACCOUNT]', 226 description => 'Send a Twitter Direct Message to USER on ACCOUNT (defaults to default_sender,' 227 . "\nor first service if no default is provided)" 314 228 }); 315 229 … … 317 231 { 318 232 summary => 'Send a Twitter @ message', 319 usage => 'twitter-atreply USER', 320 description => 'Send a Twitter @reply Message to USER' 233 usage => 'twitter-atreply USER [ACCOUNT]', 234 description => 'Send a Twitter @reply Message to USER on ACCOUNT (defaults to default_sender,' 235 . "or first service if no default is provided)" 321 236 } 322 237 ); … … 325 240 sub cmd_twitter { 326 241 my $cmd = shift; 327 if(@_) { 328 my $status = join(" ", @_); 329 twitter($status); 330 } else { 331 BarnOwl::start_edit_win('What are you doing?', \&twitter); 332 } 242 my $account = shift; 243 if (defined $account) { 244 if(@_) { 245 my $status = join(" ", @_); 246 twitter($account, $status); 247 return; 248 } 249 } 250 BarnOwl::start_edit_win('What are you doing?' . (defined $account ? " ($account)" : ""), sub{twitter($account, shift)}); 333 251 } 334 252 … … 337 255 my $user = shift; 338 256 die("Usage: $cmd USER\n") unless $user; 339 BarnOwl::start_edit_win("$cmd $user", sub{twitter_direct($user, shift)}); 257 my $account = shift; 258 BarnOwl::start_edit_win("$cmd $user" . (defined $account ? " $account" : ""), sub{twitter_direct($account, $user, shift)}); 340 259 } 341 260 … … 344 263 my $user = shift || die("Usage: $cmd USER [In-Reply-To ID]\n"); 345 264 my $id = shift; 346 BarnOwl::start_edit_win("Reply to \@" . $user, sub { twitter_atreply($user, $id, shift) }); 265 my $account = shift; 266 BarnOwl::start_edit_win("Reply to \@" . $user . (defined $account ? " on $account" : ""), sub { twitter_atreply($account, $user, $id, shift) }); 347 267 } 348 268
Note: See TracChangeset
for help on using the changeset viewer.