Twitter ボットのエンハンス
複数の Twitter ボット (@housai, @domoraen, @aa_canvas) を作って運用しているんだけど、ソースが汚くて修正しづらい上に、OAuth 対応してなかったり、domoraen に関しては同じリプライに何度も反応してしまうバグがあったり、色々ダメな感じだったので色々エンハンスしました。
エンハンス項目
- ソースをきれいにする
- OAuth 対応
- domoraen のバグ修正
- リプライするボットは最後にリプライした id をどこかに保存しておいてリプライ済みのステータスには返事しないようにする必要があって、その id の保存先を YAML ファイルしてたら domoraen がリプライ考えている間ファイルがロックされちゃって id が読めなくなって何度も同じステータスにリプライ返しちゃうみたいなバグがあったので、
フバレコ様も使っている*1 TokyoTyrant で読み書きしてロックとか起こらないようにする
- リプライするボットは最後にリプライした id をどこかに保存しておいてリプライ済みのステータスには返事しないようにする必要があって、その id の保存先を YAML ファイルしてたら domoraen がリプライ考えている間ファイルがロックされちゃって id が読めなくなって何度も同じステータスにリプライ返しちゃうみたいなバグがあったので、
作ったライブラリ
上記の項目を総合して、かなり強引な感じのオレオレ Twitter Bot 用ライブラリを作った。
たとえば「単純に 'hello' とポストしたり、自分へのリプライを取得して 'hello' と言われたら 'hello' と返すボット」だったら以下のように書けます:
#!/usr/bin/env ruby $:.unshift(File.dirname(__FILE__)) require 'bot' bot = Bot.new('hello_bot') # ボットの Twitter ユーザ名をコンストラクタに与える bot.add_behavior(:tweet, Proc.new{ 'hello' }) bot.add_behavior(:reply, Proc.new{|r| if r['text'].match(/^@hello_bot\s+hello/) "@#{r['user']['screen_name']} hello" else next nil end }) bot.act!
個別ボットのスクリプトでは振る舞いだけを定義できるようにして、内部的な処理 (設定ファイルの読み込み、TokyoTyrant への接続、OAuth 経由での Twitter へのポスト) は全部 bot.rb に任せている感じです。
(ボット用ライブラリではないけど lib-text-converter とか sinatra-openid の「面倒くさい処理を全部ライブラリに任せて呼び出し側はやりたいことに専念する」みたいなコンセプトを参考にしています。)
運用方法
上記のファイルを bot.rb と同じディレクトリに置いて、こんな感じで起動します。
/path/to/hello_bot.rb tweet # Twitter に 'hello' と投稿 /path/to/hello_bot.rb reply # リプライを取得して 'hello' と言われたら 'hello' と返す
初めて起動すると、エディタを開いて OAuth の認証情報と TokyoTyrant の接続情報を聞いてきます (Pit を使ってます)。
--- tokyotyrant_port: tokyotyrant_port here consumer_key: consumer_key here consumer_secret: consumer_secret here access_token_secret: access_token_secret here tokyotyrant_hostname: tokyotyrant_hostname here access_token: access_token here
これさえ入力すればあとは TokyoTyrant で最後にリプライした id を読み書きしつつ、OAuth 経由で適当に Twitter へ投稿してくれるようになります。
crontab ビフォーアフター
もとがグダグダすぎたんだけど、実際に運用している crontab がかなり読みやすくなった。
エンハンス前:
0 * * * * /home/tily/bin/housai_twitter_bot/post_housai_haiku.sh 0,30 * * * * /home/tily/svn/MarkovChain/bin/markov speak -c /home/tily/bin/domoraen /config_domoraen.yaml -w 2 | /home/tily/bin/domoraeon/post.rb 2>&1 >/dev/null 0,30 * * * * /home/tily/bin/aa_canvas/post.rb /home/tily/bin/aa_canvas/config.yaml 2>&1 >/dev/null */5 * * * * /home/tily/svn/MarkovChain/bin/domoraen_reply /home/tily/svn/MarkovChain/config_doraemon.yaml 2>&1 /dev/null */5 * * * * /home/tily/bin/aa_canvas/bot.rb /home/tily/bin/aa_canvas/config.yaml 2>&1 >/dev/null
エンハンス後:
0 * * * * /home/tily/bots/housai.rb tweet 2>&1 >/dev/null 0,30 * * * * /home/tily/bots/domoraen.rb tweet 2>&1 >/dev/null 0,30 * * * * /home/tily/bots/aa_canvas.rb tweet 2>&1 >/dev/null */5 * * * * /home/tily/bots/domoraen.rb reply 2>&1 >/dev/null */5 * * * * /home/tily/bots/aa_canvas.rb reply 2>&1 >/dev/null
まとめ
ほんとは Gearman も導入してみたかったんだけど、フバレコみたいに極端に重い処理はしていないのでとりあえず保留でいいや。
今回の修正でだいぶ個別のボットに機能追加しやすくなったので、なんか面白い機能を付け加えたり、新しいボットを作ったりしたい。
*1:フバレコは Cabinet のほうでした via http://twitter.com/fuba/status/15546742151