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

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