source: lab.git/Dev/twitter/twitterbot.pl @ 29f68714

trunk
Last change on this file since 29f68714 was 8333ea0, checked in by mitty <mitty@…>, 14 years ago

git-svn-id: https://lab.mitty.jp/svn/lab/trunk@92 7d2118f6-f56c-43e7-95a2-4bb3031d96e7

  • Property mode set to 100755
File size: 6.0 KB
RevLine 
[5ce2daf]1#! /usr/bin/perl -w
2
3use strict;
4use warnings;
5use utf8;
6
7## IMPORTANT ##
8# When Net::Twitter::Lite encounters a Twitter API error or a network error,
9# it throws a Net::Twitter::Lite::Error object.
10# You can catch and process these exceptions by using eval blocks and testing $@
11## from http://search.cpan.org/perldoc?Net::Twitter::Lite#ERROR_HANDLING
12use Net::Twitter::Lite;
13use FindBin qw($Bin);
14use YAML::Tiny;
[139622c]15use Date::Parse qw(str2time);
[5ce2daf]16
[f3ef4f6]17my $_execmode = $ARGV[0] || 0;
18sub VERBOSE () { $_execmode eq 'verbose' };
19sub DEBUG   () { VERBOSE or $_execmode eq 'debug' };
[f72bb76]20use Data::Dumper;
21
22DEBUG and warn "$0: debug mode";
23
[5ce2daf]24my $conf = loadconf("$Bin/config.yml");
25if (! defined $conf) {
26    die "$0: cannot parse config file.\n";
27}
28my $stat = loadconf("$Bin/status.yml");
29if (! defined $stat) {
[35889d4]30    $stat = {};
[5ce2daf]31}
32
33my $bot = login($conf);
34if (! $bot->authorized) {
35    die "$0: this client is not yet authorized.\n";
36}
37
[ac68e58]38my $tweets = {};
[e0c0679]39%$tweets = (
40    %$tweets,
41    %{ or_search($bot, $conf->{hashtag}, $stat->{search}) }
42);
43%$tweets = (
44    %$tweets,
45    %{ mentions_ids($bot, $stat->{mention}) }
46);
[5ce2daf]47
[ac68e58]48foreach my $id (sort keys %$tweets) {
49    # $tweets->{$id}{type} eq 'search'  => found by search API
50    #                      eq 'mention' => found by mention API
51    if ($tweets->{$id}{type} eq 'retweet') {
[5ce2daf]52        next;
53    }
[f72bb76]54    DEBUG or sleep($conf->{sleep});
[ac68e58]55   
56    # do retweet found tweets
[5ce2daf]57    my $res;
58    eval {
[f72bb76]59        DEBUG  or $res = $bot->retweet($id);
[ac68e58]60        DEBUG and warn "retweet($id) => ", Dumper($tweets->{$id});
[5ce2daf]61    };
62    if ($@) {
63        evalrescue($@);
64        warn "status_id => $id\n";
65        next;
66    }
67   
[ac68e58]68    $stat->{$tweets->{$id}{type}} = $id;
[5ce2daf]69}
70
[ac68e58]71if ($tweets) {
[5ce2daf]72    # save last status to yaml file
[f72bb76]73    DEBUG  or YAML::Tiny::DumpFile("$Bin/status.yml", $stat);
74    DEBUG and warn "status.yml => ", Dumper($stat);
[5ce2daf]75}
76
77
78sub loadconf {
79    # load configration data from yaml formatted file
80    #   param   => scalar string of filename
81    #   ret     => hash object of yaml data
82   
83    my $file = shift @_;
84   
85    my $yaml = YAML::Tiny->read($file);
86   
87    if ($!) {
88        warn "$0: '$file' $!\n";
89    }
90   
91    return $yaml->[0];
92}
93
94sub login {
95    # make Net::Twitter::Lite object and login
96    #   param   => hash object of configration
97    #   ret     => Net::Twitter::Lite object
98   
99    my $conf = shift @_;
100   
101    my $bot = Net::Twitter::Lite->new(
102        consumer_key    => $conf->{consumer_key},
103        consumer_secret => $conf->{consumer_secret},
104    );
105   
106    $bot->access_token($conf->{access_token});
107    $bot->access_token_secret($conf->{access_token_secret});
108   
109    return $bot;
110}
111
112sub or_search {
113    # search tweets containing keywords
114    #   param   => Net::Twitter::Lite object, ArrayRef of keywords, since_id
115    #   ret     => HashRef of status_id (timeline order is destroyed)
116    #               or undef (none is found)
117   
118    my $bot      = shift @_;
119    my $keywords = shift @_;
[35889d4]120    my $since_id = shift @_ || 1;
[5ce2daf]121   
122    my $key = "";
123    foreach my $word (@$keywords) {
124        if ($key) {
125            $key .= " OR $word";
126        }
127        else {
128            $key = $word;
129        }
130    }
[f72bb76]131    DEBUG and warn "searching '$key'";
[5ce2daf]132   
133    my $res;
134    my $ids = {};
135    eval {
136        if ($key) {
137            $res = $bot->search(
138                {
139                             => $key,
140                    since_id    => $since_id,
141                }
142            );
143        }
[139622c]144        VERBOSE and warn Dumper($res);
[5ce2daf]145        if ($res->{results}) {
146            foreach my $tweet (@{$res->{results}}) {
147                my $res = $bot->show_status($tweet->{id});
[139622c]148                VERBOSE and warn Dumper($res);
149               
150                my $id = {
151                    date        => str2time($res->{created_at}),
152                    screen_name => $res->{user}{screen_name},
153                    status_id   => $res->{id},
154                    text        => $res->{text},
[567a134]155                    user_id     => $res->{user}{id},
[139622c]156                };
[5ce2daf]157                if ($res->{retweeted_status}) {
[567a134]158                    $id->{retweet_of}   = $res->{retweeted_status}{id};
159                    $id->{type}         = 'retweet';
[5ce2daf]160                }
161                else {
[139622c]162                    $id->{type} = 'search';
[5ce2daf]163                }
[139622c]164                $ids->{$tweet->{id}} = $id;
[5ce2daf]165            }
166        }
167    };
168    if ($@) {
169        evalrescue($@);
170    }
171   
[f72bb76]172    DEBUG and warn "search result => ", Dumper($ids);
[5ce2daf]173    return $ids;
174}
175
176sub mentions_ids {
177    # return status_ids mentioned to me
178    #   param   => Net::Twitter::Lite object, since_id
179    #   ret     => HashRef of status_id (timeline order is destroyed)
180    #               or undef (none is found)
181   
182    my $bot      = shift @_;
[35889d4]183    my $since_id = shift @_ || 1;
[5ce2daf]184   
185    my $res;
186    eval {
187        $res = $bot->mentions(
188            {
189                since_id    => $since_id,
190            }
191        );
[f72bb76]192        VERBOSE and warn Dumper($res);
[5ce2daf]193    };
194    if ($@) {
195        evalrescue($@);
196    }
197   
[f72bb76]198    my $ids = {};
[5ce2daf]199    if ($res && @{$res}) {
200        $ids = {
[ac68e58]201            map {
202                $_->{id} => {
[139622c]203                    date        => str2time($_->{created_at}),
204                    screen_name => $_->{user}{screen_name},
205                    status_id   => $_->{id},
206                    text        => $_->{text},
207                    type        => 'mention',
[567a134]208                    user_id     => $_->{user}{id},
[ac68e58]209                }
210            } @{$res}
[5ce2daf]211        };
212    }
213   
[f72bb76]214    DEBUG and warn "mentions result => ", Dumper($ids);
[5ce2daf]215    return $ids;
216}
217
218sub evalrescue {
219    # output error message at eval error
220   
221    use Scalar::Util qw(blessed);
222   
223    if (blessed $@ && $@->isa('Net::Twitter::Lite::Error')) {
224        warn $@->error;
225        if ($@->twitter_error) {
226            my %twitter_error = %{$@->twitter_error};
227            map {
228                $twitter_error{"$_ => "} = $twitter_error{$_} . "\n";
229                delete $twitter_error{$_}
230            } keys %twitter_error;
231            warn join("", %twitter_error);
232        }
233    }
234    else {
235        warn $@;
236    }
237}
Note: See TracBrowser for help on using the repository browser.