トップ «前の日記(2012-01-28) 最新 次の日記(2012-02-03)» 編集

Public Diary


2012-01-29

[プログラミング] TwitterのツイートをtDiaryにポストするスクリプト (とりあえず最後)

昨日の続きです。

(1) tDiaryへの投稿

まず、実際に update.rb へツイートを投稿する部分を作ります。今までファイルに書き込んでいた write_to_file というメソッドを少し改造します。

参考にするのは、tDiary の contrib に含まれている posttdiary.rb というファイルで、これは電子メールで送付されてきた日記をウェブ経由で tDiary にポストするプログラムです。なぜファイルの入出力を使わないのかとも思いましたが、おそらく前方互換性なりファイルシステムの変更なりに対応するためということでしょう。ファイルI/Oが使えないなら最初からRubyで作る必要もなかったか…という気もしますが。

このあたりはあまり悩む必要もなく純粋にPOSTでデータを渡すだけなので、ほぼコピー&ぺーストで作っていきます。なお、update.rb の場所、Basic認証のユーザ名・パスワード等は、今のところグローバル変数で定義しています。

$tdiary_url   = 'http://.../update.rb'   # update.rb のURL
$tdiary_uname = '.....'   # update.rb にアクセスするのに必要な Basic 認証用ユーザ名
$tdiary_pass  = '.....'   # update.rb にアクセスするのに必要な Basic 認証用パスワード
$diary_heading = '![Twitter] 本日のツイート'  # 日記のサブタイトル
def post_tweets(day, tweets)
  if tweets.size > 0 then
    html_data = $diary_heading + "\n"
    tweets.sort {|x,y| x.created_at <=> y.created_at }.reverse.each do |t|
      html_data += tweet2html(t) + "\n"
    end

    data = "year=#{day.year}&month=#{day.month}&day=#{day.day}"
    data << "&body=#{CGI::escape html_data}"
    data << "&append=true"
    data << "&makerss_update=false"

    uri = URI.parse($tdiary_url)
    inre = /<input type="hidden" name="csrf_protection_key" value="([^"]+)">/

    Net::HTTP.start(uri.host, uri.port) do |http|
      auth = ["#{$tdiary_uname}:#{$tdiary_pass}"].pack('m').strip
      res, = http.get(uri.path,
                      "Authorization" => "Basic #{auth}",
                      "Referer" => $tdiary_url)

      if inre =~ res.body then
        data << "&csrf_protection_key=#{CGI::escape(CGI::unescapeHTML($1))}"
      end

      res, = http.post(uri.path, data,
                       "Authorization" => "Basic #{auth}",
                       "Referer" => $tdiary_url)

    end
    puts day.strftime("Posted tweets for %Y/%m/%d. Sleeping for 1 sec.")
    sleep 1
  end
end

(2) コマンドラインオプションに対応する

この時点で概ねやりたいことには対応していますが、動作検証しやすいよう、コマンドラインオプションを付け加えていきます。

以前は Ruby も他の言語と同様 getopts を使ってコマンドラインオプションをパースしていましたが、最近(Ruby 1.8.2以降)は OptionParser というものを使うそうです。

parser = OptionParser.new
cmd_opts = Hash.new("#{__FILE__}: post tweets to tdiary.\nUsage: #{__FILE__} [options]")
parser.on("-d", "--debug", "enter debug mode")      { |v| cmd_opts[:debug]   = true }
parser.on("-v", "--verbose", "enter verbose mode")  { |v| cmd_opts[:verbose] = true }
parser.on("--max-id ID", Integer,
          "retrieve tweets with id smaller (older) than ID") { |v|
           cmd_opts[:max_id]  = v }
parser.on("--days MANDATORY", Integer,
          "set maximum days to retrieve tweets")   { |v|
          cmd_opts[:days] = v }
parser.on("--cont", "continue from the previous run (read max_id from cache)") { |v|
          cmd_opts[:cont] = true
          cmd_opts[:max_id] = read_lastid_from_cache
}

begin
  parser.parse!(ARGV)
rescue OptionParser::ParseError
  $stderr.puts "#{__FILE__}: error #{$!}"
  exit 1
end

def debug_puts(txt, level=0)
  puts " " * level + txt if (cmd_opts[:debug] || false)
end

(3) できあがり

その他、リツイートされたツイートには RT マークを付けるなど細かいところを修正し、最終的にできあがったのが tw2td-20120129.tar.gz です。


1980|03|
1986|04|
1998|04|
2002|01|11|
2003|03|04|05|07|08|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|02|03|04|06|07|08|11|12|
2008|01|02|03|04|06|07|08|09|10|
2009|01|12|
2011|05|10|11|
2012|01|02|10|