はじめに
社内のコミュニケーションスペースに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としても利用できそうですし...。 ↩