Frog Advent Calendar 2016 – 16日目

カナダの西海岸に位置する、海も山も和食もある街バンクーバーのFrog Houseからお届けします。

技術の話ですが、せっかくなのでバンクーバーの紹介もさせて頂こうかと思います。

概要

  • シェアハウス内はSlackで連絡を取っている
  • バンクーバーの電車遅延はウェブページで公開されている
  • ページをスクレイピング、加工して情報取得
  • Slack botで定期的に情報を取得、問題があれば通知する

こんな感じ。チュートリアルではないのであんまり細かい手順やコードは載せてません、各種公式ドキュメントや実装コードをご確認願います。

遅延と回復を検知して通知。

フロッグハウス

Frog Houseというシェアハウスがバンクーバーにございまして、私ギンペイは現在そこに在住しております。住人は七名、うち私を含む五名がウェブ制作方面の人間です。

Slack

かつてはFacebookメッセージで家の中の連絡をしていたんですが、なかなか煩雑なのと検索性能が低いことから、Slackを導入しました。今のところうまく回っているようです。Slackはいいぞ。

最近二軒目Frog House 2ndも統合して、別チャンネル同チームで運用しております。なんか家賃とかそういうアレの管理をまとめてしたかったんですって。

現在チームには20のチャンネルが作成されていまして、全体用 #general と一軒目用 #general-1 みたいに分けて運用してます。まあ各自で適当に作ったりしてもらえれば良いかと。

先に申し上げた通りウェブ系の人が多いので、 #webdev チャンネルを設けて情報共有したりもしてます。たのしい。

とまあそんな感じで、日常生活にSlackを組み込んだ感じですね。

バンクーバーの交通事情

割と北米は車文化が根強い感じはあるんですが、バンクーバーでは “SkyTrain” という呼称で電車が走ってます。バンクーバーのダウンタウン(中心街)を軸に三本の路線が稼働中です。

ちなみに電車、無人運転です

東京のゆりかもめ線みたいな感じ。すげーぞカナダ人。

車両の先頭はこんな景色。あ、わかりづらいこれ……

あと改札も無人。前は性善説もとい諸事情により扉もなく開放されていたんですが、最近はSuicaみたいな “Compass” というカードが導入されまして、通るたびにピッピッとやります。オートチャージはなくて、駅に設置の機械でチャージしてやります。

チケット制度

日本の電車と比べて面白いのが運賃計算です。

全体がダウンタウンを中心にざっくり三通りの区域に分けられてお、なんとその区域ごとにざっくり同一運賃です。領域内なら一駅乗っても端から端まで移動しても同額! 区域をまたぐと加算されます。

路線図。

またチケットは一度購入すると90分間有効で、その間乗り放題です。買い物へ出かけるにも一枚分の購入で行って帰ってくることが可能です。

料金

たいだい一回 $3 、隣の区間まで行くと +$2 です。一か月定期券は $91 となかなかのお値段。

あ、あと空港から乗ると特別に +$5 かかります。ひどい。

電車以外

バスも縦横無尽に走ってます。車がなくてもそんなには不便はないですね。荷物運びは別ですが。

あとはダウンタウンの北に大きな河口があって、そこを北上するのには船 “SeaBus” を利用します。船といっても平たい形のやつで中は広く、そんなに揺れません。何度か乗ったけど、まだ乗るたびにちょっとわくわくします。

共有チケット

これらの公共交通機関は一括してTransLinkという会社が運営しており、先のチケットは共有です。つまり90分の間に電車とバスと船を乗り継いで移動して行って帰ってくる、なんてことが可能なわけです。

遅延情報

さてここから本題ですが、そんな公共交通機関の遅延情報は運営会社のウェブページで公開されています。

Alerts & Advisories

まあ電車が遅延することはそんなにないのですが、たまには遅延することもあります。

HTML情報の解析と取得

特段APIもないようなので(バスの運行情報は取得できるみたいなんだけど)、このHTMLを取得、解析して情報を取得するようにします。

実装コードはこちら。

いわゆる「スクレイピング」というやつです。お行儀よく情報収集するよう、慎重になる必要があるかと思います。慎重になっても駄目な場合もあるけどね。

HTMLファイルを取得

request というライブラリを利用してファイルを取得します。 jar という仕組みでcookieを割と簡単に操作できます。

件のページはcookie制御をしないと無限にリダイレクトされ続けるようになっていて、この jar の仕組みは簡単で助かりました。

まあないならないでも良さそう。

HTMLファイルを解析、情報抽出

HTMLファイルを取得できたので、次はこのテキストの中からお目当ての情報を探してゆきます。

正規表現なんかで頑張る代わりに、cheerioを使ってjQuery風にセレクタで要素検索することにしました。

利用例をREADMEから引用します。

let cheerio = require('cheerio')
let $ = cheerio.load('<h2 class="title">Hello world</h2>')

$('h2.title').text('Hello there!')
$('h2').addClass('welcome')

$.html()
//=> <h2 class="title welcome">Hello there!</h2>

あーいいですね、だいたい見慣れたjQueryのコードですね、簡単すね。

さてお目当ての情報自体はブラウザのdev toolsで確認して、 #tab0 .alertInfo の中にあることがわかりました。

細かい分割は、具体的にはコードを見て頂きたいのですが、それなりにセレクタやDOMの知識が必要になる場合があります。もともとこういう情報提供用に設計されたHTMLではないので仕方のないことです。頑張りましょう。

というわけでセレクタとか何とかを駆使して、無事情報を取得することができました。

Slack bot

情報は得られたので、これをあれこれしてSlackへ投稿するようにします。Hubot(ひゅーぼっと)というフレームワークを使ってさくっと作ります。

このHubotはGitHub社が作ってるそうです。本体自体はbot機能のみで、別途「アダプタ」を介して各種サービスへ接続します。TwitterやChatWork用のサードパーティーアダプタもあるようです。

定期的にTransLink遅延情報を得る

先ほどのライブラリと、cronというライブラリを利用します。

で、こんな感じです。

var translinkAlerts = require('translink-alerts');
var CronJob = require('cron').CronJob;

module.exports = function(robot) {
  new CronJob({
    cronTime: '0 */6 * * * *',
    start: true,
    timeZone: 'America/Vancouver',
    onTick: function () {
      translinkAlerts.get(function(data) {
        var train = data.train;

        // 問題あれば発言
        if (!train.fine) {
          var channelId = 'CXXXXXXXX'
          var message = '電車、駄目みたいだよ';
          robot.messageRoom(channelId, message);
        }
      });
    },
  });
};

cronTime は左から分、時、日、月、曜日、年を指定して、そのタイミングで onTick を実行してくれます。今回は毎一時間中6回、つまり10分ごとに実行しています。timeZone はそのままですね、日本時間なら "Asia/Tokyo" ですね。

で、実行する内容なんですが、問題があったら発言、なければ沈黙です。動作確認時は両方で発言するようにした方が良いかも。

また例を見るとだいたい誰かの発言に反応する形ですが、今回は時間で自発的に発言する必要があります。この場合は robot.messageRoom() を利用します。Slackの場合は channelId の取得がちょっと面倒です。

translinkAlertsで得られる情報

translinkAlerts.get() で得られる data はこんな感じです。

console.log(data.train.fine);  // => true/false
console.log(data.train.title);
console.log(data.train.outline);
console.log(data.train.description);

チャンネルIDを取得

API接続トークンを作成してからチャンネルIDを取得するという流れになります。

まずトークンはこちらから “Create token” で作成します。チームにひとつです。

トークン作成後はチャンネル情報取得APIで情報を取得します。こちらからウェブ上で(も)利用できます。

たぶん長いので頑張って目当てのチャンネルを探してください。チャンネル名( random とか。 # なし。)でページ内検索すると良いかと。

ID、見つかりました?

トークンは(これに限らず一般的に)秘密にしておく必要があります。じゃないと知らない人がそのトークンを使ってAPIにアクセスしてあんなことやこんなことをしてくれます。気を付けましょう。コードに直接書く代わりに、環境変数なんかを利用します。後ほどそんな話が出てきます。

Herokuにbotを載せる

Herokuはウェブアプリケーションサーバです。ですか? 自作アプリを設置して実行することができます。

自前のサーバがあるならそっちでも良いけども。

環境変数

Settings > Config Variablesから環境変数を二つ設定する必要があります。

  • HUBOT_HEROKU_KEEPALIVE_URL – 画面右上 “Open app” のリンク先URL
  • HUBOT_SLACK_TOKEN – これから作成するトークン

トークンはちょっと待ってね。先ほど作成したトークンとは別物です、これから作ります。

コード管理

アプリ本体はDeployから設定します。選択肢はいくつかあるが、GitHub連携からのAutomatic deploysがおすすめです。

最初に一度Manual deployしなくちゃだめかも。

便利アドオン

Overviewのadd-onsのところで管理します。以下の二つが定番。

  • Papertrail – エラーや何やらを記録してくれる。「なんか動かない」ときに助かる。
  • Process Scheduler – 指定の時間だけ起動させてくれる。Herokuの無料プランだとだいじ。

古い常識かな。たぶんまだ大丈夫。

Slackにbotを導入する

いよいよ大詰めです。

アプリをインストール

まずはチームのメニューから “Apps & integrations” を開きます。こんなURLのページ。

  • https://xxxxxxxxxxx.slack.com/apps

“hubot” で検索して “Add Configuration” から新規作成です。

すぐ見つかる

作ったらアイコンやら名前やらを設定してください。

トークンをHerokuへ

個別アプリのページに “API Token” という項目があるなので、その内容をコピーします。

上の方にある

そんでもってHerokuへ戻って、 HUBOT_SLACK_TOKEN の値として設定します。

Slackでbotを招待

最後に、Slackの方でbotを動かしたいチャンネルにそのbotをinviteします。botはもうSlackチームにいるはずですが、最初はどこのチャンネルにも入ってません。

さあこれで終わり!

もっと便利に

Frog Houseのbotはもっと色々な機能があります。毎朝天気予報や為替(日本円vsカナダドル)をお知らせしたり。遅延情報も詳しい情報を投稿したり、回復した場合にもお知らせしたりしてます。

それにはもうちょっとムニャムニャする必要があります。実装コードをご参考にどうぞ。seena-tanはバンクーバー用だけど、他の地域は応用してください。

ちなみに seena-tan で「スェナたん」です。セナさんではありません。アイコンは得意な住民が描いてくれました。

スェナさん近影

おしまい。