9 $Data::Dumper::Indent = 1;
 
  12     $ENV{JSON_ANY_ORDER} = "JSON Syck DWIW";
 
  19 use vars qw($VERSION %IRSSI);
 
  22 my ($REV) = '$Rev: 354 $' =~ /(\d+)/;
 
  24     authors     => 'Dan Boger',
 
  25     contact     => 'zigdon@gmail.com',
 
  27     description => 'Send twitter updates using /tweet.  '
 
  28       . 'Can optionally set your bitlbee /away message to same',
 
  29     license => 'GNU GPL v2',
 
  30     url     => 'http://tinyurl.com/twirssi',
 
  31     changed => '$Date: 2009-01-08 14:46:04 -0800 (Thu, 08 Jan 2009) $',
 
  41 my $last_poll = time - 300;
 
  46     my ( $data, $server, $win ) = @_;
 
  48     return unless &logged_in($twit);
 
  50     my ( $target, $text ) = split ' ', $data, 2;
 
  51     unless ( $target and $text ) {
 
  52         ¬ice("Usage: /dm <nick> <message>");
 
  56     &cmd_direct_as( "$user $data", $server, $win );
 
  60     my ( $data, $server, $win ) = @_;
 
  62     return unless &logged_in($twit);
 
  64     my ( $username, $target, $text ) = split ' ', $data, 3;
 
  65     unless ( $username and $target and $text ) {
 
  66         ¬ice("Usage: /dm_as <username> <nick> <message>");
 
  70     return unless &valid_username($username);
 
  73         unless ( $twits{$username}
 
  74             ->new_direct_message( { user => $target, text => $text } ) )
 
  76             ¬ice("DM to $target failed");
 
  82         ¬ice("DM caused an error.  Aborted");
 
  86     ¬ice("DM sent to $target");
 
  87     $nicks{$target} = time;
 
  91     my ( $data, $server, $win ) = @_;
 
  93     return unless &logged_in($twit);
 
  95     $data =~ s/^\s+|\s+$//;
 
  97         ¬ice("Usage: /tweet <update>");
 
 101     &cmd_tweet_as( "$user $data", $server, $win );
 
 105     my ( $data, $server, $win ) = @_;
 
 107     return unless &logged_in($twit);
 
 109     $data =~ s/^\s+|\s+$//;
 
 110     my ( $username, $data ) = split ' ', $data, 2;
 
 112     unless ( $username and $data ) {
 
 113         ¬ice("Usage: /tweet_as <username> <update>");
 
 117     return unless &valid_username($username);
 
 119     if ( Irssi::settings_get_str("short_url_provider") ) {
 
 120         foreach my $url ( $data =~ /(https?:\/\/\S+[\w\/])/g ) {
 
 122                 my $short = makeashorterlink($url);
 
 123                 $data =~ s/\Q$url/$short/g;
 
 128     return if &too_long($data);
 
 131         unless ( $twits{$username}->update($data) )
 
 133             ¬ice("Update failed");
 
 139         ¬ice("Update caused an error.  Aborted.");
 
 143     foreach ( $data =~ /@([-\w]+)/ ) {
 
 147     my $away = &update_away($data);
 
 149     ¬ice( "Update sent" . ( $away ? " (and away msg set)" : "" ) );
 
 153     my ( $data, $server, $win ) = @_;
 
 155     return unless &logged_in($twit);
 
 157     $data =~ s/^\s+|\s+$//;
 
 159         ¬ice("Usage: /reply <nick[:num]> <update>");
 
 163     $data =~ s/^\s+|\s+$//;
 
 164     my ( $id, $data ) = split ' ', $data, 2;
 
 165     unless ( $id and $data ) {
 
 166         ¬ice("Usage: /reply_as <nick[:num]> <update>");
 
 170     &cmd_reply_as( "$user $id $data", $server, $win );
 
 174     my ( $data, $server, $win ) = @_;
 
 176     unless ( Irssi::settings_get_bool("twirssi_track_replies") ) {
 
 177         ¬ice("twirssi_track_replies is required in order to reply to "
 
 178               . "specific tweets.  Either enable it, or just use /tweet "
 
 179               . "\@username <text>." );
 
 183     return unless &logged_in($twit);
 
 185     $data =~ s/^\s+|\s+$//;
 
 186     my ( $username, $id, $data ) = split ' ', $data, 3;
 
 188     unless ( $username and $data ) {
 
 189         ¬ice("Usage: /reply_as <username> <nick[:num]> <update>");
 
 193     return unless &valid_username($username);
 
 196     $id =~ s/[^\w\d\-:]+//g;
 
 197     ( $nick, $id ) = split /:/, $id;
 
 198     unless ( exists $id_map{ lc $nick } ) {
 
 199         ¬ice("Can't find a tweet from $nick to reply to!");
 
 203     $id = $id_map{__indexes}{$nick} unless $id;
 
 204     unless ( $id_map{ lc $nick }[$id] ) {
 
 205         ¬ice("Can't find a tweet numbered $id from $nick to reply to!");
 
 209     # remove any @nick at the beginning of the reply, as we'll add it anyway
 
 210     $data =~ s/^\s*\@?$nick\s*//;
 
 211     $data = "\@$nick " . $data;
 
 213     if ( Irssi::settings_get_str("short_url_provider") ) {
 
 214         foreach my $url ( $data =~ /(https?:\/\/\S+[\w\/])/g ) {
 
 216                 my $short = makeashorterlink($url);
 
 217                 $data =~ s/\Q$url/$short/g;
 
 222     return if &too_long($data);
 
 226             $twits{$username}->update(
 
 229                     in_reply_to_status_id => $id_map{ lc $nick }[$id]
 
 234             ¬ice("Update failed");
 
 240         ¬ice("Update caused an error.  Aborted");
 
 244     foreach ( $data =~ /@([-\w]+)/ ) {
 
 248     my $away = &update_away($data);
 
 250     ¬ice( "Update sent" . ( $away ? " (and away msg set)" : "" ) );
 
 254     my ( $usage_str, $api_name, $post_ref ) = @_;
 
 257         my ( $data, $server, $win ) = @_;
 
 259         return unless &logged_in($twit);
 
 261         $data =~ s/^\s+|\s+$//;
 
 263             ¬ice("Usage: $usage_str");
 
 268             unless ( $twit->$api_name($data) )
 
 270                 ¬ice("$api_name failed");
 
 276             ¬ice("$api_name caused an error.  Aborted.");
 
 280         &$post_ref($data) if $post_ref;
 
 285     my ( $data, $server, $win ) = @_;
 
 287     $data =~ s/^\s+|\s+$//g;
 
 288     if ( exists $twits{$data} ) {
 
 289         ¬ice("Switching to $data");
 
 290         $twit = $twits{$data};
 
 293         ¬ice("Unknown user $data");
 
 298     my ( $data, $server, $win ) = @_;
 
 300     $data =~ s/^\s+|\s+$//g;
 
 301     return unless &valid_username($data);
 
 304         ¬ice("Logging out $data...");
 
 305         $twits{$data}->end_session();
 
 306         delete $twits{$data};
 
 308         ¬ice("Logging out $user...");
 
 309         $twit->end_session();
 
 311         delete $twits{$user};
 
 313             &cmd_switch( ( keys %twits )[0], $server, $win );
 
 315             Irssi::timeout_remove($poll) if $poll;
 
 322     my ( $data, $server, $win ) = @_;
 
 325         ( $user, $pass ) = split ' ', $data, 2;
 
 326     } elsif ( my $autouser = Irssi::settings_get_str("twitter_usernames")
 
 327         and my $autopass = Irssi::settings_get_str("twitter_passwords") )
 
 329         my @user = split /\s*,\s*/, $autouser;
 
 330         my @pass = split /\s*,\s*/, $autopass;
 
 331         if ( @user != @pass ) {
 
 332             ¬ice("Number of usernames doesn't match "
 
 333                   . "the number of passwords - auto-login failed" );
 
 336             while ( @user and @pass ) {
 
 344         ¬ice("/twitter_login requires either a username and password "
 
 345               . "or twitter_usernames and twitter_passwords to be set." );
 
 349     %friends = %nicks = ();
 
 351     $twit = Net::Twitter->new(
 
 357     unless ( $twit->verify_credentials() ) {
 
 358         ¬ice("Login as $user failed");
 
 361             &cmd_switch( ( keys %twits )[0], $server, $win );
 
 367         my $rate_limit = $twit->rate_limit_status();
 
 368         if ( $rate_limit and $rate_limit->{remaining_hits} < 1 ) {
 
 369             ¬ice("Rate limit exceeded, try again later");
 
 374         $twits{$user} = $twit;
 
 375         Irssi::timeout_remove($poll) if $poll;
 
 376         $poll = Irssi::timeout_add( 300 * 1000, \&get_updates, "" );
 
 377         ¬ice("Logged in as $user, loading friends list...");
 
 379         ¬ice( "loaded friends: ", scalar keys %friends );
 
 380         if ( Irssi::settings_get_bool("twirssi_first_run") ) {
 
 381             Irssi::settings_set_bool( "twirssi_first_run", 0 );
 
 382             unless ( exists $friends{twirssi} ) {
 
 383                 ¬ice("Welcome to twirssi!"
 
 384                       . "  Perhaps you should add \@twirssi to your friends list,"
 
 385                       . " so you can be notified when a new version is release?"
 
 386                       . "  Just type /twitter_friend twirssi." );
 
 393         ¬ice("Login failed");
 
 398     my ( $data, $server, $win ) = @_;
 
 400     my $loc = Irssi::settings_get_str("twirssi_location");
 
 403 "$loc isn't writable, can't upgrade.  Perhaps you need to /set twirssi_location?"
 
 408     if ( not -x "/usr/bin/md5sum" and not $data ) {
 
 410 "/usr/bin/md5sum can't be found - try '/twirssi_upgrade nomd5' to skip MD5 verification"
 
 417         eval { use Digest::MD5; };
 
 421 "Failed to load Digest::MD5.  Try '/twirssi_upgrade nomd5' to skip MD5 verification"
 
 426         $md5 = get("http://twirssi.com/md5sum");
 
 430             ¬ice("Failed to download md5sum from peeron!  Aborting.");
 
 434         unless ( open( CUR, $loc ) ) {
 
 436 "Failed to read $loc.  Check that /set twirssi_location is set to the correct location."
 
 441         my $cur_md5 = Digest::MD5::md5_hex(<CUR>);
 
 444         if ( $cur_md5 eq $md5 ) {
 
 445             ¬ice("Current twirssi seems to be up to date.");
 
 450     my $URL = "http://twirssi.com/twirssi.pl";
 
 451     ¬ice("Downloading twirssi from $URL");
 
 452     LWP::Simple::getstore( $URL, "$loc.upgrade" );
 
 455         unless ( open( NEW, "$loc.upgrade" ) ) {
 
 457 "Failed to read $loc.upgrade.  Check that /set twirssi_location is set to the correct location."
 
 462         my $new_md5 = Digest::MD5::md5_hex(<NEW>);
 
 465         if ( $new_md5 ne $md5 ) {
 
 466             ¬ice("MD5 verification failed. expected $md5, got $new_md5");
 
 471     rename $loc, "$loc.backup"
 
 472       or ¬ice("Failed to back up $loc: $!.  Aborting")
 
 474     rename "$loc.upgrade", $loc
 
 475       or ¬ice("Failed to rename $loc.upgrade: $!.  Aborting")
 
 478     my ( $dir, $file ) = ( $loc =~ m{(.*)/([^/]+)$} );
 
 479     if ( -e "$dir/autorun/$file" ) {
 
 480         ¬ice("Updating $dir/autorun/$file");
 
 481         unlink "$dir/autorun/$file"
 
 482           or ¬ice("Failed to remove old $file from autorun: $!");
 
 483         symlink "../$file", "$dir/autorun/$file"
 
 484           or ¬ice("Failed to create symlink in autorun directory: $!");
 
 487     ¬ice("Download complete.  Reload twirssi with /script load $file");
 
 497             print $fh "type:debug Loading friends page $page...\n"
 
 498               if ( $fh and &debug );
 
 499             my $friends = $twit->friends( { page => $page } );
 
 500             last unless $friends;
 
 501             $new_friends{ $_->{screen_name} } = time foreach @$friends;
 
 503             last if @$friends == 0 or $page == 10;
 
 508         print $fh "type:debug Error during friends list update.  Aborted.\n";
 
 512     my ( $added, $removed ) = ( 0, 0 );
 
 513     print $fh "type:debug Scanning for new friends...\n" if ( $fh and &debug );
 
 514     foreach ( keys %new_friends ) {
 
 515         next if exists $friends{$_};
 
 520     print $fh "type:debug Scanning for removed friends...\n"
 
 521       if ( $fh and &debug );
 
 522     foreach ( keys %friends ) {
 
 523         next if exists $new_friends{$_};
 
 528     return ( $added, $removed );
 
 532     print scalar localtime, " - get_updates starting" if &debug;
 
 535       Irssi::window_find_name( Irssi::settings_get_str('twitter_window') );
 
 538           ->print( "Can't find a window named '"
 
 539               . Irssi::settings_get_str('twitter_window')
 
 540               . "'.  Create it or change the value of twitter_window" );
 
 543     return unless &logged_in($twit);
 
 545     my ( $fh, $filename ) = File::Temp::tempfile();
 
 549         Irssi::timeout_add_once( 5000, 'monitor_child', [ $filename, 0 ] );
 
 550         Irssi::pidwait_add($pid);
 
 551     } elsif ( defined $pid ) {    # child
 
 559         $error += &do_updates( $fh, $user, $twit );
 
 560         foreach ( keys %twits ) {
 
 562             $error += &do_updates( $fh, $_, $twits{$_} );
 
 565         my ( $added, $removed ) = &load_friends($fh);
 
 566         if ( $added + $removed ) {
 
 567             print $fh "type:debug %R***%n Friends list updated: ",
 
 569                 sprintf( "%d added",   $added ),
 
 570                 sprintf( "%d removed", $removed ) ),
 
 573         print $fh "__friends__\n";
 
 574         foreach ( sort keys %friends ) {
 
 575             print $fh "$_ $friends{$_}\n";
 
 579             print $fh "type:debug Update encountered errors.  Aborted\n";
 
 580             print $fh $last_poll;
 
 587     print scalar localtime, " - get_updates ends" if &debug;
 
 591     my ( $fh, $username, $obj ) = @_;
 
 593     print scalar localtime, " - Polling for updates for $username" if &debug;
 
 596         $tweets = $obj->friends_timeline(
 
 597             { since => HTTP::Date::time2str($last_poll) } );
 
 601         print $fh "type:debug Error during friends_timeline call.  Aborted.\n";
 
 605     unless ( ref $tweets ) {
 
 606         if ( $obj->can("get_error") ) {
 
 607             print $fh "type:debug API Error during friends_timeline call: ",
 
 608               JSON::Any->jsonToObj( $obj->get_error() ), "  Aborted.\n";
 
 611               "type:debug API Error during friends_timeline call. Aborted.\n";
 
 616     foreach my $t ( reverse @$tweets ) {
 
 617         my $text = decode_entities( $t->{text} );
 
 619         $text =~ s/(^|\W)\@([-\w]+)/$1%B\@$2%n/g;
 
 620         $text =~ s/[\n\r]/ /g;
 
 622         if (    Irssi::settings_get_bool("show_reply_context")
 
 623             and $t->{in_reply_to_screen_name} ne $username
 
 624             and $t->{in_reply_to_screen_name}
 
 625             and not exists $friends{ $t->{in_reply_to_screen_name} } )
 
 627             $nicks{ $t->{in_reply_to_screen_name} } = time;
 
 630                 $context = $obj->show_status( $t->{in_reply_to_status_id} );
 
 634                 my $ctext = decode_entities( $context->{text} );
 
 636                 $ctext =~ s/(^|\W)\@([-\w]+)/$1%B\@$2%n/g;
 
 637                 $ctext =~ s/[\n\r]/ /g;
 
 638                 printf $fh "id:%d account:%s nick:%s type:tweet %s\n",
 
 639                   $context->{id}, $username,
 
 640                   $context->{user}{screen_name}, $ctext;
 
 643                 print $fh "type:debug request to get context failed: $@";
 
 646 "type:debug Failed to get context from $t->{in_reply_to_screen_name}"
 
 651           if $t->{user}{screen_name} eq $username
 
 652               and not Irssi::settings_get_bool("show_own_tweets");
 
 653         printf $fh "id:%d account:%s nick:%s type:%s %s\n",
 
 654           $t->{id}, $username, $t->{user}{screen_name}, $reply, $text;
 
 657     print scalar localtime, " - Polling for replies" if &debug;
 
 659         $tweets = $obj->replies( { since => HTTP::Date::time2str($last_poll) } )
 
 664         print $fh "type:debug Error during replies call.  Aborted.\n";
 
 668     foreach my $t ( reverse @$tweets ) {
 
 670           if exists $friends{ $t->{user}{screen_name} };
 
 672         my $text = decode_entities( $t->{text} );
 
 674         $text =~ s/(^|\W)\@([-\w]+)/$1%B\@$2%n/g;
 
 675         $text =~ s/[\n\r]/ /g;
 
 676         printf $fh "id:%d account:%s nick:%s type:tweet %s\n",
 
 677           $t->{id}, $username, $t->{user}{screen_name}, $text;
 
 680     print scalar localtime, " - Polling for DMs" if &debug;
 
 683           $obj->direct_messages( { since => HTTP::Date::time2str($last_poll) } )
 
 688         print $fh "type:debug Error during direct_messages call.  Aborted.\n";
 
 692     foreach my $t ( reverse @$tweets ) {
 
 693         my $text = decode_entities( $t->{text} );
 
 695         $text =~ s/(^|\W)\@([-\w]+)/$1%B\@$2%n/g;
 
 696         $text =~ s/[\n\r]/ /g;
 
 697         printf $fh "id:%d account:%s nick:%s type:dm %s\n",
 
 698           $t->{id}, $username, $t->{sender_screen_name}, $text;
 
 700     print scalar localtime, " - Done" if &debug;
 
 707     my $filename = $data->[0];
 
 708     my $attempt  = $data->[1];
 
 710     print scalar localtime, " - checking child log at $filename ($attempt)"
 
 713     if ( open FILE, $filename ) {
 
 717             last if /^__friends__/;
 
 719             foreach my $key (qw/id account nick type/) {
 
 720                 if (s/^$key:(\S+)\s*//) {
 
 725             next if exists $meta{id} and exists $tweet_cache{ $meta{id} };
 
 726             $tweet_cache{ $meta{id} } = time;
 
 728             if ( $meta{account} ne $user ) {
 
 729                 $account = "$meta{account}: ";
 
 733             if (    $meta{type} ne 'dm'
 
 734                 and Irssi::settings_get_bool("twirssi_track_replies")
 
 738                 $marker = ( $id_map{__indexes}{ $meta{nick} } + 1 ) % 100;
 
 739                 $id_map{ lc $meta{nick} }[$marker] = $meta{id};
 
 740                 $id_map{__indexes}{ $meta{nick} }  = $marker;
 
 741                 $marker                            = ":$marker";
 
 744             if ( $meta{type} eq 'tweet' ) {
 
 745                 push @lines, "[$account%B\@$meta{nick}%n$marker] $_\n",;
 
 746             } elsif ( $meta{type} eq 'reply' ) {
 
 747                 push @lines, "[$account\\--> %B\@$meta{nick}%n$marker] $_\n",;
 
 748             } elsif ( $meta{type} eq 'dm' ) {
 
 749                 push @lines, "[$account%B\@$meta{nick}%n (%WDM%n)] $_\n",;
 
 750             } elsif ( $meta{type} eq 'error' ) {
 
 751                 push @lines, "ERROR: $_\n";
 
 752             } elsif ( $meta{type} eq 'debug' ) {
 
 753                 print "$_" if &debug,;
 
 755                 print "Unknown line type $meta{type}: $_" if &debug,;
 
 765             my ( $f, $t ) = split ' ', $_;
 
 766             $nicks{$f} = $friends{$f} = $t;
 
 769         if ($new_last_poll) {
 
 770             print "new last_poll = $new_last_poll" if &debug;
 
 771             foreach my $line (@lines) {
 
 773                 $window->print( $line, MSGLEVEL_PUBLIC );
 
 774                 foreach ( $line =~ /\@([-\w]+)/ ) {
 
 781               or warn "Failed to remove $filename: $!"
 
 784             # keep enough cached tweets, to make sure we don't show duplicates.
 
 785             foreach ( keys %tweet_cache ) {
 
 786                 next if $tweet_cache{$_} >= $last_poll;
 
 787                 delete $tweet_cache{$_};
 
 789             $last_poll = $new_last_poll;
 
 794                 Irssi::settings_get_str("twirssi_replies_store") )
 
 796                 if ( open JSON, ">$file" ) {
 
 797                     print JSON JSON::Any->objToJson( \%id_map );
 
 800                     ¬ice("Failed to write replies to $file: $!");
 
 809     if ( $attempt < 12 ) {
 
 810         Irssi::timeout_add_once( 5000, 'monitor_child',
 
 811             [ $filename, $attempt + 1 ] );
 
 813         ¬ice("Giving up on polling $filename");
 
 814         unlink $filename unless &debug;
 
 819     return Irssi::settings_get_bool("twirssi_debug");
 
 823     $window->print( "%R***%n @_", MSGLEVEL_PUBLIC );
 
 829     if (    Irssi::settings_get_bool("tweet_to_away")
 
 831         and $data !~ /^[dD] / )
 
 834           Irssi::server_find_tag( Irssi::settings_get_str("bitlbee_server") );
 
 836             $server->send_raw("away :$data");
 
 839             ¬ice( "Can't find bitlbee server.",
 
 840                 "Update bitlbee_server or disable tweet_to_away" );
 
 851     if ( length $data > 140 ) {
 
 853             "Tweet too long (" . length($data) . " characters) - aborted" );
 
 861     my $username = shift;
 
 863     unless ( exists $twits{$username} ) {
 
 864         ¬ice("Unknown username $username");
 
 874         ¬ice("Not logged in!  Use /twitter_login username pass!");
 
 882     my ( $complist, $window, $word, $linestart, $want_space ) = @_;
 
 885         $linestart =~ /^\/twitter_reply(?:_as)?\s*$/
 
 886         or ( Irssi::settings_get_bool("twirssi_use_reply_aliases")
 
 887             and $linestart =~ /^\/reply(?:_as)?\s*$/ )
 
 889     {    # /twitter_reply gets a nick:num
 
 890         @$complist = grep /^\Q$word/i, sort keys %{ $id_map{__indexes} };
 
 893     # /tweet, /tweet_as, /dm, /dm_as - complete @nicks (and nicks as the first
 
 895     if ( $linestart =~ /^\/(?:tweet|dm)/ ) {
 
 896         my $prefix = $word =~ s/^@//;
 
 897         $prefix = 0 if $linestart eq '/dm' or $linestart eq '/dm_as';
 
 898         push @$complist, grep /^\Q$word/i,
 
 899           sort { $nicks{$b} <=> $nicks{$a} } keys %nicks;
 
 900         @$complist = map { "\@$_" } @$complist if $prefix;
 
 904 sub event_send_text {
 
 905     my ( $line, $server, $win ) = @_;
 
 906     my $awin = Irssi::active_win();
 
 908     # if the window where we got our text was the twitter window, and the user
 
 909     # wants to be lazy, tweet away!
 
 910     if ( ($awin->get_active_name() eq $window->{name})
 
 911          and Irssi::settings_get_bool("tweet_window_input") ) {
 
 912         &cmd_tweet($line, $server, $win);
 
 916 Irssi::signal_add( "send text", "event_send_text" );
 
 918 Irssi::settings_add_str( "twirssi", "twitter_window",     "twitter" );
 
 919 Irssi::settings_add_str( "twirssi", "bitlbee_server",     "bitlbee" );
 
 920 Irssi::settings_add_str( "twirssi", "short_url_provider", "TinyURL" );
 
 921 Irssi::settings_add_str( "twirssi", "twirssi_location",
 
 922     ".irssi/scripts/twirssi.pl" );
 
 923 Irssi::settings_add_str( "twirssi", "twitter_usernames", undef );
 
 924 Irssi::settings_add_str( "twirssi", "twitter_passwords", undef );
 
 925 Irssi::settings_add_str( "twirssi", "twirssi_replies_store",
 
 926     ".irssi/scripts/twirssi.json" );
 
 927 Irssi::settings_add_bool( "twirssi", "tweet_to_away",             0 );
 
 928 Irssi::settings_add_bool( "twirssi", "show_reply_context",        0 );
 
 929 Irssi::settings_add_bool( "twirssi", "show_own_tweets",           1 );
 
 930 Irssi::settings_add_bool( "twirssi", "twirssi_debug",             0 );
 
 931 Irssi::settings_add_bool( "twirssi", "twirssi_first_run",         1 );
 
 932 Irssi::settings_add_bool( "twirssi", "twirssi_track_replies",     1 );
 
 933 Irssi::settings_add_bool( "twirssi", "twirssi_use_reply_aliases", 0 );
 
 934 Irssi::settings_add_bool( "twirssi", "tweet_window_input",        0 );
 
 935 $window = Irssi::window_find_name( Irssi::settings_get_str('twitter_window') );
 
 938     Irssi::command_bind( "dm",               "cmd_direct" );
 
 939     Irssi::command_bind( "dm_as",            "cmd_direct_as" );
 
 940     Irssi::command_bind( "tweet",            "cmd_tweet" );
 
 941     Irssi::command_bind( "tweet_as",         "cmd_tweet_as" );
 
 942     Irssi::command_bind( "twitter_reply",    "cmd_reply" );
 
 943     Irssi::command_bind( "twitter_reply_as", "cmd_reply_as" );
 
 944     Irssi::command_bind( "twitter_login",    "cmd_login" );
 
 945     Irssi::command_bind( "twitter_logout",   "cmd_logout" );
 
 946     Irssi::command_bind( "twitter_switch",   "cmd_switch" );
 
 947     Irssi::command_bind( "twirssi_upgrade",  "cmd_upgrade" );
 
 948     if ( Irssi::settings_get_bool("twirssi_use_reply_aliases") ) {
 
 949         Irssi::command_bind( "reply",    "cmd_reply" );
 
 950         Irssi::command_bind( "reply_as", "cmd_reply_as" );
 
 955             print "twits: ", join ", ",
 
 956               map { "u: $_->{username}" } values %twits;
 
 957             print "friends: ", join ", ", sort keys %friends;
 
 958             print "nicks: ",   join ", ", sort keys %nicks;
 
 959             print "id_map: ", Dumper \%{ $id_map{__indexes} };
 
 960             print "last poll: $last_poll";
 
 966             ¬ice("Twirssi v$VERSION (r$REV); "
 
 967                   . "Net::Twitter v$Net::Twitter::VERSION. "
 
 969                   . JSON::Any::handler()
 
 970                   . ".  See details at http://twirssi.com/" );
 
 976             "/twitter_friend <username>",
 
 978             sub { ¬ice("Following $_[0]"); $nicks{ $_[0] } = time; }
 
 984             "/twitter_unfriend <username>",
 
 986             sub { ¬ice("Stopped following $_[0]"); delete $nicks{ $_[0] }; }
 
 989     Irssi::command_bind( "twitter_updates", "get_updates" );
 
 990     Irssi::signal_add_last( 'complete word' => \&sig_complete );
 
 992     ¬ice("  %Y<%C(%B^%C)%N                   TWIRSSI v%R$VERSION%N (r$REV)");
 
 993     ¬ice("   %C(_(\\%N           http://twirssi.com/ for full docs");
 
 995         "    %Y||%C `%N Log in with /twitter_login, send updates with /tweet");
 
 997     my $file = Irssi::settings_get_str("twirssi_replies_store");
 
 998     if ( $file and -r $file ) {
 
 999         if ( open( JSON, $file ) ) {
 
1004                 my $ref = JSON::Any->jsonToObj($json);
 
1006                 my $num = keys %{ $id_map{__indexes} };
 
1007                 ¬ice( sprintf "Loaded old replies from %d contact%s.",
 
1008                     $num, ( $num == 1 ? "" : "s" ) );
 
1011             ¬ice("Failed to load old replies from $file: $!");
 
1015     if ( my $provider = Irssi::settings_get_str("short_url_provider") ) {
 
1016         eval "use WWW::Shorten::$provider;";
 
1020 "Failed to load WWW::Shorten::$provider - either clear short_url_provider or install the CPAN module"
 
1025     if (    my $autouser = Irssi::settings_get_str("twitter_usernames")
 
1026         and my $autopass = Irssi::settings_get_str("twitter_passwords") )
 
1033       ->print( "Create a window named "
 
1034           . Irssi::settings_get_str('twitter_window')
 
1035           . " or change the value of twitter_window.  Then, reload twirssi." );