ha's notepad II

メモ書きです。

RubyでTwitterのUserStreamを使う

前回紹介したRuby Twitter Gemで、ターミナルにテキストでTLを表示するクライアントを作ろうとした。最初に作ったコードは以下のような感じ。

# encoding: utf-8
require 'rubygems'
require 'twitter'

client = Twitter::REST::Client.new do |config|
  config.consumer_key        = API_KEY
  config.consumer_secret     = API_SECRET
  config.access_token        = ACCESS_TOKEN
  config.access_token_secret = ACCESS_TOKEN_SECRET
end

mytl = client.home_timeline #TLを取得
hist = []
while 1

	update = mytl - hist #前回取得したTLとの差分
	num = update.length - 1
	
	num.downto(0) do |i|
		id = update[i][:id] # TweetのID。ふぁぼRT等に使用
		username = update[i][:user][:screen_name] # 発言者ID
		contents = update[i][:text] # 発言内容
		str = username + ":" + contents
		puts str

	end
	hist = mytl
	sleep(90)
	mytl = client.home_timeline
end

90秒ごとにclient.home_timelineでTLを取得し、前回取得しなかったぶんを「ID:発言内容」という形式で表示できる。client.home_timelineで取得したTLは新しいものから古いものへと順になった配列になっているため、downtoを使うことで古いものから順に表示させるようにしている。

ここで問題になったのが、APIの制限だ。このgem(以下twitter gem)ではTLを取得するたびにAPIを使用する必要があり、リアルタイムに更新しようとすると15分に15回までというTwitterAPIの制限に簡単に達してしまう。これを避けるために更新頻度を90秒間隔に落としているが、これでは応答が遅くなってしまう。また、client.home_timelineでは一度に20件しか取得できないようなので、フォロー数の多い場合はTLの取得漏れが起きることもある。

そこで、UserStreamを使える新たなgemを使う。こちらがそれ。
 
tweetstream/tweetstream · GitHub

これを使って、以下のようなコードでTLの表示をリアルタイムで行うことが出来る。

# 認証

TweetStream.configure do |config|
	config.consumer_key = API_KEY
	config.consumer_secret = API_SECRET
	config.oauth_token = ACCESS_TOKEN
	config.oauth_token_secret = ACCESS_TOKEN_SECRET
	config.auth_method = :oauth
end

# TL取得

streamclient = TweetStream::Client.new
streamclient.userstream do |status|

	username = status[:user][:screen_name]
	contents = status[:text]
	id = status[:id]
	str = username + ":" + contents
	puts str
	
	
end

認証部分はtwitter gemとよく似た方法でできる。gemのサイトに使用例がいくつかあるが、streamclient.userstreamの部分を色々変更してやれば、検索やリプに即座に反応するよなbotを作成することも容易だろう。
ただし、これはあくまでUserStreamを利用してツイートを取得するだけなので、botとしてつぶやいたりふぁぼったりといった動作をするにはtwitter gemを利用する必要がある。
以下にtweetstreamとtwitter gemを利用して、起動時に過去のツイート20件を取得し表示してからUserStreamによるリアルタイム更新を行い、特定のワードに反応してリプを飛ばすクライアント兼botのようなものの例を載せる。

# encoding: utf-8
require 'rubygems'
require 'twitter'
require 'tweetstream'
require 'reply_judge.rb' # 反応するかどうかを判定


TweetStream.configure do |config|
	config.consumer_key = API_KEY
	config.consumer_secret = API_SECRET
	config.oauth_token = ACCESS_TOKEN
	config.oauth_token_secret = ACCESS_TOKEN_SECRET
	config.auth_method = :oauth
end

normalclient = Twitter::REST::Client.new do |config|
  config.consumer_key        = API_KEY
  config.consumer_secret     = API_SECRET
  config.access_token        = ACCESS_TOKEN
  config.access_token_secret = ACCESS_TOKEN_SECRET
end


puts "Configuration Success"

# twitter gemで初回更新

mytl = client.home_timeline
19.downto(0) do |i|
	id = mytl[i][:id]
	username = mytl[i][:user][:screen_name]
	contents = mytl[i][:text]
		
	str = username + ":" + contents
	puts str

end

# tweetstream使用

streamclient = TweetStream::Client.new
streamclient.userstream do |status|
	username = status[:user][:screen_name]
	contents = status[:text]
	id = status[:id]
	str = username + ":" + contents
	puts str
	
	if reply_judge(username,contents) != 0

		time = Random.new.rand 10.0
		# time = 0.1
		sleep(time)
		str = "@" + username + " foobar"
		normalclient.update(str, :in_reply_to_status_id => id) # リプ先のID指定

	end
	
end

RubyでTwitterにポスト(その2・解決)

前回どうもうまく行かなかったRuby Twitter gemだが、Ubuntuで最新版(v5.7.1)をインストールしてみたら、何故かうまくツイートできるようになった。
gemのバージョンアップによって、若干認証の書式が変わっていたりする。

まずはdev.twitter.comの"My application"から作ったアプリを開き、「API Keys」タブでAPI keyとAPI secretの文字列を表示させる。そして、実際につぶやくアカウントの認証を行う。認証には、
Ruby OAuth GEMを使うので、必要ならインストール。


このステップは、アプリの開発者とつぶやくアカウントが同じ場合、省略できる(API keyとAPI secretの下の方にあるAccess toke nとAccess secretをそのまま読む)。

require 'rubygems'
require 'oauth'

@consumer=OAuth::Consumer.new( "API key","API secret", {
    :site=>"https://api.twitter.com"
    })
@request_token=@consumer.get_request_token
@request_token.authorize_url

"API key"と"API secret"のところに、先の画面に表示された文字列を入れて(引用符を忘れない!)このコードを実行すると、アカウント認証のためのURLが出てくるので、ブラウザ等で開いて実際につぶやきたいアカウントで認証を行う。すると、数字の列(PIN)が表示されるので、これをメモ。続いて以下のコードを実行する。「●PIN●」の位置には先ほどのPINを、こんどは引用符無しで入れる。

@access_token = @request_token.get_access_token(:oauth_verifier => ●PIN●)
puts @access_token.token
puts @access_token.secret

最初の文字列が「Access token」で、2番めが「Access token secret」になる。この文字列があれば、Rubyでつぶやくことが可能だ。

ここまでが、いわば認証のための準備。次は、いよいよ認証をして実際につぶやいてみる。この最初の認証の部分が、Twitter gemのバージョンアップに伴い若干変更されたので要注意。

require 'rubygems'
require 'twitter'

client = Twitter::REST::Client.new do |config|
  config.consumer_key        = "API key"
  config.consumer_secret     = "API secret"
  config.access_token        = "Access token"
  config.access_token_secret = "Access token secret"
end

client.update("rubytest")

やはり引用符を忘れないように注意。
上手く行けば、認証を行ったアカウントで、「rubytest」とツイートされているはずだ。

実際につぶやく際のメソッドは、このサイトが参考になりそう。
http://route477.net/w/RubyTwitterJa.html

Graphillionと最長片道切符

一部で話題になってた「組み合わせ爆発のお姉さん」の動画、気付いたら新作が出ていた。


元動画はこちら。やたら壮大なスケールで、組み合わせ爆発の怖さがわかる。

『フカシギの数え方』 おねえさんといっしょ! みんなで数えてみよう! - YouTube

そしてこっちが新作、というかフォローみたいな動画。

Graphillion: 数え上げおねえさんを救え / Don't count naively - YouTube


25万年、ひたすら数え上げしていたお姉さんはいったいどうやって救われるのかと思って動画を見てみたら、特に新ストーリーとかではなく、ゆっくりボイスでグラフを扱うスクリプトの紹介をされていた・・・なんだ・・・

と思ったが、このGraphillionPythonのモジュールで、複雑なグラフも簡単な記述で扱えちゃうスグレモノらしい。この動画では電力ネットワークの例題が上げられているが、ほかにも東京近郊区間大回りの計算なんかもサクッとできてしまうようだ。この大回り計算は、その名もekillionとしてweb上に公開されていて、だれでも簡単に大回りの経路を列挙して眺めて楽しむことが出来る。

このekillionで遊びながら、これを「最長片道切符」の経路の算出に応用したら結構簡単に経路が出せるのではないかとかふと思った。

最長片道切符のルートの計算については、葛西隆也氏が整数計画法という数学的手法を用いて計算したり(
最長片道きっぷの経路を求める
)、近藤英明氏が整数計画法をExcelのソルバーに落としこむ(EXCELによる最長片道ルート探索)など、既にいくつかの例がある。
葛西氏は商用のソルバーによる計算を行っていたが、フリーのソルバーGLPKを使用する方法もサイト上で紹介されている。私もこれで経路を計算したことがあるが、当時所有のPCでは計算に非常に長い時間がかかり、ツール内部の理解も十分には出来なかった。整数計画法で問題を解くには、片道切符の発券条件を線形の「制約式」に置き換える必要があるが、その制約式の作り方がトリッキーで、自分で組み立てるのは困難であった。

Graphillionを使用すると、制約式を作る作業無しに最長片道切符の経路の探索ができ、問題の変更も比較的簡便にできるようなので、少し遊んでみると楽しそうだ。

RubyでTwitterにポスト(未完)

Cygwin上で動作するRubyのスクリプトプログラムの中で、Twitterに動作状況をポストできるbotを作ろうと思って、少し調べてみた。

RubyでどのようにTwitterを使おうか調べた所、とても簡単なライブラリがあることを知った。

Twitter by sferik
こちらがそのライブラリ(Rubyではこういったライブラリをgemというようだ)の作者におる説明。Rubyはほとんど書いたことないが、これを見る限りとても簡単に扱えそうであった。
(gemに関しての説明はhttp://www.ruby-lang.org/ja/libraries/が詳しい)



http://rubyinstaller.org/downloads/

gemのインストールは簡単で、Cygwinのコンソールやコマンドプロンプトから、

$ gem install twitter

と打つだけで、必要なものが一式まとめてインストールできる。

まずOAuthでアプリの認証をする(このへんは別の記事で)。そして、最初に紹介したサイトにあるように、認証キーの設定をしてポストにチャレンジしてみる。
実際につぶやく部分は1行で済んでおり、ごくごく簡単にTwitterを活用できそうだ。

require 'rubygems'
require 'twitter'

Twitter.configure do |config|
  config.consumer_key = YOUR_CONSUMER_KEY
  config.consumer_secret = YOUR_CONSUMER_SECRET
  config.oauth_token = YOUR_OAUTH_TOKEN
  config.oauth_token_secret = YOUR_OAUTH_TOKEN_SECRET
end

Twitter.update("rubytest")

これを適当なファイル名で保存し、Cygwinから実行してみたが・・・
・・・残念ながら、下のようなエラーが出てきた。

/usr/lib/ruby/1.9.1/net/http.rb:800:in `connect': SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed (Twitter::Error::ClientError)
        from /usr/lib/ruby/1.9.1/net/http.rb:800:in `block in connect'
        from /usr/lib/ruby/1.9.1/timeout.rb:69:in `timeout'
        from /usr/lib/ruby/1.9.1/timeout.rb:100:in `timeout'
        from /usr/lib/ruby/1.9.1/net/http.rb:800:in `connect'
        from /usr/lib/ruby/1.9.1/net/http.rb:756:in `do_start'
        from /usr/lib/ruby/1.9.1/net/http.rb:745:in `start'
        from /usr/lib/ruby/1.9.1/net/http.rb:1285:in `request'
        from /usr/lib/ruby/gems/1.9.1/gems/faraday-0.8.7/lib/faraday/adapter/net_http.rb:75:in `perform_request'
(以下略)

Twitterは更新されていなかった。

エラーの文で調べてみると、どうも、SSL通信がうまくいっていないようだ。
SSL通信を行うために必要な証明書関連に問題があるらしい。


SSLの認証を切ったらどうなるか試すため、

require 'openssl'
OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE

という宣言を入れてみたら・・・

/usr/lib/ruby/gems/1.9.1/gems/twitter-4.8.1/lib/twitter/response/raise_error.rb:21:in `on_complete': Could not authenticate you (Twitter::Error::Unauthorized)
        from /usr/lib/ruby/gems/1.9.1/gems/faraday-0.8.7/lib/faraday/response.rb:9:in `block in call'
        from /usr/lib/ruby/gems/1.9.1/gems/faraday-0.8.7/lib/faraday/response.rb:63:in `on_complete'
        from /usr/lib/ruby/gems/1.9.1/gems/faraday-0.8.7/lib/faraday/response.rb:8:in `call'
        from /usr/lib/ruby/gems/1.9.1/gems/faraday-0.8.7/lib/faraday/request/url_encoded.rb:14:in `call'
(以下略)

Could not authenticate you・・・そうですか・・・
これはうまく行かなさそうなので、大人しく証明書を何とかすることに。

まず、証明書がどこにあるのかをirbで調べてみた。

irb(main):001:0> require 'openssl'
=> true
irb(main):002:0> p OpenSSL::X509::DEFAULT_CERT_FILE
"/usr/ssl/cert.pem"
=> "/usr/ssl/cert.pem"

このディレクトリを確認したら、なんと"cert.pem"などというファイルが存在しない!

ここでTwitterSSL通信について、以下のサイトなどを参考にしてみた。
https://dev.twitter.com/docs/security/using-ssl
http://d.hatena.ne.jp/otherworld/20101020/1287551203
http://d.hatena.ne.jp/kesikaran/20100213/1266069166

あまりしっかり理解していないが、"VeriSign Class 3 Extended Validation"なる証明書が必要とされている模様。
そして、この証明書(と他色々な証明書がセットになったファイル)は

http://curl.haxx.se/ca/cacert.pem

からダウンロードできる模様。
ここから証明書を引っ張ってきて、あるべきディレクトリに放り込んだ。そしてbotを実行すると・・・

/usr/lib/ruby/gems/1.9.1/gems/twitter-4.8.1/lib/twitter/response/raise_error.rb:21:in `on_complete': Could not authenticate you (Twitter::Error::Unauthorized)
        from /usr/lib/ruby/gems/1.9.1/gems/faraday-0.8.7/lib/faraday/response.rb:9:in `block in call'

またかよ!ということは、この"Could not authenticate you"というエラーは、証明書関連とは別のところか。
認証がうまくいってないような気がする。

http://stackoverflow.com/questions/15146767/twitter-could-not-authenticate-you-error
http://stackoverflow.com/questions/17060459/how-do-i-retrieve-a-status-from-twitter-with-the-oauth-gem-directly
https://dev.twitter.com/docs/auth/oauth/single-user-with-examples#ruby
http://doruby.kbmj.com/daoka_tips/20100302/ruby_oauth_

ここらへんを見て、頭を抱え中。

gnuplotのPNG出力でアンチエイリアス

gnuplot(Ver4.6)で関数やデータをプロットして、どこかに貼り付けたいというとき、

set terminal XXX
set output "(filename)"
replot

と打てばいろいろな形式で保存が可能(Windowsなら戻すにはwxtと打てばOK)。
XXXにはだいたい拡張子を入れれば、あまり考えなくても好きな形式で出力できるのは魅力だが、PNGで普通に出力しようとすると以下のような、アンチエイリアスのかかってない画像が出てくる。

(コマンド)

set term png size 400,300
set output "normal.png"
plot sin(x),0.5+0.3*cos(x), -sin(x/2+1) 

(結果)
f:id:halpha656:20130304184246p:plain

見栄えの良いグラフを貼り付けるには、PNGではなくベクタ形式の画像(MS Office系ならemf形式、TeXならepsが良い感じ)にするのが手っ取り早いが、どうしてもラスタ形式で出力したいということもあって悩ましかった。

そんな時は、"png"とは別の"pngcairo"という出力ターミナルを使えばいい。

(コマンド)

set term pngcairo size 400,300
set output "cairo.png"
rep 

(結果)
f:id:halpha656:20130304184645p:plain

こんな感じで、アンチエイリアスのかかったきれいな画像が出力される。その分ファイルサイズも4倍近くに大きくなるが。

GCC(G++)でC++をコンパイルすると動作が止まった

GCCには、C++コンパイラ(g++)も含まれている。行列演算とかでC++のライブラリを使ってみたくなったのでテストしてみた。

#include <iostream>
using namespace std;

int main(){
    double k=1.0138100;
	
	cout << "テスト" << endl;
	cout << "Hello, C++!!" << endl;
	cout << k << endl;
	return 0;
}

これをコマンドプロンプトから、

>g++ hello.cpp -o hello.exe
>hello

とコンパイルして実行してみたところ、

テスト
Hello, C++!!

とまで表示されたはいいものの、「hello.exe は動作を停止しました」というWindowsのエラーメッセージが表示され止まってしまった。何故かと調べていた所、どうやらMinGWのライブラリのリンクがうまく行っていないために、Windowsでは実行できなかったようだ。ちなみに、MSYSのほうからコンパイルして実行すると、

>g++ -static hello.cpp -o hello_s.exe
>hello_s
テスト
Hello, C++!!
1.01381
>

と問題なく実行できて終了した。

これを解決するためには、g++のオプションで「-static」を付け、ライブラリを静的にリンクする必要があるようだ。このオプションを入れると、たしかにコマンドプロンプトからでも最後まで正常に作動するようになった。

ただしこれはMinGWに含まれているライブラリがexeファイル内にリンクされるらしく、ファイルサイズが増えてしまう。実際、このプログラムもstaticオプションなしでは26kBだったが、staticをつけることで1,278kBにもサイズが膨れ上がってしまった。でも、動かないよりはマシかと考え、当面はこれで動かすことに。

ちなみに、このプログラムで動作を止めた犯人は、

cout << k << endl;

の部分らしく、ここを削除したらstatic無しでもエラーを吐かなくなった。とはいえ、数値計算で途中経過をディスプレイに出せないのは痛い。

GCCを導入してみた

計算が速いということでよく利用されるC/C++。そのコンパイラとして、Visual C++をよく使っていたけど、なんかちょっとした計算をしたい時にいちいち起動してコンパイル、っていうのがどうも面倒くさくなってしまった。そこで、GCCを導入してみた。

GCCにはMinGW版とCygwin版があるが、別にCygwinUNIXっぽい環境は作らなくていいかなとなんとなく感じたので、MinGWをインストールすることに。

MinGW - Minimalist GNU for Windows - Browse Files at SourceForge.net

ここからとりあえず、「mingw-get-inst-20120426.exe」をダウンロードし、インストール。

 

コンパイルは、コマンドプロンプトからなら

C:\test\>gcc hello.c -o hello.exe

と打ってやればいける。

 

ちなみに、MinGWをインストールすると、MSYSというターミナルのようなものが付いてくる。インストール先がC:\MinGWだとしたら、C:\MinGW\msys\1.0の下に「msys.bat」というバッチファイルが出てくる。こいつを開くと、

User@User-PC ~
$

なんてプロンプトが色付きで出てくる。こちらからでもgccは使えるし、lsとかUNIXっぽいコマンドも使えるので、ここから作業することもできそう。ちなみにホームフォルダは、<MinGWインストール先>\msys\1.0\home\<User名>である。