4.1 コンちゃん(Lunatic)について
おちんちんっていうツイートする。その各々でいいとする必要単位を開発してる
— コンちゃん(Lunatic) (@lunatic_club) 2013, 12月 5
前のコンちゃんはEasyBotterを利用していたためPHPで作成していたが、色々と自由度が減る上にcronサービス*1が不安定だったため、あまり良い出来とは言えなかった(ウザさは一級品だったが)。
というわけで、今回はRubyとTwitter gemを使ってTwitter botを作成した。
その軌跡について、かなり詳しく書いてみる。
準備
実は、今回のbot作成はRubyとgemの扱いに慣れることが主目的だったりする(他にやりたいことがある)。
開発環境は様々なことを考慮してLinuxの方が良いので、Virtual Boxを利用してUbuntu12.04をWindows7に入れた。実は既にUbuntuはデュアルブートさせているのだが、いちいち再起動するのがめんどいのと作業中に遊びが欲しいのでWindowsと同時に使えるVirtual Boxを選択した。
Ubuntuの何がいいかって、欲しいパッケージは魔法の呪文「sudo apt-get install hogehoge」でダウンロードからインストールまで全部やってくれるってところ。Androidと同じような感じ。開発環境とかWindowsより圧倒的に楽に準備できる。あと、Terminalカチャカチャやってるとなんだかカッコいいです。
Ruby
プログラミング言語Rubyは、日本人によって作られたオブジェクト指向言語である。
と言っても、Rubyは恐ろしく自由度が高い言語で、そもそもmain関数というものが存在しない。基本的にはコードの一番上から順番に実行される。でも、関数やクラスを用いてパッケージングも出来る。
puts 'Hello World!!' # RubyのHello World
数字や演算子に至るまですべてがオブジェクト(Java風にいうとインスタンス)であり、それらには初めから基本メソッドが存在している。基本メソッドは結構な数があり、痒いところに手が届くように作られていて非常に快適にコーディング出来る。
108.to_s # 108を文字列に変換する string.to_i # 文字列stringを整数に変換する
また、変数宣言が必要ない。変数は、必要に応じて型が自動的に割り当てられる。型ももちろんオブジェクト。
hoge = 7 # 整数型になる st = '文字列' # 文字列型になる hoge = 'ふが' # 文字列型に変換される puts hoge # 'ふが'が表示される(7は表示されない)
配列は動的で、異なる型(クラス)のオブジェクトを1つに配列に入れることも可能。
list = [1, '2', hoge] # できる。
正規表現を気軽に扱えるのも強みだ。
st = 'this is a pen.' st = st.gsub(/[^ ]*is/, 'hoge') # //で囲まれた部分は正規表現と見なされる puts st # hoge hoge a pen.
また、対話環境も自動でついてくる。irbで起動できる。
ちなみに、インタプリタなので、
% ruby hogehoge.rb
これでプログラムを実行する。今回はRuby1.9.3で作成した。
gem
Rubyに必要なライブラリを自動的に落としてくれる便利なもの。
% gem install hogehoge
これでhogehogeというライブラリを落としてきてくれる。重いものを落としているとフリーズしたように見えるのが欠点。
今回はgem1.8.23を使用した。
必要なライブラリを落としまくる
先ほどのgemでどんどん入れていく。以下、gem install hogehogeを使うこと。
Twitter APIを叩く便利なもの。たぶん公式ドキュメント→http://rdoc.info/gems/twitter
自由度がとても高い。すごい。
アカウントを準備する
アカウントを作って、Twitter Developersでログイン。新しくアプリケーションを作成する。OAuthのAccess LevelはRead and Writeとしておき、アプリの管理画面からAccess Tokenを生成する。
Access Tokenの生成が終わったら、Consumer Key、Consumer Secret、Access Token、Access Token Secretの値をメモっておく。
コードを書く
とりあえず呟かせてみよう。
# -*- coding: utf-8 -*- require 'twitter' # アクセス設定 client = Twitter::REST::Client.new do |config| config.consumer_key = "さっき取得したやつ" config.consumer_secret = "さっき取得したやつ" config.access_token = "さっき取得したやつ" config.access_token_secret = "さっき取得したやつ" end client.update("テスト")
これで「テスト」と投稿されるはずである。
その他の例については、公式(たぶん)を見て欲しい。
マルコフ連鎖
おちんちんっていうツイートする。その各々でいいとする必要単位を開発してる
— コンちゃん(Lunatic) (@lunatic_club) 2013, 12月 5
コンちゃんは、このような文章を延々と吐いている。これは、コンちゃんのTLから拾った文章を形態素解析して、マルコフ連鎖でランダムに繋げて文章を生成しているのである。
マルコフ連鎖の数学的定義は置いといて、コンちゃんでは以下の様な仕組みで文章を生成している。
- 文章を形態素にバラす。この時、先頭には'__BEGIN__'タグを、最後には'__END__'タグをつける。
- それぞれ2つずつをペアにして配列に入れる。たとえば、"私はLです"だと["__BEGIN__","私],["私","は"],["は","L"],["L","です"],["です","__END__"]のようになる。
- 溜まった形態素ペア群からまず__BEGIN__を探してきて、ランダムに選択。次に、__BEGIN__とペアになっている単語を生成文章の後ろに追加し、次はペアになっていた単語から始まる形態素ペア群をランダムに選択、それを__END__が現れるまで繰り替えす。
形態素ペアはグループにする数を増やせば増やすほどまともな文章が現れる確率は上がる(もっとも、下手に数を増やしても元の文章とそのままのものが出てくる確率が上がってあまりおもしろくない)。今回だとグループは2つの要素を持つので、2次マルコフ連鎖と呼ばれるらしい??
かの有名なしゅうまい君も、基本的にはこのような仕組みで動いている。
というわけで、マルコフ連鎖部分だけコードを載せる~~
# -*- coding: utf-8 -*- def make_markov(texts) # textsから2次マルコフ連鎖で文章を生成。中身は1次配列。 message = '' # 返す文章 next_front = '__BEGIN__' # 次のデータの先頭文字 while 1 > 0 list = [] #候補に上がったデータ (texts.length).times do |m| if m%2 == 0 # データの先頭文字とnext_frontが一致すれば、その後の文字をリストにpush if texts[m] == next_front list = list + [texts[m+1]] end end end if list.length == 0 # リストが空なら終了。 message = '__FAILED__' break elsif message.length > 140 # 140文字以上なら終了。 message = '__FAILED__' break else m = list[rand(list.length)] if m == '__END__' break else message = message + m next_front = m end end end return message end
cronの設定
botのコードが無事書けたら、最後はcronを設定して定期的に実行させてやろう。
Ubuntuの場合、以下のように叩けば良い。
% crontab -e
初めて起動するときは使用エディタを聞いてくる。私の場合はemacsなのでemacsを選んだ。
なんかいっぱいコメントが書いてあるが、最後の行に以下のように書こう*2。
0,30 * * * * cd /hoge/fuga; /hoge/.rbenv/shims/ruby /hoge/fuga/bot.rb
これで、毎時0分と30分に"% ruby bot.rb"を起動してくれる。cdでrbファイルがある場所に移動しとく必要があるのと、すべてフルパスで書く必要があることに注意。
もちろん、Ubuntuの起動中にしか動かないぞ☆
あと、
% crontab -r
これは、設定したcronを確認なしで全て消去するコマンドなので注意。eとrは隣にあるからタイプミスし易いのだが…。色々と対策はあるので気になる人は各自対策しよう。