source: lab/trunk/twitter/twitterbot.pl @ 63

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