* remove needless hash check
[lab.git] / twitterbot.pl
index ccd2c5a..de7a250 100755 (executable)
@@ -12,6 +12,14 @@ use utf8;
 use Net::Twitter::Lite;
 use FindBin qw($Bin);
 use YAML::Tiny;
+use Date::Parse qw(str2time);
+
+my $_execmode = $ARGV[0] || 0;
+sub VERBOSE () { $_execmode eq 'verbose' };
+sub DEBUG   () { VERBOSE or $_execmode eq 'debug' };
+use Data::Dumper;
+
+DEBUG and warn "$0: debug mode";
 
 my $conf = loadconf("$Bin/config.yml");
 if (! defined $conf) {
@@ -27,30 +35,29 @@ if (! $bot->authorized) {
     die "$0: this client is not yet authorized.\n";
 }
 
-my %tweets;
-my $tweet;
-
-$tweet = or_search($bot, $conf->{hashtag}, $stat->{search});
-if ($tweet) {
-    %tweets = (%tweets, %$tweet);
-}
-
-$tweet = mentions_ids($bot, $stat->{mention});
-if ($tweet) {
-    %tweets = (%tweets, %$tweet);
-}
+my $tweets = {};
+%$tweets = (
+    %$tweets,
+    %{ or_search($bot, $conf->{hashtag}, $stat->{search}) }
+);
+%$tweets = (
+    %$tweets,
+    %{ mentions_ids($bot, $stat->{mention}) }
+);
 
-foreach my $id (sort keys %tweets) {
-    if ($tweets{$id} eq 'retweet') {
+foreach my $id (sort keys %$tweets) {
+    # $tweets->{$id}{type} eq 'search'  => found by search API
+    #                      eq 'mention' => found by mention API
+    if ($tweets->{$id}{type} eq 'retweet') {
         next;
     }
-    sleep($conf->{sleep});
-    # retweet found tweet
-    #   $tweets->{$id} eq 'search'  => found by search API
-    #                  eq 'mention' => found by mention API
+    DEBUG or sleep($conf->{sleep});
+    
+    # do retweet found tweets
     my $res;
     eval {
-        $res = $bot->retweet($id);
+        DEBUG  or $res = $bot->retweet($id);
+        DEBUG and warn "retweet($id) => ", Dumper($tweets->{$id});
     };
     if ($@) {
         evalrescue($@);
@@ -58,12 +65,41 @@ foreach my $id (sort keys %tweets) {
         next;
     }
     
-    $stat->{$tweets{$id}} = $id;
+    $stat->{$tweets->{$id}{type}} = $id;
 }
 
-if (%tweets) {
+foreach my $word (keys %{$conf->{response}}) {
+    # reply to follower's tweets containing keywords
+    $tweets = follower_search($bot, $word);
+    foreach my $id (sort keys %$tweets) {
+        if ($tweets->{$id}{type} eq 'retweet') {
+            next;
+        }
+        DEBUG or sleep($conf->{sleep});
+        
+        my $text = '@' . $tweets->{$id}{screen_name}
+            . " " . $conf->{response}{$word};
+        my $res;
+        eval {
+            DEBUG  or $res = $bot->update(
+                {
+                    in_reply_to_status_id   => $id,
+                    status                  => $text,
+                }
+            );
+            DEBUG and warn "update(", $text, ") => ",
+                Dumper($tweets->{$id});
+        };
+        if ($@) {
+            evalrescue($@);
+        }
+    }
+}
+
+if ($tweets) {
     # save last status to yaml file
-    YAML::Tiny::DumpFile("$Bin/status.yml", $stat);
+    DEBUG  or YAML::Tiny::DumpFile("$Bin/status.yml", $stat);
+    DEBUG and warn "status.yml => ", Dumper($stat);
 }
 
 
@@ -120,6 +156,7 @@ sub or_search {
             $key = $word;
         }
     }
+    DEBUG and warn "searching '$key'";
     
     my $res;
     my $ids = {};
@@ -132,15 +169,27 @@ sub or_search {
                 }
             );
         }
+        VERBOSE and warn Dumper($res);
         if ($res->{results}) {
             foreach my $tweet (@{$res->{results}}) {
                 my $res = $bot->show_status($tweet->{id});
+                VERBOSE and warn Dumper($res);
+                
+                my $id = {
+                    date        => str2time($res->{created_at}),
+                    screen_name => $res->{user}{screen_name},
+                    status_id   => $res->{id},
+                    text        => $res->{text},
+                    user_id     => $res->{user}{id},
+                };
                 if ($res->{retweeted_status}) {
-                    $ids->{$tweet->{id}} = 'retweet';
+                    $id->{retweet_of}   = $res->{retweeted_status}{id};
+                    $id->{type}         = 'retweet';
                 }
                 else {
-                    $ids->{$tweet->{id}} = 'search';
+                    $id->{type} = 'search';
                 }
+                $ids->{$tweet->{id}} = $id;
             }
         }
     };
@@ -148,6 +197,7 @@ sub or_search {
         evalrescue($@);
     }
     
+    DEBUG and warn "search result => ", Dumper($ids);
     return $ids;
 }
 
@@ -167,18 +217,67 @@ sub mentions_ids {
                 since_id    => $since_id,
             }
         );
+        VERBOSE and warn Dumper($res);
     };
     if ($@) {
         evalrescue($@);
     }
     
-    my $ids;
+    my $ids = {};
     if ($res && @{$res}) {
         $ids = {
-            map { $_->{id} => 'mention' } @{$res}
+            map {
+                $_->{id} => {
+                    date        => str2time($_->{created_at}),
+                    screen_name => $_->{user}{screen_name},
+                    status_id   => $_->{id},
+                    text        => $_->{text},
+                    type        => 'mention',
+                    user_id     => $_->{user}{id},
+                }
+            } @{$res}
         };
     }
     
+    DEBUG and warn "mentions result => ", Dumper($ids);
+    return $ids;
+}
+
+sub follower_search {
+    # search follower's tweets containing keywords
+    #   param   => Net::Twitter::Lite object, keyword string, since_id
+    #   ret     => HashRef of status_id (timeline order is destroyed)
+    #               or undef (none is found)
+    
+    my $bot      = shift @_;
+    my $keyword  = shift @_;
+    my $since_id = shift @_ || 1;
+    
+    my $tweets = or_search($bot, [ $keyword ], $since_id);
+    if (! $tweets) {
+        return {};
+    }
+    
+    my $followers;
+    eval {
+        $followers = $bot->followers_ids();
+        VERBOSE and warn Dumper($followers);
+    };
+    if ($@) {
+        evalrescue($@);
+    }
+    
+    my $ids = {};
+    foreach my $status_id (keys %$tweets) {
+        foreach my $user_id (@$followers) {
+            if ($tweets->{$status_id}{user_id} == $user_id) {
+                $ids->{$status_id} = $tweets->{$status_id};
+                last;
+            }
+        }
+    }
+    
+    DEBUG and warn "search result in followers => ", Dumper($ids);
     return $ids;
 }