いけむランド

はてダからやってきました

お約束な発言をした人にフラグをセットする twitter bot「ふらぐったー」をつくってみました

[追記] このエントリはつくった時の話で今はいろいろと変わってます。詳しくは以下のエントリでどうぞ。


Ruby の勉強の成果を出してみようと twitter bot をつくってみました。


名前のとおり、死亡フラグをセットします。

つまり、以下のようなうっかり発言をすると...



「ふらぐったー」にフラグをセットされます。



動作は以下のとおりです。

  1. Twitter 検索で "俺" "たら" "するんだ" という文字列を含む発言の検索結果を RSS で取得します。
  2. 検索結果のうち、自分自身 (@flagtter) の発言は除きます。
  3. さらにその中でログファイルの最終更新時刻より新しい発言である場合は "俺.*たら.*するんだ" という正規表現を含むかチェックします。*1
  4. 上記の条件を満たしていたら、それ以外の部分をカットしたものをテンプレートの文字列 *2 に埋め込み、post します。

以上の処理を 5 分周期で cron に実行させています。*3

今のところ、条件にマッチする発言が 1 件/日くらい出現してしかないので気にしていませんが、短時間に条件にマッチする発言が複数、投稿された場合には取りこぼす可能性が大いにあります。

また、cron を動かしているサーバに負荷をかけてしまっているせいかどうかはわかりませんが 10% くらいの確率で ruby スクリプトの実行中に abort してしまうので、その時は bot 動作に失敗します。


更新頻度は低いと思われるので、かなり面白くないと思いますが、気が向いた方はフォローをどうぞ。


忘れてましたけど、以下にコードも晒しておきます。

  • Timeout がよくわからなかったので適当。
  • 文字列を UTF-8 にしたら、-Ku をつけないと動かなかったのは教訓。

[追記] id:rairairou のリクエストで自動 follow 機能を入れてみました。 *4

#!/usr/pkg/bin/ruby -Ku

require 'net/http'
require 'rexml/document'
require 'time'
require 'timeout'

FILE = ENV["HOME"] + "/var/log/flagtter.log"

USERNAME = "flagtter"
PASSWORD = "********"

Net::HTTP.version_1_2

log = File.open(FILE, "a")
result = ""

# 検索結果の RSS を入手
begin
  timeout(60) {
    result = Net::HTTP.get("twitter.1x1.jp",
                           "/rss/search/?keyword=俺+たら+するんだ&text=1")
  }
rescue
  # RSS が入手できなかったら終了
  log.close
  exit(0)
rescue TimeoutError
  # RSS が入手できなかったら終了
  log.close
  exit(0)
end

# RSS から文字列を抽出 → 投稿
rexml = REXML::Document.new(result)
rexml.elements.each("//rss/channel/item") { |item|
  name = item.elements["title"].text.gsub(/ <.*>/, "")
  if ((name <=> "@flagtter") != 0)
    time = Time.rfc822(item.elements["pubDate"].text)
    if (log.mtime < time)
      text = item.elements["description"].text
      if (text.match(/.*たら.*するんだ/))
        text.gsub!(/.*/, "").gsub!(/するんだ.*/, "するんだ")
        status = "#{text}』とつぶやいた #{name} にフラグがセットされました。"
        # 投稿
        begin
          timeout(60) {
            request1 = Net::HTTP::Post.new('/statuses/update.json')
            request1.basic_auth(USERNAME, PASSWORD)
            request1.body = "status=" + status
            Net::HTTP.start("twitter.com", 80) { |http|
              response = http.request(request1)
            }
            # 発言者をフォロー
            # 投稿に成功した場合だけフォローするようにする
            request2 = Net::HTTP::Post.new("/friendships/create/#{name[1..-1]}.json")
            request2.basic_auth(USERNAME, PASSWORD)
            Net::HTTP.start("twitter.com", 80) { |http|
              response = http.request(request2)
            }
          }
        rescue
          # ignore
        rescue Timeout::Error
          # ignore
        end
        log.puts "#{time} - #{status}" # 失敗してもログには追記
      end
      break # 1 回につき 1 投稿のみ
    end
  end
}

log.flush
log.close

*1:なので、もしかしたら関係ない発言を拾うかもしれません。

*2:"『#{text}』とつぶやいた #{name} にフラグがセットされました。"

*3:@meitantei_bot を参考にしました。

*4:name の先頭に @ があることを忘れててハマってました。name から @ を除いたものを API に渡さないといけませんでした。