こういうわけで、7月からtwitterAPIを使うのにベーシック認証が使えなくなるのですね。
マイコミジャーナルではツイッターで記事につけたコメントを集めたまじつぶというサービスをやっとるんですが、「記事にコメントをつける」ところでベーシック認証を使ってる。なので代わりの方法であるOAuth(http://j.mp/arIIzhを参考にオォースと読むことにしてる)に切り替えないといけないんだけど何だかややこしい。ツイッターのボットでも作ってみたらいい演習になるんじゃない?と考えました。
どんなボットがいいかな〜と思って何週間か悩んでいたらテレビにねづっちがでていた。でツイッターに「ねづっちボットってあったら面白そう」と言ったら面白そうですねという反応があった( http://j.mp/9FMSIR http://j.mp/akKdff )のでやる気になりGWに作ることに。
※比較的最近使い始めたdeliciousのrubyタグはねづっちbot作成のための資料がほとんど
◯ねづっち作成方針
ー 大方針 ー
・@nezucchi_bot [お題] のように話しかけてもらい、[お題]でツイッターの世界を検索。全角文字がなるべく多く含まれるツイートを探し、これを[心]とする。[心]を、Yahooの形態素解析サービス( http://j.mp/abW6hx )を使って品詞単位に分割し、名詞だけを集める。この中から「全てひらがなでない・全て半角でない・[お題]とのレーベンシュタイン距離がなるべく遠い」という条件で候補を絞り、最終的に残ったものの中からランダムで[答え]を決定する。
こうして、「ととのいました![お題]とかけまして[答え]とときます、その心は[心]!」というなぞかけを完成させる。
・誰も話しかけてくれない時はお題を自分から探しにいく。はてなキーワードからランダムに1つを選択。
ー 小方針 ー
・一度答えたお題にそれ以降答えないようにするため、お題のツイートIDをDBに保存する。
・DBを簡単に利用するため、また、ActiveSupportの便利なモジュールを利用するため、Railsを使う。
・ボットはウェブアプリケーションではなくバッチで動かすので script/runner を使う。
・長いURLをポストする時は短縮する( bit.ly )
◯環境構築
まだあまり使いこなせていないMacの環境構築から始める。
OSバージョン:10.6.3
Core2 Duo, メモリ4GB
Rails : 2.3.5(初めからインストールされてた)
mysql : 5.1.46 http://j.mp/cOB7ch を参考に。64bit版じゃないと動かない
XCode : Cコンパイラとか。Mac付属のCDに入ってるらしいんだけどDLした( http://j.mp/cNzefy )
※Windowsの場合はVisual Studio 2008 Express Editionあたりをインストールすればよかろう。
・プロジェクトの作成
# rails nezubot --database=mysql
# cd nezubot
# ruby script/generate scaffold Nezubot postid:string user:string tubu:text
# vi config/database.yml -> adapter: mysql
# rake db:create
# rake db:migrate
・OAuthの準備をする
http://j.mp/9TbPyH を参考に4つのキーを取得する。
・短縮URLの準備をする
bitlyのAPIキーを取得しておく。
・Yahoo形態素解析の準備をする
yahooのAPIキーを取得しておく。
◯実装
長いので急所だけ。
#!/usr/bin/env ruby
# coding: utf-8
require 'json'
require 'rest_client'
require 'uri'
require 'oauth'
require 'pp'
require 'leven' # http://j.mp/awOXy6 を外部ファイル化
# URL短縮関数
def shorten(long_url)
id = 'xxx'
api_key = 'xxx'
req = "http://api.bit.ly/v3/shorten?login=#{id}&apiKey=#{api_key}&uri=#{URI.encode(
long_url)}&format=json"
json = RestClient.get req
jhash = ActiveSupport::JSON.decode json
jhash["data"]["url"]
end
# お題[q]から[心]を求める関数
def getBestres(q)
bestres = []
uri = "http://search.twitter.com/search.json?q=" + URI.encode(q) + "&locale=ja";
begin
json = RestClient.get(uri)
jhash = ActiveSupport::JSON.decode(json)
rescue
abort("rescued in getjson")
end
if( jhash["results"] != [])
jhash["results"].each{ |r|
if( r["text"].gsub(/[ -~。-゚]*/,"").length > 200 )
bestres = [r["id"],r["text"],r["from_user"],q ]
break
end
bestres = [r["id"],r["text"],r["from_user"],q ] if bestres==[]
}
end
return bestres
end #getBestres
# [心]から[答え]を決定する関数
def getBestnoun(bestres)
yuri = "http://jlp.yahooapis.jp/MAService/V1/parse?appid=[API_KEY]&results=ma,uniq&uniq_filter=9|10&sentence="
nounlist = []
if( bestres != [] )
yuri = URI.encode(yuri + bestres[1])
yhash = Hash.from_xml RestClient.get(yuri)
words = yhash["ResultSet"]["ma_result"]["word_list"]["word"]
words.each{ |w|
nounlist.push w["surface"] if w["pos"]=="名詞"
}
# 名詞ではなくても、括弧類で囲まれた語は候補に加える
nounlist.concat bestres[1].scan(/(「.*?」)|(\[.*?\])|(『.*?』)|((.*?))|(\(.*?\))/).
flatten
nounlist.uniq!
# casecmpは全角半角無視で文字列同士を比べる
nounlist = nounlist.select{ |i| (i=~/^[^ -~。-゚]*$/)==0 && !(i=~/^[ぁ-ん]*$/) && i.length
> 5 && i.casecmp(bestres[3])!=0 }
(中略)
return nounlist.rand
end
# 取得した4つのキーを使ってOAuthの下準備をする
begin
consumer = OAuth::Consumer.new(
CONSUMER_KEY,
CONSUMER_SECRET,
:site => 'http://twitter.com'
)
access_token = OAuth::AccessToken.new(
consumer,
ACCESS_TOKEN,
ACCESS_TOKEN_SECRET
)
response = access_token.get('http://twitter.com/statuses/mentions.json')
rescue
abort("rescued in oauth.new")
end
# 出してもらったお題を処理してなぞかけを送信する
JSON.parse(response.body).each do |status|
news = []
postid = status['id']
user = status['user']
tubu = status['text'].gsub(" "," ")
next unless (tubu=~/^(@|\.@|@)/)==0
ActiveRecord::Base.cache do
news = Spbot.find(:all, :conditions => ["postid = ?", [postid] ])
end
if( news==[] )
begin
q = tubu.split(' ')[1] || ""
next if(q=="")
bestres = getBestres(q)
bestnoun = getBestnoun( bestres ) || "error:#{q}:できませんでした!(T-T)"
ans = bestnoun
ans = ".@#{user['screen_name']} ととのいました! #{q} とかけまして #{bestnoun} とときます、その心は次のツイートで!" if !ans.include? "error"
if !$DEBUG
access_token.post('http://twitter.com/statuses/update.json','status'=> ans )
access_token.post("http://twitter.com/statuses/retweet/#{bestres[0].to_s}.jso
n") if !ans.include? "error"
end
rescue
abort("rescued in postid:#{postid}")
else
Spbot.create(:postid => postid, :user => user, :tubu => tubu) if !$DEBUG
end
end
end if ARGV[0] #JSON.parse
# 一人なぞかけは省略
◯定期実行
0 * * * * ( script/runner nezu.rb) # 一人つぶやき
*/10 * * * * ( script/runner nezu.rb reply) # お題に答える
◯完成品
http://twitter.com/nezucchi_bot
5/26 追記 別のねづっちボット