tomatomax.net

Every rule has its exception.

Archive for the Ruby Category

uninitialized constant Class::YAML (NameError)

自宅サーバのアップデートを行ったのだけれども、そのあといつの間にかBotが止まっていた。いつもはcronで動かしているけど、手動で実行してみると以下のエラー。

/usr/lib/ruby/gems/1.8/gems/twitter4r-0.3.0/lib/twitter/console.rb:23:in `from_config’: uninitialized constant Class::YAML (NameError)

とりあえずエラーメッセージでぐぐってみると、以下の記事がヒット。

environment.rb uninitialized constant when starting console

この記事に書いてあるとおり、require ‘yaml’を追加することで無事復活。今は反応するようになってます

さくらサーバにWWW::Mechanizeをインストール

さくらインターネットのサーバでこのブログを運用しているわけですが、せっかくサーバを借りてみたのでRubyスクリプトを動作させたい!

でもWWW::Mechanizeが入ってません><

rubygemも入ってません(こっちはまあ当然か)

さくらのレンタルサーバーに RubyGems をインストールする手順を参考に、rubygemをインストール。ここまでは問題なし。

続けてmechanizeのインストールに取り掛かったのだけれども、こっちは失敗。

%gem18 install nokogiri
Building native extensions. This could take a while…
ERROR: Error installing nokogiri:
ERROR: Failed to build gem native extension.

/usr/local/bin/ruby18 extconf.rb install nokogiri
checking for #include … no
libxml2 is missing. try ‘port install libxml2′ or ‘yum install libxml2′
*** extconf.rb failed ***
(略)

libxml2がないそうです。自分のサーバなら、書いてあるようにyumでインストールするところなんだけど、当然できません。なので、ユーザフォルダにlibxml2を展開してみる。
The XML C parser and toolkit of Gnomeからlibxml2をダウンロード。prefixにユーザフォルダを指定してインストールします(UserNameを自分のユーザ名に置き換える)

%./configure –prefix=/home/UserName
%make
%make install

あとはインストールしたヘッダを読むように指定してmechanizeのインストール

%gem18 install mechanize — –with-opt-include=/home/UserName/include/libxml2
Building native extensions. This could take a while…
Successfully installed nokogiri-1.2.1
Successfully installed mechanize-0.9.2
2 gems installed
Installing ri documentation for nokogiri-1.2.1…
Installing ri documentation for mechanize-0.9.2…
Installing RDoc documentation for nokogiri-1.2.1…
Installing RDoc documentation for mechanize-0.9.2…
%

今度は成功。ページも取得できているみたい。

net/imapでGMailのメールを取得

自動でfollowするように、GMailに届いたfollow通知からユーザ名を取り出してみる。IMAPでやってみた版。

参考:katoy: cocolog: ruby で gmail (imap) にアクセスする練習

require 'net/imap'

followers = []
username = "hoge"
password = "hogehoge"

port = 993
usessl = true

begin
imap = Net::IMAP.new("imap.gmail.com", port, usessl)
p imap.greeting
imap.login(username, password)
imap.examine("inbox")
imap.fetch(1..-1, "ENVELOPE").each do |f|
messageNo = f.seqno
subject = f.attr["ENVELOPE"].subject
to_mailbox = f.attr["ENVELOPE"].to[0].mailbox
if to_mailbox == "hoge+bot" && # To:がhoge+bot@gmail.comのものを取り出す
/is now following you on Twitter/ =~ subject
imap.fetch(messageNo, "RFC822").each do |m|
# ユーザ名を得る
if /http:\/\/twitter\.com\/(\w+)/ =~ m.attr["RFC822"]
p $1
followers << $1
end
end
end
end
followers.each { |u|
follow(u)
}
p "success"
rescue
p "failed"
end

twitter4r その2

例のBotがFollowされるたびに、手動でFollowをする必要があった*1ので、twitter4rのfriend関連の機能を使って、自動Followを実装してみた。

def follow(friend_name)
config_file = File::join(File.dirname(__FILE__), "conf_m.yaml")
twitter = Twitter::Client.from_config(config_file, "prod")
fri = twitter.my(:friends)
fri.each { |user|
return if user.screen_name == friend_name
}
twitter.friend(:add, friend_name)
end

ログインしているところは前と同じ。my(:friends)でログインユーザのFollowing一覧を取得して、既に登録している人は弾く。friend(:add, ユーザ名)で指定したユーザをFollowする。

メールサーバを動かしていないので、一定間隔でGMailを見て新規Followerを取得してます。こっちは毎分動かしているわけではないのですぐにFollow返しはできません><

*1:大変けまらしい勢いでした

twitter/Hinnyu

Twitter / Hinnyu

Followingの特定の言葉に激しく反応します。手動Botと言われてる人に対抗するために作ってみました。現在自動Followを行っていないので登録まで時間がかかります。自動Followのテスト中です。登録まで大体1時間ほどかかります。

胸板が厚いのでPicture書いてくれる人募集中。

以下、実装の詳細とか。

反応するのは以下の正規表現。

reg = /(貧乳|ひんにゅう|ひんぬー|ひんにゅー|ペチャパイ|まな板|胸ぺったん|ベニア板|洗濯板)/

語尾のパターンはこんだけ。あんまりパターンがないのでこれも案募集中。

message = ["と申されたか!", "と申されたな!", "は正義!", "はステータスだ 希少価値だ", "と申しましたな!?", "はいいものだ"]

以下の2つには反応しない。

  • マッチする正規表現+”bot” (大文字小文字無視)
  • /^@w+s/

2番目のように、今のところBot本体に@投げられても無視します。

あと、同じ発言に何回も反応しないように、最新の発言のidを記録しておいて、それ以上昔の発言は追わないようになってます。

元ネタの手動Botから引き継いだ機能として、一部の人にはまったく違う反応をします。下のコメント欄とかtwitterで@やDMいただければ一部の人に追加する対応もします(Botの名前とは全然関係なくなりますが)。

twitter4r

ruby用twitterライブラリ。Botを作るのに使ってみた。

詳細はTwitter4R v0.3.0: Open Source Ruby Client Library for the Twitter REST APIを参照。

まとめ

  • “gem install twitter4r”でインストール
  • YAMLファイルにログインユーザ名、パスワードを記述
  • Twitter::Client.timeline_forでタイムライン取得。パラメータに:friendを指定するとFollowing込みのタイムラインを取得できる
  • Twitter::Client.statusで発言する

インストール

gem install twitter4r

公式には上のコマンドになっているけど、うちのマシンでやると”Bulk updating Gem source index for: http://gems.rubyforge.org“で止まったままになるので直接gemをダウンロードしてきてインストール。jsonが必要なので先にインストール。

gem install json-1.1.2.gem
gem install twitter4r-0.3.0.gem

ユーザ認証

YAMLファイルを作成してそこにユーザ名とパスワードを書く。

test:
login: hoge
password: hogehoge
prod:
login: foo
password: bar

作成したファイルは以下のように読み込む。同時にtwitterクライアントのインスタンスを取得。

twitter = Twitter::Client.from_config(config_file, "prod")

この例では”prod”を指定しているので”foo”の方でログインする。

必ずファイルに生パスワード書かないといけないのかどうかがよくわからんかった。irb用のHelperとは書いてあるのだけれども、他にログイン用のメソッドがあるのかな?

タイムラインの読み込み

timeline_forでタイムラインを取得。引数に:friendを指定するとログインユーザのFollowingを含めたタイムラインを取得ができる。他には:publicでパブリックタイムライン、:meで自分の発言のタイムラインを取得可能。引数を追加すると発言を絞り込める(id, since等)。当然twitter APIの制限を受けるので1時間に70回しか使えない。

twitter.timeline_for(:friend).each { |friend|
if /@foo/ =~ friend.text
twitter.status(:post, "@" + friend.user.screen_name + " " + message)
end
}

取得したタイムラインから各ユーザの発言を取り出す時は、friend.textが各ユーザの発言、friend.user.screen_nameがユーザ名になる。

発言

statusの引数に:postを指定して第二引数に発言内容を指定する。

twitter.status(:post, "@" + friend.user.screen_name + " " + message)

感想

JavascriptでAPI直叩きのときよりはるかに簡単で使いやすい。Botの基本動作作るのに2時間もかからなかったのは満足。一番時間がかかってたのが”gem install twitter4r”だからなぁ。

Feed::rememberthemilk

PraggerでRTMのデータを取得するためのプラグインを作ってみた。出力形式はどうするのがいいんだろ。

pragger-feed-rememberthemilk – Google Code

Praggerさわってみた

「はらへった」と検索するとピザが注文されることで有名なPlaggerのruby版。ruby1.8.5以上が必要なのだけれども、aptitudeで落とそうとすると最新が1.8.2になっていたので、結局ソースからビルド。

落としたら後はサクサク……と行きたいところだったのだけれども、各モジュール間のデータの受け渡しの形式が良くわからなくて苦労した。

下のは2chのヘッドラインRSSから特定のカテゴリのヘッドラインを1行で出力するYAML。

- module: RSS::load
config:
url: http://headline.2ch.net/bbynews/news.rss
- module: Filter::get_title
- module: Filter::grep
config:
regex: "\[[ビ速二]"
- module: Filter::subs
config:
regex: "\[[ビ速二][+軍]\] "
to: ""
- module: concat
config:
- module: stdin
- module: Filter::subs
config:
regex: "\n"
to: ""
- module: Filter::uniq
- module: stdout

Filter::get_titleとFilter::uniqは一応自作。ほとんど手がかかってないけど。

def get_title(config,data)
r = []
data.each {|i| r.push(eval("i.title")) }
return r
end
def uniq(config, data)
return data.uniq
end

実行方法。

$ ./pragger -c rss2ch.yaml < output1.txt > output2.txt
...
$ cat output2.txt
【米国】ロサンゼルス南部でファストフード店廃止案
【滋賀】医療保険金還付と不審電話相次ぐ
【企業】赤福、年内営業再開は困難 14日に改善計画書
$

標準入出力を使っているのは変えたい。あとはデータ量を抑えたいのでリストの要素数を制限するフィルタを作るか。