東京電力のtweetを素早く察知する


前回東京電力のホームページが更新されたら、その内容をメールに送信するようにした。実際、それはちゃんと動いてくれたが、ちょっと問題があった。ホームページの更新のタイミングが遅いのだ!ニュースではすでに「計画停電は中止」と読み上げているのに、ホームページにはその情報が未だ載っていない...。せっかく作ったのに、テレビより遅いのはちょっと悔しい。
東京電力は、Twitterhttp://twitter.com/#!/OfficialTEPCO)でも情報を発信している。そこで、東京電力tweetしたら、それをメールで送信するように変更してみた。

Twitter API

  • 実際に上記URLにアクセスしてみると、以下のようなxmlが取得できた。
$ curl http://twitter.com/statuses/user_timeline/OfficialTEPCO.xml
<?xml version="1.0" encoding="UTF-8"?>
<statuses type="array">
<status>
  <created_at>Tue Mar 22 00:55:28 +0000 2011</created_at>
  <id>49997586458886144</id>
  <text>&#9632;&#9632;&#35336;&#30011;&#20572;&#38651;&#9632;&#9632;&#65299;&#26376;&#65298;&#65298;&#26085;&#65305;&#26178;&#65298;&#65296;&#20998;&#12363;&#12425;&#31532;&#65297;&#65319;&#12398;&#35336;&#30011;&#20572;&#38651;&#12434;&#23455;&#26045;&#12375;&#12390;&#12356;&#12414;&#12377;&#12290;&#12372;&#36855;&#24785;&#12434;&#12362;&#12363;&#12369;&#12375;&#12390;&#12289;&#35488;&#12395;&#30003;&#12375;&#35379;&#12372;&#12374;&#12356;&#12414;&#12379;&#12435;&#12290;
&#20379;&#32102;&#21147;&#12364;&#22823;&#22793;&#21427;&#12375;&#12367;&#12394;&#12387;&#12390;&#12356;&#12414;&#12377;&#12290;&#30342;&#12373;&#12414;&#12395;&#12399;&#19968;&#23652;&#12398;&#31680;&#38651;&#12398;&#12372;&#21332;&#21147;&#12289;&#12424;&#12429;&#12375;&#12367;&#12362;
&#39000;&#12356;&#12375;&#12414;&#12377;&#12290;</text>
  <source>web</source>
  <truncated>false</truncated>
  <favorited>false</favorited>
  <in_reply_to_status_id></in_reply_to_status_id>
  <in_reply_to_user_id></in_reply_to_user_id>
  <in_reply_to_screen_name></in_reply_to_screen_name>
  <retweet_count>100+</retweet_count>
  <retweeted>false</retweeted>
  <user>
    ...(中略)...
  </user>
  <geo/>
  <coordinates/>
  <place/>
  <contributors/>
</status>
<status>
  ...(中略)...
</status>

...以下、tweetの数だけ<status>が続く...
  • tweetは、textタグの中に記載されているが、&#XXXX;の連続で内容は読めない。(ブラウザでアクセスすれば、読めるのだけど)
  • どうやら、実体参照で返されているようだ。

tidyコマンド

  • そこで、tidyコマンドに渡してみると...
$ curl http://twitter.com/statuses/user_timeline/OfficialTEPCO.xml|tidy -xml -utf8 -i
<?xml version="1.0" encoding="utf-8"?>
<statuses type="array">
  <status>
    <created_at>Tue Mar 22 00:55:28 +0000 2011</created_at>
    <id>49997586458886144</id>
    <text>
    ■■計画停電■■3月22日9時20分から第1Gの計画停電を実施しています。ご迷惑をおかけして、誠に申し訳ございません。
    供給力が大変厳しくなっています。皆さまには一層の節電のご協力、よろしくお 願いします。</text>
    <source>web</source>
  ...(中略)...
  • うまく変換された!これで人が読める。

追加されたtweetを取得する

  • 次は、多くのtweetの中から、新しく追加されたものだけ取得したい。
  • tweetにはidも付加されている。
  • たぶん、17桁の数値は最新のidほど大きくなると予想して、
  • 前回チェックしたidを記録しておき、それより大きなidのtweetのみ取得するようにしてみた。

xmlをパースする

  • xmlはテキストのまま扱うと骨が折れるので、xmlとして扱いたい。
  • AppleScriptには、xmlを扱う仕組みが用意されていた。


tell application "System Events"
"<statuses type=\"array\">
<status>
<id>123456</id>
<text>テストです。</text>
</status>
<status>
<id>234567</id>
<text>サンプルです。</text>
</status>
</statuses>"
set xml_data to make new XML data with data result
--XML data id "名称未設定 6" of application "System Events"
set first_id to xml_data's XML element "statuses"'s XML element 1's XML element "id"'s value
--"123456"
set status_elements to xml_data's XML element "statuses"'s XML elements
--{ XML element 1 of XML element 1 of XML data id "名称未設定 4" of application "System Events",
-- XML element 2 of XML element 1 of XML data id "名称未設定 4" of application "System Events" }
end tell

なるほど!

  • make new XML data with data "xml書式のテキスト"によって、テキストをXML data オブジェクトに変換する。

xml_data変数に代入して...

  • xml_data's XML element "タグ名称"'s valueによって、タグが囲む値を取得する。
  • xml_data's XML element "statuses"'s XML elementsによって、statuses直下のXML elementをリストで取得する。

APIの実行回数制限

AppleScript

  • 以上のことを考慮して、コードを書き直してみた。


property twitter_account : "OfficialTEPCO"
property twitter_url : "http://twitter.com/"
property current_id : ""

on run
set current_id to my newest_id(twitter_account) my message("東京電力tweet", "監視を始めます。") end run

on idle
set tweets to my updated_tweets(twitter_account) if tweets ≠ {} then
set twitter_tepco_url to twitter_url & "#!/" & twitter_account
set mail_body to my join({} & twitter_tepco_url & tweets, return & return & return) my send_mail("東京電力tweet", "差出人@mail.com", "宛先@mail.com", mail_body) end if
my message("東京電力tweet", "チェックしました。") return 300 --5分ごとにチェック
end idle

--更新されたtweetのみ取得する
on updated_tweets(account) tell application "System Events"
set xml_text to my user_timeline_api(account, ".xml") set xml_data to make new XML data with data xml_text
set first_id to xml_data's XML element "statuses"'s XML element 1's XML element "id"'s value
set status_elements to xml_data's XML element "statuses"'s XML elements
set tweets to {} repeat with status_element in status_elements
set created_at_element to status_element's XML element "created_at"'s value
set id_element to status_element's XML element "id"'s value
set text_element to status_element's XML element "text"'s value
if id_element > current_id then
set tweets's end to text_element --& return & created_at_element
else
set current_id to first_id
return tweets
end if
end repeat
end tell
end updated_tweets

--最新のidを取得する
on newest_id(account) tell application "System Events"
set xml_text to my user_timeline_api(account, ".xml") set xml_data to make new XML data with data xml_text
set first_id to xml_data's XML element "statuses"'s XML element 1's XML element "id"'s value
end tell
end newest_id

on user_timeline_api(account, format) "curl http://twitter.com/statuses/user_timeline/" & account & format & "|tidy -xml -utf8 -i"
do shell script result
end user_timeline_api

--メールを送信する
--send_mail(件名, 差出人アドレス, 宛先アドレス, 本文)
on send_mail(a_subject, a_sender, a_recipient, a_body) tell application "Mail"
set new_msg to make new outgoing message ¬ with properties {subject:a_subject, sender:a_sender, content:a_body} tell new_msg
make new to recipient at end of to recipients ¬ with properties {address:a_recipient} send
end tell
end tell
end send_mail

--Growlあるいはdisplay alertでメッセージ表示
on message(title, msg) try
do shell script "/usr/local/bin/growlnotify " & title & " -m " & quoted form of msg & " 2>&1"
if result is not "" then error -128
on error
activate
display alert title message msg giving up after 4
end try
end message

--リストを、区切り文字で繋げてテキストにする
on join(sourceList, delimiter) set oldDelimiters to AppleScript's text item delimiters
set AppleScript's text item delimiters to delimiter
set theText to sourceList as text
set AppleScript's text item delimiters to oldDelimiters
return theText
end join

使い方

  • Mail.app(OSX付属のメーラー)でメールを送信できる設定になっている必要がある。
  • on idleハンドラ内の"差出人@mail.com"、"宛先@mail.com"を自分のMail.appの設定に合わせて修正する。
  • 上記AppleScriptを「twitter_mail.app」として、以下の設定で保存した。
    • ファイルフォーマット:アプリケーション
    • オプション:「実行後、自動的に終了しない」チェック入

試用

  • twitter_mail.appを起動してみると、5分ごとに東京電力のタイムラインをチェックしている模様。
  • タイムラインにtweetが追加されると、追加されたtweetをメール送信した。
  • 東京電力tweetは、情報の鮮度が良さそうなのでいい感じ!

しばらく使ってみる。

参考ページ

以下のページがたいへん参考になりました。感謝です!