Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env perl
- use strict;
- use warnings;
- use utf8;
- use opts;
- use AnyEvent;
- use AnyEvent::Twitter;
- use Coro;
- use Coro::Channel;
- use Coro::Timer;
- use Data::Dumper;
- use Log::Dispatch;
- opts
- my $debug => +{ isa => 'Bool', default => 0, required => 1 },
- my $log_file => +{ isa => 'Str' };
- binmode STDOUT, ':encoding(utf8)';
- binmode STDERR, ':encoding(utf8)';
- my $twitter = AnyEvent::Twitter->new(
- access_token => '',
- access_token_secret => '',
- consumer_key => '',
- consumer_secret => '',
- );
- my $logger = Log::Dispatch->new(
- outputs => (
- $log_file
- ? [
- [
- FileShared => (
- callbacks => \&modify_message,
- filename => $log_file,
- min_level => +($debug ? 'debug' : 'info'),
- name => 'FileLogger',
- newline => 1,
- )
- ],
- ]
- : [
- [
- Screen => (
- callbacks => \&modify_message,
- min_level => +($debug ? 'debug' : 'info'),
- newline => 1,
- )
- ],
- ]
- ),
- );
- my $unko_tweets = Coro::Channel->new;
- async {
- my $since_id = 0;
- while ($since_id == 0) {
- my ($header, $res, $reason) = search_unko_tweets($twitter, 1, $since_id);
- unless ($header->{Status} eq '200' and $res->{max_id}) {
- $logger->warn(sprintf('[search] Failed to fetch results: %s.', $reason));
- sleep_thread(30);
- next;
- }
- $since_id = $res->{max_id};
- }
- while (1) {
- $logger->debug(sprintf('[search] since_id = %d.', $since_id));
- my ($header, $res, $reason) = search_unko_tweets($twitter, 100, $since_id);
- unless ($header->{Status} eq '200' and $res->{max_id}) {
- $logger->warn(sprintf('[search] Failed to fetch results: %s.', $reason));
- next;
- }
- $since_id = $res->{max_id};
- for my $tweet (grep { is_unko_tweet($_) } @{ $res->{results} }) {
- if ($tweet->{text}) {
- $unko_tweets->put($tweet);
- cede;
- } else {
- $logger->info(
- sprintf('[search] Empty hash returned from API: %s.', Dumper($tweet))
- );
- }
- }
- } continue {
- sleep_thread(30);
- }
- };
- async {
- while (1) {
- my $tweet = $unko_tweets->get;
- async {
- my $orig_tweet_id = $tweet->{id_str};
- my $reply_text = create_reply_text($tweet);
- my $target = $tweet->{from_user};
- $logger->info(
- sprintf(
- '[reply] Reply for %s (id = %s, text = "%s", tweeted at %s)'
- . ' is scheduled.',
- $target,
- $orig_tweet_id,
- $tweet->{text},
- $tweet->{created_at},
- )
- );
- sleep_thread(10 * 60);
- $twitter->post('statuses/update' => +{
- in_reply_to_status_id => $orig_tweet_id,
- status => $reply_text,
- }, Coro::rouse_cb);
- my ($header, $res, $reason) = Coro::rouse_wait;
- if ($header->{Status} eq '200') {
- $logger->info(sprintf('[reply] Replied to %s.', $target));
- } else {
- $logger->warn(
- sprintf(
- '[reply] Failed to reply to %s'
- . ' (in_reply_to_status_id = %s, text = "%s") : %s.',
- $target,
- $orig_tweet_id,
- $reply_text,
- $reason
- )
- );
- }
- };
- }
- };
- AE::cv->recv; # Infinite loop.
- sub create_reply_text {
- my $tweet = shift;
- my $tmpl = '@!user が "!tweet" とつぶやいてから10分経った。うんこは有意義だったか?';
- my $orig_tweet = $tweet->{text};
- my $text = create_reply_text1($tmpl, $tweet->{from_user}, $orig_tweet);
- if (length($text) > 140) {
- substr($orig_tweet, 140 - length($text) - 1) = '…';
- $text = create_reply_text1($tmpl, $tweet->{from_user}, $orig_tweet);
- }
- return $text;
- }
- sub create_reply_text1 {
- my ($tmpl, $screen_name, $orig_tweet) = @_;
- $tmpl =~ s/!user/$screen_name/;
- $tmpl =~ s/!tweet/$orig_tweet/;
- return $tmpl;
- }
- sub is_unko_tweet {
- my $tweet = shift;
- return if $tweet->{to_user};
- return if $tweet->{text} =~ /\bRT\b/;
- 1;
- }
- sub modify_message {
- my %args = @_;
- sprintf('[%d][%s]%s', time, $args{level}, $args{message});
- }
- sub search_unko_tweets {
- my ($twitter, $rpp, $since_id) = @_;
- $twitter->get(
- search => +{
- locale => 'ja',
- q => 'うんこなう',
- result_type => 'recent',
- rpp => $rpp,
- since_id => $since_id,
- },
- Coro::rouse_cb
- );
- Coro::rouse_wait;
- }
- sub sleep_thread {
- my $sec = shift;
- my $timeout = Coro::Timer::timeout($sec);
- schedule until $timeout;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement