「IT勉強会カレンダー」の新着エントリをTwitterに自動投稿

前回の記事、「IT勉強会カレンダー」から、1日単位でエントリを切り出す をベースに、勉強会情報をTwitterに自動投稿するスクリプトを作ってみました。

仕様は若干変更して、「指定した日に開催される勉強会」ではなく「昨日1日間に更新されたエントリの内容」を投稿するようにしています。少なくとも私にとってはその方が便利そうなので。新着記事だけでなく、一定期間ごとにリマインダを投稿する方が良さそうですが、それは今後のお楽しみということで。

とりあえずテスト用Twitterアカウントを対象に手動実行したところでは期待通りに動いているようなので、今後専用アカウントを作って、cronで自動実行することを考えています。

メモ

  • Twitterへの記事投稿のために、Ruby用のTwitterクライアントライブラリ「Twitter4R」を使用
  • Google Calendar/Google Data APIへのクエリとして、以下のものを使用
    • update-min: エントリ更新日時が指定日時以降であるもののみ取得
    • update-max: エントリ更新日時が指定日時より早いもののみ取得
    • orderby=startime: エントリの並び順を、イベント開始日時に従って決定
    • sortorder=ascending: エントリの並び順を昇順に指定
    • max-results: 取得するエントリ数の上限 (デフォルトは25の模様)
  • 取得したエントリのフィルタリング条件として、Rubyスクリプト側でイベントタイトルに正規表現を適用
  • 取得したAtomフィードからイベント日時を取り出すために、content要素から "期間:" または "開始日:" で始まる文字列を正規表現マッチングで切り出し
  • 地味な工夫として、Date#wdayを用いて、イベント日時に対応する曜日を取得

ソースコード (抜粋)

#!/usr/bin/ruby

require 'rubygems'
require 'twitter'
require 'rss'
require 'erb'
require 'open-uri'
require 'date'

# 日付文字列をURLエンコード
def encode_date(date)
  ERB::Util.u(date.strftime('%Y-%m-%dT00:00:00+09:00'))
end

# エントリ更新日付の範囲を、前日1日間に指定
update_min = Date.today - 1
update_max = update_min + 1

query =  'updated-min=' + encode_date(update_min) + '&' +
  'updated-max=' + encode_date(update_max) + '&' +
  'orderby=starttime&sortorder=a&max-results=50'

# Google Calendar APIを呼び出してAtomフィード取得
atom_feed = nil
open("#{FEED_URL}?#{query}") {|fin|
  atom_feed = fin.read
}

# 取得した各イベント情報についてフィルタを適用し、パスしたものを
# Twitterに投稿
FILTER_TITLE = /^(\[神奈川)|(\[東京)/
WDAYMAP = ['', '', '', '', '', '', '']
twitter = nil
rss.entries.each {|entry|
  next if entry.title.content !~ FILTER_TITLE
  date = (entry.content.content =~ /(期間|開始日): ([0-9\/]*)/) ?
    "#{$2}(#{WDAYMAP[Date.parse($2).wday]})" : ""
  link = (entry.content.content =~ %r|予定の説明: (http://.*)$|) ?
    $1 : ""
  message = "#{date} #{entry.title.content} #{link}"

  if twitter == nil
    twitter = Twitter::Client.new(:login => TWITTER_USER,
                                 :password => TWITTER_PASSWORD)
  end
  twitter.status(:post, message)
}