Organization Information about the organization or company associated with your application. This information is optional. Organization None Organization website None OAuth settings Your application's OAuth settings. Keep the "Consumer secret" a secret. This key should never be human-readable in your application. Access level Read-only About the application permission model Consumer key fA8OQwDNQkUa4L55znTWA Consumer secret rfB5lWocMdKoSKzWpXmIMB9eDALKbWRpKHlh9vSV178 Request token URL https://api.twitter.com/oauth/request_token Authorize URL https://api.twitter.com/oauth/authorize Access token URL https://api.twitter.com/oauth/access_token Callback URL None https://dev.twitter.com/apps/1376385/settings Access token 409005745-aUFj8VGr5QrHUiTwkGiv3grp08znowCikWtkpkD4 Access token secret vDqY9gXmy3Bu09YmZuo384GajZjZg9Y8WvWsIW0s ID : jmr114 パスワード : b849u2cjBu7Pf5 jinkun FTPサーバ : www35.atpages.jp ホームページURL : http://www35.atpages.jp/jmr114/ 1 #!/usr/bin/perl 2 3 # マルコフ連鎖POST 4 # OAuth ver 5 6 use strict; 7 use Net::Twitter; 8 use LWP::Simple; 9 use XML::Simple; 10 use File::Basename; 11 use Cwd 'abs_path'; 12 use Dumpvalue; 13 14 my $d = Dumpvalue->new(); # デバッグ用 15 16 # OAuth settings 17 use constant CONSUMER_KEY => 'CONSUMER_KEY'; 18 use constant CONSUMER_KEY_SECRET => 'CONSUMER_KEY_SECRET'; 19 use constant ACCESS_TOKEN => 'ACCESS_TOKEN'; 20 use constant ACCESS_TOKEN_SECRET => 'ACCESS_TOKEN_SECRET'; 21 22 # XML settings 23 use constant TWITTER_XML_URL => 'http://twitter.com/statuses/user_timeline/--USER_ID--.xml?count=100'; 24 use constant OWNER_USER_ID => 'OWNER_USER_ID'; 25 use constant APPID => 'APPID'; 26 use constant API_BASE_URL => 'http://jlp.yahooapis.jp/MAService/V1/parse'; 27 use constant API_FIX_PARAM => '&result=ma'; 28 29 # other settings 30 use constant SINCE_ID_FILENAME => '.since_id'; 31 use constant ROOP_COUNT => 50; 32 use constant RAND_PROB => 10; # 確率の分子 33 use constant RAND_PARAM => 100; # 確率の分母 34 35 # OAuthの準備(global) 36 my $twitter = Net::Twitter->new( 37 traits => ['API::REST', 'OAuth'], 38 consumer_key => CONSUMER_KEY, 39 consumer_secret => CONSUMER_KEY_SECRET, 40 ); 41 $twitter->access_token(ACCESS_TOKEN); 42 $twitter->access_token_secret(ACCESS_TOKEN_SECRET); 43 44 # main 45 { 46 # 前回のmentionsの最大のIDをファイルから読み込み 47 my $since_id = &read_since_id(SINCE_ID_FILENAME); 48 49 # 新規mentionsに対してreplyを返す 50 my $recent_since_id = &reaction_to_mentions($since_id); 51 52 # 今回のmentionの最大のIDをファイルに書き込み 53 &write_since_id(SINCE_ID_FILENAME,$recent_since_id); 54 55 # 確率でPOSTする 56 my @rand_val = 1 .. RAND_PARAM; 57 if(rand @rand_val < RAND_PROB){ 58 my $owner_xml_url = TWITTER_XML_URL; 59 my $owner_user_id = OWNER_USER_ID; 60 $owner_xml_url =~ s/--USER_ID--/$owner_user_id/; 61 62 # XMLを取得しマルコフ連鎖文字列を生成 63 my $tweet_str = &make_markov_chain_str($owner_xml_url); 64 65 # 返ってきた文字列がundefでなければPOSTする 66 if(defined $tweet_str){ 67 my $res = $twitter->update({ status => "$tweet_str" }); 68 } 69 } 70 } 71 72 # 新着mentionに対して相手のPOST履歴からマルコフ連鎖文字列を生成しreplyを返す 73 # 74 # ARGV: 75 # $since_id : 前回のmentionsの最大のID 76 # 77 # return: 78 # $recent_since_id : 今回のmentionの最大のID 79 # 80 sub reaction_to_mentions{ 81 my $since_id = my $recent_since_id = shift; 82 my %uniq_user; 83 84 # mentions取得 85 my $mentions = $twitter->mentions(); 86 87 foreach(@{$mentions}){ 88 my $user_xml_url = TWITTER_XML_URL; 89 90 # 今回のmentionの最大のIDが$recent_since_idに入るようにする 91 $recent_since_id = &max($recent_since_id,$_->{'id'}); 92 93 # 以下の条件に一致するものにはreplyしない 94 # ・前回のmentionsの最大のIDより前のIDのもの 95 # ・先頭以外に@があるもの 96 # ・プロテクトアカウント 97 # ・今回の起動で既に1回replyしたユーザー 98 if($_->{'id'} <= $since_id or $_->{'text'} =~ m/^(.)(.*)\@/ or $_->{'user'}->{'protected'} == 1 or exists $uniq_user{$_->{'user'}->{'id'}}){ 99 next; 100 } 101 102 # $since_idが0だった場合今回のIDを入れておく 103 unless($since_id){ 104 $since_id = $_->{'id'}; 105 } 106 107 # 今回replyしたユーザーのリスト 108 $uniq_user{$_->{'user'}->{'id'}} = 1; 109 110 # 取得するXMLのURLにuser_idをセット 111 $user_xml_url =~ s/--USER_ID--/$_->{'user'}->{'id'}/; 112 113 # XMLを取得しマルコフ連鎖文字列を生成 114 my $tweet_str = &make_markov_chain_str($user_xml_url); 115 116 # 返ってきた文字列がundefでなければ@、in_reply_toをつけてPOSTする 117 if(defined $tweet_str){ 118 $tweet_str = '@'.$_->{'user'}->{'screen_name'}.' '.$tweet_str; 119 my $res = $twitter->update({ status => "$tweet_str", in_reply_to_status_id => $_->{'id'} }); 120 } 121 sleep(10); 122 } 123 return $recent_since_id; 124 } 125 126 # 指定XMLからマルコフ連鎖文字列を生成 127 # 128 # ARGV: 129 # $xml_url : 取得対象XMLのURL 130 # 131 # return: 132 # undef or $tweet_str : 生成したマルコフ連鎖文字列 133 # 134 sub make_markov_chain_str{ 135 my $xml_url = shift; 136 my $markov_table; 137 my $tweet_list; 138 my @head_word; 139 140 # XML取得 141 my $xml_result = get($xml_url); 142 my $xs = new XML::Simple(); 143 my $ref = $xs->XMLin($xml_result); 144 145 # textキーのみハッシュへ 146 foreach my $key (sort keys %{$ref->{'status'}}){ 147 $tweet_list->{$key} = $ref->{'status'}->{$key}->{'text'}; 148 } 149 150 # 1POSTごとに形態素解析しマルコフテーブルを作成 151 foreach my $key (keys %{$tweet_list}){ 152 my @markov_list; 153 154 # # @ http を含むPOSTの場合はスキップ 155 if($tweet_list->{$key} =~ m/http/ or $tweet_list->{$key} =~ m/\@/ or $tweet_list->{$key} =~ m/@/ or $tweet_list->{$key} =~ m/\#/ or $tweet_list->{$key} =~ m/#/){ 156 next; 157 } 158 159 # apiを叩いて形態素解析 160 my $api_param = '?appid='.APPID.'&sentence='.$tweet_list->{$key}.API_FIX_PARAM; 161 my $api_url = API_BASE_URL.$api_param; 162 my $api_res = get($api_url); 163 chomp($api_res); 164 165 # 切り出された単語をリストにpushする 166 while($api_res =~ m/(.+?)<\/surface>/g){ 167 unless(scalar(@markov_list)){ 168 push (@head_word,$1); 169 } 170 push (@markov_list,$1); 171 } 172 173 # マルコフテーブルを作成 174 foreach my $num (0 .. $#markov_list){ 175 push (@{ $markov_table->{$markov_list[$num]} },$markov_list[$num+1]); 176 } 177 } 178 179 # 要素数が0の場合何もしない 180 unless(scalar(keys(%{$markov_table}))){ 181 return; 182 } 183 184 # 最初の単語を決定 185 my $tweet_str = my $chain_word = $head_word[rand @head_word]; 186 187 # 無限ループしないよう最大回数を決めてマルコフ連鎖 188 foreach (0 .. ROOP_COUNT){ 189 my $add_word = $markov_table->{$chain_word}->[rand @{$markov_table->{$chain_word}}]; 190 $tweet_str .= $chain_word = $add_word; 191 } 192 return $tweet_str; 193 } 194 195 # IDをファイルに書き込み 196 # 197 # ARGV: 198 # $filename : 書き込み対象ファイル名 199 # $id : 書き込みID 200 # 201 # return: 202 # undef 203 # 204 sub write_since_id{ 205 my $filename = shift; 206 my $id = shift; 207 my $since_id_file = dirname(abs_path($0)) . '/' . $filename; 208 209 open (OUT, ">$since_id_file"); 210 print OUT $id; 211 close(OUT); 212 213 return; 214 } 215 216 # IDをファイルから読み込み 217 # 218 # ARGV: 219 # $filename : 読み込み対象ファイル名 220 # 221 # return: 222 # $id : ファイルから読み込んだID 223 # 224 sub read_since_id{ 225 my $filename = shift; 226 my $since_id_file = dirname(abs_path($0)) . '/' . $filename; 227 my $id = 0; 228 229 if(open IN,$since_id_file){ 230 $id = ; 231 chomp($id); 232 } 233 close(IN); 234 235 return $id; 236 } 237 238 # 最大値判定処理 239 # 240 # ARGV: 241 # $val1 : 比較する値 242 # $val2 : 比較する値 243 # 244 # return: 245 # $val1 or $val2 : $val1と$val2を比較し大きい方の値を返す 246 # 247 sub max{ 248 my $val1 = shift; 249 my $val2 = shift; 250 251 if($val1 >= $val2){ 252 return $val1; 253 } 254 else{ 255 return $val2; 256 } 257 } #!/usr/bin/perl # マルコフ連鎖POST # OAuth ver use strict; use Net::Twitter; use LWP::Simple; use XML::Simple; use File::Basename; use Cwd 'abs_path'; use Dumpvalue; my $d = Dumpvalue->new(); # デバッグ用 # OAuth settings use constant CONSUMER_KEY => 'CONSUMER_KEY'; use constant CONSUMER_KEY_SECRET => 'CONSUMER_KEY_SECRET'; use constant ACCESS_TOKEN => 'ACCESS_TOKEN'; use constant ACCESS_TOKEN_SECRET => 'ACCESS_TOKEN_SECRET'; # XML settings use constant TWITTER_XML_URL => 'http://twitter.com/statuses/user_timeline/--USER_ID--.xml?count=100'; use constant OWNER_USER_ID => 'OWNER_USER_ID'; use constant APPID => 'APPID'; use constant API_BASE_URL => 'http://jlp.yahooapis.jp/MAService/V1/parse'; use constant API_FIX_PARAM => '&result=ma'; # other settings use constant SINCE_ID_FILENAME => '.since_id'; use constant ROOP_COUNT => 50; use constant RAND_PROB => 10; # 確率の分子 use constant RAND_PARAM => 100; # 確率の分母 # OAuthの準備(global) my $twitter = Net::Twitter->new( traits => ['API::REST', 'OAuth'], consumer_key => CONSUMER_KEY, consumer_secret => CONSUMER_KEY_SECRET, ); $twitter->access_token(ACCESS_TOKEN); $twitter->access_token_secret(ACCESS_TOKEN_SECRET); # main { # 前回のmentionsの最大のIDをファイルから読み込み my $since_id = &read_since_id(SINCE_ID_FILENAME); # 新規mentionsに対してreplyを返す my $recent_since_id = &reaction_to_mentions($since_id); # 今回のmentionの最大のIDをファイルに書き込み &write_since_id(SINCE_ID_FILENAME,$recent_since_id); # 確率でPOSTする my @rand_val = 1 .. RAND_PARAM; if(rand @rand_val < RAND_PROB){ my $owner_xml_url = TWITTER_XML_URL; my $owner_user_id = OWNER_USER_ID; $owner_xml_url =~ s/--USER_ID--/$owner_user_id/; # XMLを取得しマルコフ連鎖文字列を生成 my $tweet_str = &make_markov_chain_str($owner_xml_url); # 返ってきた文字列がundefでなければPOSTする if(defined $tweet_str){ my $res = $twitter->update({ status => "$tweet_str" }); } } } # 新着mentionに対して相手のPOST履歴からマルコフ連鎖文字列を生成しreplyを返す # # ARGV: #$since_id : 前回のmentionsの最大のID # # return: # $recent_since_id : 今回のmentionの最大のID # sub reaction_to_mentions{ my $since_id = my $recent_since_id = shift; my %uniq_user; # mentions取得 my $mentions = $twitter->mentions(); foreach(@{$mentions}){ my $user_xml_url = TWITTER_XML_URL; # 今回のmentionの最大のIDが$recent_since_idに入るようにする $recent_since_id = &max($recent_since_id,$_->{'id'}); # 以下の条件に一致するものにはreplyしない # ・前回のmentionsの最大のIDより前のIDのもの # ・先頭以外に@があるもの # ・プロテクトアカウント # ・今回の起動で既に1回replyしたユーザー if($_->{'id'} <= $since_id or $_->{'text'} =~ m/^(.)(.*)\@/ or $_->{'user'}->{'protected'} == 1 or exists $uniq_user{$_->{'user'}->{'id'}}){ next; } # $since_idが0だった場合今回のIDを入れておく unless($since_id){ $since_id = $_->{'id'}; } # 今回replyしたユーザーのリスト $uniq_user{$_->{'user'}->{'id'}} = 1; # 取得するXMLのURLにuser_idをセット $user_xml_url =~ s/--USER_ID--/$_->{'user'}->{'id'}/; # XMLを取得しマルコフ連鎖文字列を生成 my $tweet_str = &make_markov_chain_str($user_xml_url); # 返ってきた文字列がundefでなければ@、in_reply_toをつけてPOSTする if(defined $tweet_str){ $tweet_str = '@'.$_->{'user'}->{'screen_name'}.' '.$tweet_str; my $res = $twitter->update({ status => "$tweet_str", in_reply_to_status_id => $_->{'id'} }); } sleep(10); } return $recent_since_id; } # 指定XMLからマルコフ連鎖文字列を生成 # # ARGV: # $xml_url : 取得対象XMLのURL # # return: # undef or $tweet_str : 生成したマルコフ連鎖文字列 # sub make_markov_chain_str{ my $xml_url = shift; my $markov_table; my $tweet_list; my @head_word; # XML取得 my $xml_result = get($xml_url); my $xs = new XML::Simple(); my $ref = $xs->XMLin($xml_result); # textキーのみハッシュへ foreach my $key (sort keys %{$ref->{'status'}}){ $tweet_list->{$key} = $ref->{'status'}->{$key}->{'text'}; } # 1POSTごとに形態素解析しマルコフテーブルを作成 foreach my $key (keys %{$tweet_list}){ my @markov_list; # # @ http を含むPOSTの場合はスキップ if($tweet_list->{$key} =~ m/http/ or $tweet_list->{$key} =~ m/\@/ or $tweet_list->{$key} =~ m/@/ or $tweet_list->{$key} =~ m/\#/ or $tweet_list->{$key} =~ m/#/){ next; } # apiを叩いて形態素解析 my $api_param = '?appid='.APPID.'&sentence='.$tweet_list->{$key}.API_FIX_PARAM; my $api_url = API_BASE_URL.$api_param; my $api_res = get($api_url); chomp($api_res); # 切り出された単語をリストにpushする while($api_res =~ m/(.+?)<\/surface>/g){ unless(scalar(@markov_list)){ push (@head_word,$1); } push (@markov_list,$1); } # マルコフテーブルを作成 foreach my $num (0 .. $#markov_list){ push (@{ $markov_table->{$markov_list[$num]} },$markov_list[$num+1]); } } # 要素数が0の場合何もしない unless(scalar(keys(%{$markov_table}))){ return; } # 最初の単語を決定 my $tweet_str = my $chain_word = $head_word[rand @head_word]; # 無限ループしないよう最大回数を決めてマルコフ連鎖 foreach (0 .. ROOP_COUNT){ my $add_word = $markov_table->{$chain_word}->[rand @{$markov_table->{$chain_word}}]; $tweet_str .= $chain_word = $add_word; } return $tweet_str; } # IDをファイルに書き込み # # ARGV: # $filename : 書き込み対象ファイル名 # $id : 書き込みID # # return: # undef # sub write_since_id{ my $filename = shift; my $id = shift; my $since_id_file = dirname(abs_path($0)) . '/' . $filename; open (OUT, ">$since_id_file"); print OUT $id; close(OUT); return; } # IDをファイルから読み込み # # ARGV: # $filename : 読み込み対象ファイル名 # # return: # $id : ファイルから読み込んだID # sub read_since_id{ my $filename = shift; my $since_id_file = dirname(abs_path($0)) . '/' . $filename; my $id = 0; if(open IN,$since_id_file){ $id = ; chomp($id); } close(IN); return $id; } # 最大値判定処理 # # ARGV: # $val1 : 比較する値 # $val2 : 比較する値 # # return: # $val1 or $val2 : $val1と$val2を比較し大きい方の値を返す # sub max{ my $val1 = shift; my $val2 = shift; if($val1 >= $val2){ return $val1; } else{ return $val2; } }