背景
我が家にRaspberry Pi 2がサーバとして置いてありました。しかし、特に何も仕事をしていなかったのと、仕事でSlackを使うようになったこともあり、Botとして仕事をさせようと思いつきました。そのときの記事は下記。
- [キーボードもマウスも使わずにsshでRaspberry Pi Model Bを使えるようにした for Mac OSX] (http://seiya-orz.hatenablog.com/entry/2016/03/16/114303)
- Raspberry Piを自宅Webサーバー化するための設定
- Raspberry Pi × hubot × Slackでbot運用
前回までは、ただ家賃振り込んだかを聞いてくれる機能しかなく、活用しきれていない感がありました。そこで、電車が遅延している時にSlackで教えてくれると便利じゃないかと思い、実際に作ってみました。
完成イメージはこんな感じ。
Botが電車の遅延を検知したら、あるチャンネルに投げてくれるイメージ。また、遅延について知りたいときは、Botに聞くと答えてくれると尚良ですね。
実装アイデア
電車遅延の情報をどこから入手してくるかの候補は2つありました。ちなみに僕は通勤に京王線しか使っていませんので、ひとまず京王線で話を進めます。
- 公式サイトをスクレイピング
- 公式TwitterのTweet
1だともし路線を増やす際の手間が大きそうです。2は、Tweetの内容を処理するだけで良いので汎用性も高く、楽そうですね。
京王電鉄運行情報のアカウント
他の路線も結構やっているみたいです、使ってませんけど。
ということで、実装は以下の流れでやればできそう。
- 5分毎にTweetを取得する。
- 新たなTweetがあった場合、内容を判断する。同時にTweet情報を保持しておく。
- 遅延していた場合、Slackに投げる。
TwitterAPIで他人のタイムラインの取得方法は以下URL。
https://dev.twitter.com/rest/reference/get/statuses/user_timeline
ちなみに、15分間で300回まではリクエストできそう、たぶん。
実装
Raspiで実現していますが、実装的にはLinuxサーバと何ら変わらず自由です。HubotはNode.jsを利用しているので、Node.jsでコーディングします。CoffeeScriptでも書けるんですが、僕はCoffeeScript嫌いなので使いません。
実装コード
// Description: 京王線の遅れ取得
//
// Commands: 電車
//
// Author: Seiya Mogami
var twitter = require('twitter');
var fs = require('fs');
var cronJob = require('cron').CronJob;
// 認証
// 自分のを入れてね
var api = new twitter({
consumer_key: '',
consumer_secret: '',
access_token_key: '',
access_token_secret: ''
});
// スクリーン名
var name = 'keiodentetsu';
// 電車が遅延している場合は遅延情報を投稿し、遅延していない場合は何もしない
function notifyIfTrainDelay(sname, robot) {
fs.readFile('./' + sname + '.json', 'utf-8', function(err,jsonString){
if(err){
return robot.messageRoom('error', err);
}else{
if(!jsonString || jsonString == ""){
var data = {
id_str: '1',
text: 'まだツイートが取得されていません。5分少々お待ち下さい。'
};
fs.writeFile(sname + '.json', JSON.stringify(data));
}else{
var json = JSON.parse(jsonString);
api.get('statuses/user_timeline', { screen_name: sname, since_id: json.id_str }, function (error, tweets, response) {
if (error) {
console.log(error);
return robot.messageRoom('error', err);
} else {
if (tweets.length > 0) {
// 最新Tweetをjsonファイルとして保持しておく
fs.writeFile(sname + '.json', JSON.stringify(tweets[0]));
for (var i = 0; i < tweets.length; i++) {
var text = tweets[i].text;
if (text.match(/京王線/) && (text.match(/遅れ/) || text.match(/見合せ/))) {
// 遅延情報がある場合
return robot.messageRoom('notification', "<!everyone>: 電車遅れてるみたいやで〜\n```\n" + text + "\n```");
}
}
return;
} else {
console.log('no tweet found');
return;
}
}
});
}
}
});
}
function getTrainInfo(sname) {
var json = JSON.parse(fs.readFileSync('./' + sname + '.json', 'utf-8'));
if(json){
return json.text;
}else{
return 'まだツイートが取得されていません。5分少々お待ち下さい。';
}
}
// slackの動作部分
module.exports = function (robot) {
// 秒 分 時 日 月 年
new cronJob('00 00,05,10,15,20,25,30,35,40,45,50,55 * * * *', function () {
notifyIfTrainDelay(name, robot);
}).start();
robot.respond(/電車/, function (msg) {
var message = getTrainInfo(name);
return msg.send("今の京王線はこんな感じやな〜\n```\n" + message + "\n```");
});
};
解説
読んでいただければわかるんですが、簡単に解説します。
必要なPackageのインストール
npm install --save twitter cron
Tweetの処理
スクリーン名とTweetIDを元にTweetを取得します。since_idは指定したID以降のTweetしか取得しないようになるオプションです。よって、どこかしらに最新Tweetを保存しておく必要があるのですが、今回は、簡単のために取得したJSON形式のTweetをそのままJSONファイルにして保存し、必要に応じて読み込むことにしました。
JSON形式のTweetは簡単に言うと、以下の様な形です。今回使っているものだけ。
{
"id_str": "Tweetに割り当てられたID(数字)を文字列にしたもの",
"text": "Tweetの本文"
}
idとid_strがあるのですが、違いはIntかStringかの違いです。ただ、idのほうの桁数が半端ないので、処理系統によっては丸め込みが発生してしまう可能性があります。ですので、id_strを活用することをおすすめします。
あとは、正規表現使ってtextを処理すればよいのですが、勉強不足ですごい雑な感じで書いてしまっていますね。勉強します。この部分は路線によって書かれ方が違いそうな気がします。各自適当に合わせてください。
Tweetを取得するapi.getは非同期で動作しているので、処理の内部でSlackに投稿するようにしています。
cronで定期実行
cronJobのインスタンスを生成して定期的にTweetを取得しています。今回は5分おきにしていますが、深夜帯は取得しないようにするのが本当は理想的ですね。ただ、書くのが面倒だったので今回はしていませんが。
まとめ
いろいろハマったりしましたが、まあなんとか作れました。3時間くらいですかね。今回でTwitter活用のノウハウがたまったので、Tweetに応じていろいろBotにさせることができそうです。
HerokuとかAWSとかを使ってBotをたてることもできるようなので、しかも簡単にできるので皆さんも是非。