Twitter bot with AnyEvent
ニコニコ生放送のアラートサーバにつないで検索条件に引っかかるとツイートするBOT(https://twitter.com/pikipiki_bot)
AnyEventの使い方が感覚的にわかってきた感じがします。が、「こんな書き方ねーよ」という点があればコメントお願いします。
Net::Twitterはおそらくnon-blockingではないので、ツイート時にブロッキングしている可能性が高い。AnyEvent::Twitterを使うことをTodoに追加。
#!/usr/bin/perl use strict; use warnings; use utf8; use Encode; use Unicode::Normalize qw/NFKC/; use AnyEvent; use AnyEvent::Socket; use AnyEvent::HTTP; use AnyEvent::Handle; use Net::Twitter; use XML::Simple; use Data::Dumper; ###################### # Word initialization my @word_list = ( # regexp "プログラミング", "プログラマ", "C言語", 'C\+\+', "C#", "Objective-C", "COBOL", "D言語", "Delphi", "FORTRAN", "Groovy", "JavaScript", "Java", "Lua", "Pascal", "Perl", # "PHP", "Python", "Ruby", "機械語", "アセンブリ", "アセンブラ", "SQL", "Erlang", "Haskell", "LISP", "OCaml", "Scala", "ActionScript", "AWK", "Smalltalk", ); my @normalized_word_list = map { my $word =$_; $word = uc NFKC $word; $word =~ tr/ぁ-ん/ァ-ン/; $word; } @word_list; ################################### # Twitter setting initialization my $config = XMLin('/home/hoge/fuga.xml') or die $!; my $nt = Net::Twitter->new( traits => [qw/API::REST/], username => $config->{username}, password => $config->{password}, ); ### FLOW # get alert info # connect to the server # recieve chat xml tag # get stream info # NFKC, uc, katakanize recieved xml # grep recieved xml # if matched, then tweet my $server = get_alertinfo(); ###################### # callbacks ####################### ## http_request_cb my $http_request_cb; $http_request_cb = sub { my ($body, $hdr) =@_; my $formed_body = decode_utf8 $body; $formed_body = uc NFKC $formed_body; $formed_body =~ tr/ぁ-ん/ァ-ン/; my @matched_words = grep { my $word = $_; if ($formed_body =~ /$word/) { $word; } } @normalized_word_list; if (scalar @matched_words) { print encode_utf8 "matched: @matched_words\n"; my $body_xml = XMLin(decode_utf8 $body); my $request_id = $body_xml->{request_id}; my $title = substr $body_xml->{streaminfo}->{title}, 0, 20; my $co_name = substr $body_xml->{communityinfo}->{name}, 0, 20; my $sentence = "#nicolive http://nico.ms/$request_id "; $sentence .= "【word】$matched_words[0] 【community】$co_name 【title】$title "; my $result = $nt->update($sentence); print encode_utf8 $sentence; } }; ####################### ## socket_read_cb my $socket_read_cb; $socket_read_cb = sub { my ($handle, $chat_tag) = @_; #print Dumper $chat_tag; $chat_tag = XMLin(decode_utf8 $chat_tag); if (defined $chat_tag->{content}) { my ($lv_num, $co_num, $user_id) = split/,/, $chat_tag->{content}; if (defined $lv_num && defined $co_num && defined $user_id) { my $url = 'http://live.nicovideo.jp/api/getstreaminfo/lv'; http_request( GET => "$url$lv_num", timeout => 3, $http_request_cb ); } } $handle->push_read(line => "\0", $socket_read_cb); }; ################## ## connection_cb my $connection_cb; $connection_cb = sub { my ($fh) = @_ or die $!; my $thread_tag_attr = { thread => $server->{thread}, res_from => '-1', version => '20061206', }; my $thread_tag = XMLout($thread_tag_attr, RootName => 'thread') . "\0"; my $handle; $handle = new AnyEvent::Handle( fh => $fh, on_error => sub { warn "Error $_[2]\n"; $_[0]->destroy; }, on_eof => sub { $handle->destroy; warn "Done\n"; } ); $handle->push_write($thread_tag); $handle->push_read(line => "\0", $socket_read_cb); }; ######################### # main my $cv = AE::cv; my $connection = tcp_connect( $server->{addr}, $server->{port}, $connection_cb, ); $cv->recv; #################### # function : get_alertinfo sub get_alertinfo { my $url = 'http://live.nicovideo.jp/api/getalertinfo'; use LWP::UserAgent; my $ua = LWP::UserAgent->new(); my $res = $ua->get($url); die "$res->status_line" unless ($res->is_success); my $xml = XMLin($res->decoded_content); die "Fatal: Server status $xml->{status}" if ($xml->{status} ne 'ok'); return $xml->{ms}; }
次のエントリーでAnyEventとブロッキングについて検証した結果を書きます。