source: lab.git/twitter/twitterbot.pl @ 567a134

trunk
Last change on this file since 567a134 was 567a134, checked in by mitty <mitty@…>, 14 years ago
  • add new hash members to return value of or_search and mentions_ids

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

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