Changeset 159aaad
- 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)
- Files:
-
- 1 added
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
README
r8496cc2 r159aaad 25 25 also need to set 'apihost' and 'apirealm'. See Net::Twitter or your 26 26 blogging service's documentation for more information. 27 28 Twitter.par also supports using multiple Twitter and Twitter-compatible 29 microblogging accounts from the same instance. To enable this, add 30 additional hashes to your ~/.owl/twitter file (wrapped in a JSON list, 31 eg. [{"account_nickname":"twitter","user":"nelhage","password":"sekrit"}, 32 {"account_nickname":"identica","service":"http://identi.ca/api", 33 "user":"nelhage","password":"sekriter"}] 34 35 By default, public messages (excluding at-replies) are sent to all 36 accounts listed except those with publish_tweets set to false. 37 38 There are several additional account-specific parameters that control the 39 behavior of Twitter.par when using multiple accounts: 40 41 * account_nickname (string, required if multiple accounts are in use) 42 Specify a short name by which you can refer to the account, eg. 43 "identica" (eg. :twitter-direct nelhage identica would send a direct 44 message to @nelhage on identi.ca from your account nicknamed "identica"). 45 * default_sender (boolean, default false) 46 If true, direct messages and at-replies you send without specifying an 47 account will be sent using this account. If no account has this parameter, 48 such messages will be sent using the first account listed. 49 * poll_for_tweets (boolean, default true) 50 If true, tweets sent by your friends on this account will be displayed 51 in your BarnOwl message list. 52 * poll_for_dms (boolean, default true) 53 If true, Direct Messages sent to you by your friends on this account 54 will be displayed in your BarnOwl message list. 55 * publish_tweets (boolean, default true) 56 If true, tweets you send without specifying an account (either with the 57 :twitter command or mirrored from a zephyr class) will be published to 58 this account. -
lib/BarnOwl/Message/Twitter.pm
ra0385ad3 r159aaad 16 16 sub subcontext {undef} 17 17 sub service { return (shift->{"service"} || "http://twitter.com"); } 18 sub account { return shift->{"account"}; } 18 19 sub long_sender { 19 20 my $self = shift; … … 28 29 return $self->replysendercmd; 29 30 } elsif(exists($self->{status_id})) { 30 return 'twitter-atreply ' . $self->sender . " " . $self->{status_id} ;31 return 'twitter-atreply ' . $self->sender . " " . $self->{status_id} . " " . $self->account; 31 32 } else { 32 return 'twitter-atreply ' . $self->sender ;33 return 'twitter-atreply ' . $self->sender . " " . $self->account; 33 34 } 34 35 } … … 36 37 sub replysendercmd { 37 38 my $self = shift; 38 return 'twitter-direct ' . $self->sender ;39 return 'twitter-direct ' . $self->sender . " " . $self->account; 39 40 } 40 41 -
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.