Google Apps ScriptでSlack botをお手軽実装した話

  • 18
    いいね
  • 0
    コメント

2016/01/26追記
http://qiita.com/bbq-all-stars/items/602347dc5779184474f9
ババアbotを改良しました。


作ったもの

スクリーンショット 2017-01-23 10.35.45.png
お天気ババアbotです。
rain?
って聞くと、ババアが気合で教えてくれます。
別に日本語でも良いんですけど、日本語だとコマンド打ちにくそうだと思ったので英語で。

発端

うちの会社は地下にあるんですけど、窓がないもんだから外に出かける時に天気がわからなくて困ってました。
どうにかして外の天気を知りたいので、なんかいい方法ないかなー、と思って探すと、5分おきの降水予測値を取得するAPIがYahoo!にあるじゃありませんか。
http://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/weather.html
コイツでbot作ったらいけるんじゃね?って思って作ってみました。

botを作る方法

slackのbotの作り方を知らなかったので調べると、lambdaを使ったりherokuを使ったり、っていう方法はたくさん出てくるんですけど、lambdaはロール設定とかめんどくさいし、herokuはまだ扱ったことないので学習コストがかかるし、なんかもっとお手軽にbot作る方法はないの?って思って探したら、Google Apps Script(GAS)を使う方法を発見。
https://tech.camph.net/slack-bot-with-gas/
あらお手軽。

材料

  • Yahoo!API
  • Googleアカウント
  • SlackのOutGoing WebHooks
  • Javascriptの知識
  • 好きなババアの画像

全体の実装の流れ

  1. SlackのOutGoing WebHooksで特定のメッセージに反応してHttpリクエストを投げるようにする。
  2. Httpリクエストを投げる先は、 http://qiita.com/soundTricker/items/a4878d7e3100082576e4 にあるようにGASをウェブアプリケーションとして公開することで取得することができる。
  3. GASで、Yahoo!APIにHttpリクエストを投げる -> 得られた情報から雨かどうか判別 -> 結果をslackに投げる、という処理を書く。
  4. 完成。メッセージを打ってババアを愛でる。

Yahoo!APIとのやり取り

GASでは、UrlFetchApp.fetch(url, urlFetchOption)のメソッドを使ってHttpリクエストを投げることができます。
投げる先は以下にあるAPIを叩きます。
http://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/weather.html

ここで取得できる結果はこんな感じのデータです。

yahoo_api.json
{
    "ResultInfo":{
        "Count":1,
        "Total":1,
        "Start":1,
        "Status":200,
        "Latency":0.004206,
        "Description":"",
        "Copyright":"(C) Yahoo Japan Corporation."
    },
    "Feature":[
        {
            "Id":"200000001125_00.000000_000.00000",
            "Name":"地点(00.000000_000.00000)の2000年00月00日 11時25分から60分間の天気情報",
            "Geometry":{
                "Type":"point",
                "Coordinates":"00.000000_000.00000"
            },
            "Property":{
                "WeatherList":{
                    "Weather":[
                        {
                            "Type":"observation",
                            "Date":"200000001125",
                            "Rainfall": 1.65
                        },
                        {
                            "Type":"forecast",
                            "Date":"200000001130",
                            "Rainfall": 1.00
                        },
                        {
                            "Type":"forecast",
                            "Date":"200000001135"
                        },
                        (同じような続く)
                    ]
                }
            }
        }
    ]
}

ここでWeatherListのRainfallの値は降雨強度を指します。
降雨強度とは、瞬間的な降雨量を1時間当たりに換算したものです。
http://29.pro.tok2.com/~bye01354/hotei-HP/isahayabosai2/isahaya/refer/raindef.html
降雨量の体感の目安はこちら。
https://ja.wikipedia.org/wiki/%E9%9B%A8#.E9.9B.A8.E3.81.AE.E5.BC.B7.E3.81.95
コレに従って適当に実装するとこうなる。

ソース

code.gs
function doPost(e) {
  var token = PropertiesService.getScriptProperties().getProperty('SLACK_ACCESS_TOKEN');
  var botName = "お天気ババア";
  var botIcon = PropertiesService.getScriptProperties().getProperty('BBA_ICON');
  var verifyToken = PropertiesService.getScriptProperties().getProperty('OUTGOING_WEBHOOK_TOKEN');
  var yahooApiToken = PropertiesService.getScriptProperties().getProperty('YAHOO_API_TOKEN');
  var coodinates = PropertiesService.getScriptProperties().getProperty('COODINATES');

  //投稿の認証
  if (verifyToken != e.parameter.token) {
    throw new Error("invalid token.");
  }

  var app = SlackApp.create(token);

  var url = "https://map.yahooapis.jp/weather/V1/place?appid=" + yahooApiToken + "&coordinates=" + coodinates + "&output=json&interval=5";
  var urlFetchOption = {
    'method' : 'get',
    'contentType' : 'application/json; charset=utf-8',
    'muteHttpExceptions' : true
  };
  var response = UrlFetchApp.fetch(url, urlFetchOption);
  var json = JSON.parse(response.getContentText());
  var weatherList = json["Feature"][0]["Property"]["WeatherList"]["Weather"];

  var nowRain = weatherList[0]["Rainfall"];
  var nextRain = weatherList[1]["Rainfall"];
  var message = "";
  var rainfall = nowRain;
  if (nextRain && (!nowRain || nowRain < nextRain)){
    rainfall = nextRain;
  }
  if (!rainfall){
    message = "雨は降っとらん!!!";
  } else if (rainfall < 1){
    message = "小雨が降っとるかもしれんな。";
  } else if (rainfall < 3){
    message = "弱い雨が降っておる。気になるなら傘を持っていったほうがよいじゃろう。";
  } else if (rainfall < 10){
    message = "雨が降っておる。傘は持ったか?";
  } else if (rainfall < 20){
    message = "やや強い雨じゃ。傘は持ったか?";
  } else if (rainfall < 30){
    message = "強い雨じゃ!出かけるなら必ず傘を持っていきなさい。";
  } else if (rainfall < 50){
    message = "激しい雨が降っておる!!バケツを引っくり返したようじゃ!!!出かけんほうががええ!";
  } else if (rainfall < 80){
    message = "非常に激しい雨でまるで瀧のようじゃ!!!!外に出るのはやめるんじゃ!!";
  } else {
    message = "なにをしている!!!猛烈な雨じゃぞ!!!!一歩でも外に出たら死ぬぞ!!!!";
  }

  return app.postMessage(e.parameter.channel_id, message, {
    username: botName,
    icon_url: botIcon
  });
}

ババア駆動開発

もっとババアを作っていきたい。