はじめに
社内のコミュニケーションスペースにGoogle Homeが!その日は突然やってきました。が、ブームはすぐに過ぎ去り、話しかける人はごくわずかに...。後輩から「何かやらないと、使われなくなっちゃいますね」と、何故か圧力をかけられたので、緊急地震速報を流してみることにしました。
処理フロー
緊急地震速報は、緊急地震速報Botから取得することにしました。フォーマットも公開いただいているので、通知処理を自由に構築できます(ありがとうございます)。
また、どうせならSlackへも通知させよう、ということで以下のような処理フローにしました。
HubotでTwitterのタイムラインを監視し、推定M5.8以上、もしくは震度4以上の場合に通知させることにします。
前提として、Hubotが準備(インストール)できているものとします。Slackとは連携していなくてOKです(え?1)。
どうやったか
準備
まず、Hubotに監視させるTwitterアカウントを準備し、緊急地震速報Botをフォローしておきます。また、Twitter APIを利用するため、Twitter AppsからTwitterアプリを作成(Create New App)し、API KeyやAccess Tokenなどを入手しておきます。
Hubotスクリプト
Twitterの監視
Twitterの監視には、hubot-twitter-userstreamを利用しました。
$ npm install --save hubot-twitter-userstream
Hubotスクリプト(内部の詳細は次項以降に書きます)を作成し、
module.exports = (robot) ->
  robot.hear /(.*)/i, (msg) ->
    # (1) 緊急地震速報を取得
    ...
    # (2) 閾値判定(推定M5.8以上、もしくは震度4以上)
    ...
    # (3) Slackへ通知
    ...
    # (4) Google Homeで読み上げ
    ...
TwitterアプリのAPI KeyやAccess Tokenなどを環境変数に
$ export HUBOT_TWITTER_KEY="key"
$ export HUBOT_TWITTER_SECRET="secret"
$ export HUBOT_TWITTER_TOKEN="token"
$ export HUBOT_TWITTER_TOKEN_SECRET="secret"
のように設定した状態で
$ ./bin/hubot -a twitter-userstream
と実行すると、TwitterのタイムラインがHubotへ流れてきます。あとはこれを、煮るなり焼くなり...、です。
以下、Hubotスクリプトの詳細になります。
緊急地震速報を取得
    # (1) 緊急地震速報を取得
    if msg.message.user.name != 'eewbot'
      return
    vals = msg.message.text.split(',')
    if vals.length != 15
      return
    type      = vals[0]  # 電文の種別
    sign      = vals[1]  # 訓練識別符
    announce  = vals[2]  # 発表時刻
    status    = vals[3]  # 発表状況
    number    = vals[4]  # 電文番号 地震IDごとに00-99
    id        = vals[5]  # 地震ID
    time      = vals[6]  # 地震発生時刻
    latitude  = vals[7]  # 震源の北緯
    longitude = vals[8]  # 震源の東経
    place     = vals[9]  # 震央地名
    depth     = vals[10] # 震源の深さ
    magnitude = vals[11] # マグニチュード
    intensity = vals[12] # 最大震度
    flag      = vals[13] # 震源の海陸判定 0:陸 1:海
    alert     = vals[14] # 警報の有無 0:無 1:有
    if sign == '01' # 00:通常 01:訓練
      return
    if status == '0' # 0:通常
      caption = "■■■EEW(第#{number}報)■■■"
    else if status == '8' or status == '9' # 8,9:最終報
      caption = '■■■EEW(最終報)■■■'
    else # 7:キャンセルを誤って発表したとき(地震ID以降の項目は空)
      return
    overview = "#{place}で地震 最大震度 #{intensity}(推定)"
    canceled = false 
    if type == '35' # 35:最大震度のみ Mの推定なし
      details = "[詳細] #{time}発生 深さ#{depth}km"
    else if type == '36' or type == '37' # 36、37:最大震度 Mの推定あり
      details = "[詳細] #{time}発生 M#{magnitude} 深さ#{depth}km"
    else if type == '39' # 39:キャンセル
      caption = '□□□EEW(キャンセル報)□□□'
      canceled = true
    else
      return
    post = caption
    post += '\n' + overview + '\n' + details if !canceled
※「発表状況(status)」が7のデータを見つけられなかったので、とりあえず何もしないようにしました。
閾値判定(推定M5.8以上、もしくは震度4以上)
    # (2) 閾値判定(推定M5.8以上、もしくは震度4以上)
    filtered = true ;
    result = intensity.match(/[0-9]+[\.]?[0-9]*/)
    if result? and parseFloat( result[0] ) >= 4.0 # 震度4以上
      filtered = false ;
    if type == '36' or type == '37'
      if parseFloat( magnitude ) >= 5.8 # 推定M5.8以上
        filtered = false ;
    if status == '0'
      if number != '1' and number != '5' # 第1、5、最終報
        filtered = true ;
    if !canceled and filtered
      return
※通知の総数を減らすため、「第1、5、最終、キャンセル報のみ」という条件も追加しました。
Slackへ通知
    # (3) Slackへ通知
    data =
      channel: '#xxxxx'
      username: 'eewbot'
      text: post
    msg.http('https://hooks.slack.com/services/Txxxxxxxx/xxxxxxxxx/xxxxxxxxxxxxxxxxxxxxxxxx')
      .post(JSON.stringify(data)) (err, res, body) ->
        if err or res.statusCode != 200
          console.log 'Unknown error.'
※Slackへの通知には、Incoming WebHooksを利用しました。Webhook URLが必要になりますので、取得についてはSlackのWebhook URL取得手順などをご参照ください。
※実際のSlackへの通知内容はこんな感じになりました。
Google Homeで読み上げ
    # (4) Google Homeで読み上げ
    @exec = require('child_process').exec
    command = 'sh /home/xxxxx/announce/index.sh '
    if !canceled
      command += '緊急地震速報、' + overview.replace(' ', '、').replace(/\(推定\)/, '') + '、注意してください'
    else
      command += '先ほどの緊急地震速報はキャンセルされました'
    @exec command, (error, stdout, stderr) ->
      if error
        console.log error
      else if stdout
        console.log stdout
※シェルスクリプト経由で実行される別プロジェクトで、Google Home(google-home-notifier)での読み上げを行っています。これは、以前の記事で紹介した処理と同じですので、実装はそちらをご参照ください。
※実際の読み上げ内容は「緊急地震速報、東海道南方沖で地震、最大震度 3、注意してください」といった感じになります。
おわりに
「デスクはSlack、コミュニケーションスペースはGoogle Homeでカバー。カンペキ!」と作ってみたのですが、通知(読み上げ)の応答性に不安があり、実用に耐えない可能性が高そうです(え?)。「ケータイで十分なので、こんなのいらないっス」とか言われてしまう前に、Google Homeの活用方法、もう一度考えないと...。
- 
Slack通知のためではなく、Twitterのタイムライン監視目的でHubotを利用しました。将来Twitter Botとしても利用できそうですし...。 ↩ 

