]> git.sommitrealweird.co.uk Git - twirssi-net-twitter-lite.git/blobdiff - twirssi.pl
r615 - Release v2.2.2
[twirssi-net-twitter-lite.git] / twirssi.pl
index 8d5944c73ae976e1b268d7f5c045359de5de7eb1..0dd07ca1a8d31cdc368085a47464b65a171b4c65 100644 (file)
@@ -10,8 +10,8 @@ $Data::Dumper::Indent = 1;
 
 use vars qw($VERSION %IRSSI);
 
-$VERSION = "2.1.3beta";
-my ($REV) = '$Rev: 550 $' =~ /(\d+)/;
+$VERSION = "2.2.2";
+my ($REV) = '$Rev: 615 $' =~ /(\d+)/;
 %IRSSI = (
     authors     => 'Dan Boger',
     contact     => 'zigdon@gmail.com',
@@ -20,7 +20,7 @@ my ($REV) = '$Rev: 550 $' =~ /(\d+)/;
       . 'Can optionally set your bitlbee /away message to same',
     license => 'GNU GPL v2',
     url     => 'http://twirssi.com',
-    changed => '$Date: 2009-03-14 14:10:35 -0700 (Sat, 14 Mar 2009) $',
+    changed => '$Date: 2009-04-20 20:26:08 -0700 (Mon, 20 Apr 2009) $',
 );
 
 my $window;
@@ -104,6 +104,105 @@ sub cmd_direct_as {
     }
 }
 
+sub cmd_retweet {
+    my ( $data, $server, $win ) = @_;
+
+    return unless &logged_in($twit);
+
+    $data =~ s/^\s+|\s+$//;
+    unless ($data) {
+        &notice("Usage: /retweet <nick[:num]> [comment]");
+        return;
+    }
+
+    my ( $id, $data ) = split ' ', $data, 2;
+
+    &cmd_retweet_as( "$user $id $data", $server, $win );
+}
+
+sub cmd_retweet_as {
+    my ( $data, $server, $win ) = @_;
+
+    unless ( Irssi::settings_get_bool("twirssi_track_replies") ) {
+        &notice("twirssi_track_replies is required in order to reteet.");
+        return;
+    }
+
+    return unless &logged_in($twit);
+
+    $data =~ s/^\s+|\s+$//;
+    my ( $username, $id, $data ) = split ' ', $data, 3;
+
+    unless ($username) {
+        &notice("Usage: /retweet_as <username> <nick[:num]> [comment]");
+        return;
+    }
+
+    return unless $username = &valid_username($username);
+
+    my $nick;
+    $id =~ s/[^\w\d\-:]+//g;
+    ( $nick, $id ) = split /:/, $id;
+    unless ( exists $id_map{ lc $nick } ) {
+        &notice("Can't find a tweet from $nick to retweet!");
+        return;
+    }
+
+    $id = $id_map{__indexes}{$nick} unless $id;
+    unless ( $id_map{ lc $nick }[$id] ) {
+        &notice("Can't find a tweet numbered $id from $nick to retweet!");
+        return;
+    }
+
+    unless ( $id_map{__tweets}{ lc $nick }[$id] ) {
+        &notice("The text of this tweet isn't saved, sorry!");
+        return;
+    }
+
+# Irssi::settings_add_str( "twirssi", "twirssi_retweet_format", 'RT $n: $t ${-- $c$}' );
+    my $text = Irssi::settings_get_str("twirssi_retweet_format");
+    $text =~ s/\$n/\@$nick/g;
+    if ($data) {
+        $text =~ s/\${|\$}//g;
+        $text =~ s/\$c/$data/;
+    } else {
+        $text =~ s/\${.*?\$}//;
+    }
+    $text =~ s/\$t/$id_map{__tweets}{ lc $nick }[$id]/;
+
+    $data = &shorten($text);
+
+    return if &too_long($data);
+
+    my $success = 1;
+    eval {
+        unless (
+            $twits{$username}->update(
+                {
+                    status                => $data,
+                    in_reply_to_status_id => $id_map{ lc $nick }[$id]
+                }
+            )
+          )
+        {
+            &notice("Update failed");
+            $success = 0;
+        }
+    };
+    return unless $success;
+
+    if ($@) {
+        &notice("Update caused an error: $@.  Aborted");
+        return;
+    }
+
+    foreach ( $data =~ /@([-\w]+)/ ) {
+        $nicks{$1} = time;
+    }
+
+    &notice("Retweet sent");
+}
+
 sub cmd_tweet {
     my ( $data, $server, $win ) = @_;
 
@@ -173,10 +272,9 @@ sub cmd_reply {
         return;
     }
 
-    $data =~ s/^\s+|\s+$//;
     my ( $id, $data ) = split ' ', $data, 2;
     unless ( $id and $data ) {
-        &notice("Usage: /reply_as <nick[:num]> <update>");
+        &notice("Usage: /reply <nick[:num]> <update>");
         return;
     }
 
@@ -454,12 +552,12 @@ sub cmd_add_search {
         return;
     }
 
-    if ( exists $id_map{__searches}{$user}{$data} ) {
+    if ( exists $id_map{__searches}{"$user\@$defservice"}{$data} ) {
         &notice("Already had a subscription for '$data'");
         return;
     }
 
-    $id_map{__searches}{$user}{$data} = 1;
+    $id_map{__searches}{"$user\@$defservice"}{$data} = 1;
     &notice("Added subscription for '$data'");
 }
 
@@ -479,12 +577,12 @@ sub cmd_del_search {
         return;
     }
 
-    unless ( exists $id_map{__searches}{$user}{$data} ) {
+    unless ( exists $id_map{__searches}{"$user\@$defservice"}{$data} ) {
         &notice("No subscription found for '$data'");
         return;
     }
 
-    delete $id_map{__searches}{$user}{$data};
+    delete $id_map{__searches}{"$user\@$defservice"}{$data};
     &notice("Removed subscription for '$data'");
 }
 
@@ -667,8 +765,9 @@ sub get_updates {
         my $new_poll = time;
 
         my $error = 0;
+        my %context_cache;
         foreach ( keys %twits ) {
-            $error += &do_updates( $fh, $_, $twits{$_} );
+            $error++ unless &do_updates( $fh, $_, $twits{$_}, \%context_cache );
         }
 
         my ( $added, $removed ) = &load_friends($fh);
@@ -686,9 +785,9 @@ sub get_updates {
 
         if ($error) {
             print $fh "type:debug Update encountered errors.  Aborted\n";
-            print $fh $last_poll;
+            print $fh "-- $last_poll";
         } else {
-            print $fh $new_poll;
+            print $fh "-- $new_poll";
         }
         close $fh;
         exit;
@@ -697,36 +796,46 @@ sub get_updates {
 }
 
 sub do_updates {
-    my ( $fh, $username, $obj ) = @_;
+    my ( $fh, $username, $obj, $cache ) = @_;
 
     my $rate_limit = $obj->rate_limit_status();
     if ( $rate_limit and $rate_limit->{remaining_hits} < 1 ) {
         &notice("Rate limit exceeded for $username");
-        return 1;
+        return undef;
     }
 
     print scalar localtime, " - Polling for updates for $username" if &debug;
     my $tweets;
-    eval { $tweets = $obj->friends_timeline(); };
+    my $new_poll_id = 0;
+    eval {
+        if ( $id_map{__last_id}{$username}{timeline} )
+        {
+            $tweets = $obj->friends_timeline( { count => 100 } );
+        } else {
+            $tweets = $obj->friends_timeline();
+        }
+    };
 
     if ($@) {
-        print $fh
-          "type:debug Error during friends_timeline call: $@.  Aborted.\n";
-        return 1;
+        print $fh "type:debug Error during friends_timeline call: Aborted.\n";
+        print $fh "type:debug : $_\n" foreach split /\n/, Dumper($@);
+        return undef;
     }
 
     unless ( ref $tweets ) {
         if ( $obj->can("get_error") ) {
-            my $error;
+            my $error = "Unknown error";
             eval { $error = JSON::Any->jsonToObj( $obj->get_error() ) };
-            if ($@) { $error = $obj->get_error() }
-            print $fh "type:debug API Error during friends_timeline call: ",
-              "$error  Aborted.\n";
+            unless ($@) { $error = $obj->get_error() }
+            print $fh
+              "type:debug API Error during friends_timeline call: Aborted\n";
+            print $fh "type:debug : $_\n" foreach split /\n/, Dumper($error);
+
         } else {
             print $fh
               "type:debug API Error during friends_timeline call. Aborted.\n";
         }
-        return 1;
+        return undef;
     }
 
     foreach my $t ( reverse @$tweets ) {
@@ -740,53 +849,59 @@ sub do_updates {
         {
             $nicks{ $t->{in_reply_to_screen_name} } = time;
             my $context;
-            eval {
-                $context = $obj->show_status( $t->{in_reply_to_status_id} );
-            };
+            unless ( $cache->{ $t->{in_reply_to_status_id} } ) {
+                eval {
+                    $cache->{ $t->{in_reply_to_status_id} } =
+                      $obj->show_status( $t->{in_reply_to_status_id} );
+                };
+
+            }
+            $context = $cache->{ $t->{in_reply_to_status_id} };
 
             if ($context) {
                 my $ctext = decode_entities( $context->{text} );
                 $ctext = &hilight($ctext);
+                if ( $context->{truncated} and ref($obj) ne 'Net::Identica' ) {
+                    $ctext .=
+                        " -- http://twitter.com/$context->{user}{screen_name}"
+                      . "/status/$context->{id}";
+                }
                 printf $fh "id:%d account:%s nick:%s type:tweet %s\n",
                   $context->{id}, $username,
                   $context->{user}{screen_name}, $ctext;
-                if ( $context->{truncated} and ref($obj) ne 'Net::Identica' ) {
-                    printf $fh "id:%s account:%s nick:%s type:ellispis %s\n",
-                      $context->{id} . "-url", $username,
-                      $context->{user}{screen_name},
-"http://twitter.com/$context->{user}{screen_name}/status/$context->{id}";
-                }
                 $reply = "reply";
-            } elsif ($@) {
-                print $fh "type:debug request to get context failed: $@";
-            } else {
-                print $fh
-"type:debug Failed to get context from $t->{in_reply_to_screen_name}\n"
-                  if &debug;
             }
         }
         next
           if $t->{user}{screen_name} eq $username
               and not Irssi::settings_get_bool("show_own_tweets");
-        printf $fh "id:%d account:%s nick:%s type:%s %s\n",
-          $t->{id}, $username, $t->{user}{screen_name}, $reply, $text;
         if ( $t->{truncated} and ref($obj) ne 'Net::Identica' ) {
-            printf $fh "id:%s account:%s nick:%s type:ellispis %s\n",
-              $t->{id} . "-url", $username,
-              $t->{user}{screen_name},
-              "http://twitter.com/$t->{user}{screen_name}/status/$t->{id}";
+            $text .= " -- http://twitter.com/$t->{user}{screen_name}"
+              . "/status/$t->{id}";
         }
+        printf $fh "id:%d account:%s nick:%s type:%s %s\n",
+          $t->{id}, $username, $t->{user}{screen_name}, $reply, $text;
+        $new_poll_id = $t->{id} if $new_poll_id < $t->{id};
     }
+    printf $fh "id:%d account:%s type:last_id timeline\n",
+      $new_poll_id, $username;
 
     print scalar localtime, " - Polling for replies" if &debug;
+    $new_poll_id = 0;
     eval {
-        $tweets = $obj->replies( { since => HTTP::Date::time2str($last_poll) } )
-          || [];
+        if ( $id_map{__last_id}{$username}{reply} )
+        {
+            $tweets = $obj->replies(
+                { since_id => $id_map{__last_id}{$username}{reply} } )
+              || [];
+        } else {
+            $tweets = $obj->replies() || [];
+        }
     };
 
     if ($@) {
         print $fh "type:debug Error during replies call.  Aborted.\n";
-        return 1;
+        return undef;
     }
 
     foreach my $t ( reverse @$tweets ) {
@@ -795,26 +910,32 @@ sub do_updates {
 
         my $text = decode_entities( $t->{text} );
         $text = &hilight($text);
-        printf $fh "id:%d account:%s nick:%s type:tweet %s\n",
-          $t->{id}, $username, $t->{user}{screen_name}, $text;
         if ( $t->{truncated} ) {
-            printf $fh "id:%s account:%s nick:%s type:ellispis %s\n",
-              $t->{id} . "-url", $username,
-              $t->{user}{screen_name},
-              "http://twitter.com/$t->{user}{screen_name}/status/$t->{id}";
+            $text .= " -- http://twitter.com/$t->{user}{screen_name}"
+              . "/status/$t->{id}";
         }
+        printf $fh "id:%d account:%s nick:%s type:tweet %s\n",
+          $t->{id}, $username, $t->{user}{screen_name}, $text;
+        $new_poll_id = $t->{id} if $new_poll_id < $t->{id};
     }
+    printf $fh "id:%d account:%s type:last_id reply\n", $new_poll_id, $username;
 
     print scalar localtime, " - Polling for DMs" if &debug;
+    $new_poll_id = 0;
     eval {
-        $tweets =
-          $obj->direct_messages( { since => HTTP::Date::time2str($last_poll) } )
-          || [];
+        if ( $id_map{__last_id}{$username}{dm} )
+        {
+            $tweets = $obj->direct_messages(
+                { since_id => $id_map{__last_id}{$username}{dm} } )
+              || [];
+        } else {
+            $tweets = $obj->direct_messages() || [];
+        }
     };
 
     if ($@) {
         print $fh "type:debug Error during direct_messages call.  Aborted.\n";
-        return 1;
+        return undef;
     }
 
     foreach my $t ( reverse @$tweets ) {
@@ -822,7 +943,9 @@ sub do_updates {
         $text = &hilight($text);
         printf $fh "id:%d account:%s nick:%s type:dm %s\n",
           $t->{id}, $username, $t->{sender_screen_name}, $text;
+        $new_poll_id = $t->{id} if $new_poll_id < $t->{id};
     }
+    printf $fh "id:%d account:%s type:last_id dm\n", $new_poll_id, $username;
 
     print scalar localtime, " - Polling for subscriptions" if &debug;
     if ( $obj->can('search') and $id_map{__searches}{$username} ) {
@@ -842,14 +965,13 @@ sub do_updates {
             if ($@) {
                 print $fh
                   "type:debug Error during search($topic) call.  Aborted.\n";
-                return 1;
+                return undef;
             }
 
             unless ( $search->{max_id} ) {
-                print $fh
-"type:debug Invalid search results when searching for $topic.",
-                  "  Aborted.\n";
-                return 1;
+                print $fh "type:debug Invalid search results when searching",
+                  " for $topic. Aborted.\n";
+                return undef;
             }
 
             $id_map{__searches}{$username}{$topic} = $search->{max_id};
@@ -861,13 +983,16 @@ sub do_updates {
                 $text = &hilight($text);
                 printf $fh "id:%d account:%s nick:%s type:search topic:%s %s\n",
                   $t->{id}, $username, $t->{from_user}, $topic, $text;
+                $new_poll_id = $t->{id}
+                  if not $new_poll_id
+                      or $t->{id} < $new_poll_id;
             }
         }
     }
 
     print scalar localtime, " - Done" if &debug;
 
-    return 0;
+    return 1;
 }
 
 sub monitor_child {
@@ -877,7 +1002,7 @@ sub monitor_child {
 
     print scalar localtime, " - checking child log at $filename ($attempt)"
       if &debug;
-    my $new_last_poll;
+    my ($new_last_poll);
 
     # first time we run we don't want to print out *everything*, so we just
     # pretend
@@ -892,13 +1017,14 @@ sub monitor_child {
             last if /^__friends__/;
             my $hilight = 0;
             my %meta;
+
             foreach my $key (qw/id account nick type topic/) {
                 if (s/^$key:(\S+)\s*//) {
                     $meta{$key} = $1;
                 }
             }
 
-            if ( not $meta{type} or $meta{type} ne 'searchid' ) {
+            if ( not $meta{type} or $meta{type} !~ /searchid|last_id/ ) {
                 if ( exists $meta{id} and exists $new_cache{ $meta{id} } ) {
                     next;
                 }
@@ -911,17 +1037,17 @@ sub monitor_child {
             }
 
             my $account = "";
-            if ( lc $meta{account} ne lc "$user\@$defservice" ) {
-                $meta{account} =~ s/\@(\w+)$//;
-                my $service = $1;
-                if (
-                    lc $service eq
-                    lc Irssi::settings_get_str("twirssi_default_service") )
-                {
-                    $account = "$meta{account}: ";
-                } else {
-                    $account = "$meta{account}\@$service: ";
-                }
+            $meta{account} =~ s/\@(\w+)$//;
+            $meta{service} = $1;
+            if (
+                lc $meta{service} eq
+                lc Irssi::settings_get_str("twirssi_default_service") )
+            {
+                $account = "$meta{account}: "
+                  if lc "$meta{account}\@$meta{service}" ne lc
+                      "$user\@$defservice";
+            } else {
+                $account = "$meta{account}\@$meta{service}: ";
             }
 
             my $marker = "";
@@ -931,15 +1057,18 @@ sub monitor_child {
                 and $meta{id} )
             {
                 $marker = ( $id_map{__indexes}{ $meta{nick} } + 1 ) % 100;
-                $id_map{ lc $meta{nick} }[$marker] = $meta{id};
-                $id_map{__indexes}{ $meta{nick} }  = $marker;
-                $marker                            = ":$marker";
+                $id_map{ lc $meta{nick} }[$marker]           = $meta{id};
+                $id_map{__indexes}{ $meta{nick} }            = $marker;
+                $id_map{__tweets}{ lc $meta{nick} }[$marker] = $_;
+                $marker                                      = ":$marker";
             }
 
             my $hilight_color =
               $irssi_to_mirc_colors{ Irssi::settings_get_str("hilight_color") };
-            if ( ( $_ =~ /\@$meta{account}\W/i )
-                && Irssi::settings_get_bool("twirssi_hilights") )
+            my $nick =
+              '@' . substr( $meta{account}, 0, index( $meta{account}, "@" ) );
+            if ( $_ =~ /\Q$nick\E(?:\W|$)/i
+                and Irssi::settings_get_bool("twirssi_hilights") )
             {
                 $meta{nick} = "\cC$hilight_color$meta{nick}\cO";
                 $hilight = MSGLEVEL_HILIGHT;
@@ -951,9 +1080,6 @@ sub monitor_child {
                     ( MSGLEVEL_PUBLIC | $hilight ),
                     $meta{type}, $account, $meta{nick}, $marker, $_
                   ];
-            } elsif ( $meta{type} eq 'ellispis' ) {
-                push @lines,
-                  [ MSGLEVEL_PUBLIC, "tweet", $account, $meta{nick}, "", $_ ];
             } elsif ( $meta{type} eq 'search' ) {
                 push @lines,
                   [
@@ -987,6 +1113,11 @@ sub monitor_child {
                 } elsif (&debug) {
                     print "Search '$meta{topic}' returned invalid id $meta{id}";
                 }
+            } elsif ( $meta{type} eq 'last_id' ) {
+                $id_map{__last_id}{"$meta{account}\@$meta{service}"}{$_} =
+                  $meta{id}
+                  if $id_map{__last_id}{"$meta{account}\@$meta{service}"}{$_} <
+                      $meta{id};
             } elsif ( $meta{type} eq 'error' ) {
                 push @lines, [ MSGLEVEL_MSGS, $_ ];
             } elsif ( $meta{type} eq 'debug' ) {
@@ -998,8 +1129,8 @@ sub monitor_child {
 
         %friends = ();
         while (<FILE>) {
-            if (/^\d+$/) {
-                $new_last_poll = $_;
+            if (/^-- (\d+)$/) {
+                ($new_last_poll) = ($1);
                 last;
             }
             my ( $f, $t ) = split ' ', $_;
@@ -1007,7 +1138,8 @@ sub monitor_child {
         }
 
         if ($new_last_poll) {
-            print "new last_poll = $new_last_poll" if &debug;
+            print "new last_poll    = $new_last_poll" if &debug;
+            print "new last_poll_id = ", Dumper( $id_map{__last_id} ) if &debug;
             if ($suppress) {
                 print "First call, not printing updates" if &debug;
             } else {
@@ -1166,7 +1298,7 @@ sub sig_complete {
     my ( $complist, $window, $word, $linestart, $want_space ) = @_;
 
     if (
-        $linestart =~ /^\/twitter_reply(?:_as)?\s*$/
+        $linestart =~ /^\/(?:retweet|twitter_reply)(?:_as)?\s*$/
         or ( Irssi::settings_get_bool("twirssi_use_reply_aliases")
             and $linestart =~ /^\/reply(?:_as)?\s*$/ )
       )
@@ -1178,6 +1310,13 @@ sub sig_complete {
           keys %{ $id_map{__indexes} };
     }
 
+    if ( $linestart =~ /^\/twitter_unfriend\s*$/ )
+    {    # /twitter_unfriend gets a nick
+        $word =~ s/^@//;
+        push @$complist, grep /^\Q$word/i,
+          sort { $nicks{$b} <=> $nicks{$a} } keys %nicks;
+    }
+
     # /tweet, /tweet_as, /dm, /dm_as - complete @nicks (and nicks as the first
     # arg to dm)
     if ( $linestart =~ /^\/(?:tweet|dm)/ ) {
@@ -1316,6 +1455,8 @@ Irssi::settings_add_str( "twirssi", "twitter_passwords",       undef );
 Irssi::settings_add_str( "twirssi", "twirssi_default_service", "Twitter" );
 Irssi::settings_add_str( "twirssi", "twirssi_nick_color",      "%B" );
 Irssi::settings_add_str( "twirssi", "twirssi_topic_color",     "%r" );
+Irssi::settings_add_str( "twirssi", "twirssi_retweet_format",
+    'RT $n: "$t" ${-- $c$}' );
 Irssi::settings_add_str( "twirssi", "twirssi_location",
     ".irssi/scripts/twirssi.pl" );
 Irssi::settings_add_str( "twirssi", "twirssi_replies_store",
@@ -1352,6 +1493,8 @@ if ($window) {
     Irssi::command_bind( "dm_as",                      "cmd_direct_as" );
     Irssi::command_bind( "tweet",                      "cmd_tweet" );
     Irssi::command_bind( "tweet_as",                   "cmd_tweet_as" );
+    Irssi::command_bind( "retweet",                    "cmd_retweet" );
+    Irssi::command_bind( "retweet_as",                 "cmd_retweet_as" );
     Irssi::command_bind( "twitter_reply",              "cmd_reply" );
     Irssi::command_bind( "twitter_reply_as",           "cmd_reply_as" );
     Irssi::command_bind( "twitter_login",              "cmd_login" );
@@ -1361,6 +1504,7 @@ if ($window) {
     Irssi::command_bind( "twitter_unsubscribe",        "cmd_del_search" );
     Irssi::command_bind( "twitter_list_subscriptions", "cmd_list_search" );
     Irssi::command_bind( "twirssi_upgrade",            "cmd_upgrade" );
+    Irssi::command_bind( "twitter_updates",            "get_updates" );
     if ( Irssi::settings_get_bool("twirssi_use_reply_aliases") ) {
         Irssi::command_bind( "reply",    "cmd_reply" );
         Irssi::command_bind( "reply_as", "cmd_reply_as" );
@@ -1408,7 +1552,14 @@ if ($window) {
             sub { &notice("Stopped following $_[0]"); delete $nicks{ $_[0] }; }
         )
     );
-    Irssi::command_bind( "twitter_updates", "get_updates" );
+    Irssi::command_bind(
+        "twitter_device_updates",
+        &gen_cmd(
+            "/twitter_device_updates none|im|sms",
+            "update_delivery_device",
+            sub { &notice("Device updated to $_[0]"); }
+        )
+    );
     Irssi::signal_add_last( 'complete word' => \&sig_complete );
 
     &notice("  %Y<%C(%B^%C)%N                   TWIRSSI v%R$VERSION%N (r$REV)");