会社に傘を忘れがちなので、slackのstatusで天気予報してみた

オフィスにいると外の天気を気にしないがちで、外に出て初めて雨が降っていることを知るが時すでに遅し、オフィスに傘を忘れてしまっていることが数多くあります。

いつも目につく場所で今の天気がわかればこんな悲しい思いはしないだろうと思い、**slackのstatus**（名前の横にemojiを表示できるやつ）で今の天気を表示するプログラムを作成しました。

## 今回使用するもの
- **slack API**
    - 機能に応じて数多くのAPIが提供されている
    - statusアイコンや、messageを変更することができる
- **OpenWeatherMap API**
    - 世界中の天気予報情報を取得できる
    - 他にもいくつか天気予報を取得できるAPIはありましたが、日本の気象庁のデータも取り込んでいるとのことだったのでこれを選択（参考：[気象情報API比較してみた](https://qiita.com/Barbara/items/93ae7969691164c7c2bc)）
- **Google Apps Script(GAS)**
    - 自動化の強い味方。いつもお世話になります。
    
## 今回作るもの
1. OpenWeatherMap APIを叩いて**現在の天気を取得**
2. 取得した天気を、対応するslack **emojiにマッピング**
3. slack APIにリクエストを投げて、**statusのemojiを変更**
1. 上記の一連の流れをGASにより自動化、**定期的に実行**

ここからは実装の解説に入ります。

## slackAPIキー取得
slackのAPIはとてもたくさんありますが、今回使用するのはprofileを変更できる**users.propfile.set**というAPI。
https://api.slack.com/methods/users.profile.set

APIのインストールやリクエストを投げる際に必要なトークンの取得についてはこちらを参考にさせていただきました
[Slack API 推奨Tokenについて](https://qiita.com/ykhirao/items/3b19ee6a1458cfb4ba21)

記事の中で「**スコープの設定**」という項目がありますが、今回使用する機能の場合`users.profile:write`をスコープに設定する必要があります。

これで取得できる**OAuth Access Token**を用いて、後ほどAPIを叩いていきます。


## OpenWeatherMap APIキー取得
続いて天気予報の情報を取得するための**APIキーを取得**します。
https://openweathermap.org/api
![スクリーンショット 2019-11-24 14.12.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287471/b92b194c-3cc2-080a-33c7-db3aab0c8d01.png)


画面上部の**sign up**からユーザー情報の登録を行ったのち、メニューの**API**から`Current weather data`をsubscribeします。

今回利用する目的は現在の天気を知りたいだけですし、そんなに激しくAPIを叩く必要もなさそうなので、Freeのプランで十分でしょう。
**Get API and Start**を選んだのち、**sign up** → **API keys**を見るとAPIが取得できているはずです。


## GAS実装
下準備は終わったので、ここからGASでの実装をしていきます。
GASはJava Script likeなサーバーサイドのスクリプト言語で、Googleのサーバー上で実行することができます。実行のトリガーも時間やイベントなどから指定することができます。**無料**です。超便利。

今回の処理は大きく分けて

- **天気情報を取得**
- **slackのstatusを変更**

の二つなので、それぞれ分けて関数を作れば良さそうです。

### 天気情報を取得
先ほど取得したOpenWeatherMapのAPIキーを用いて、リクエストを送ります。
city IDはOpenWeatherMapのサイトかデータをダウンロードしてきて探す必要があります。（これが地味に面倒）。

http://bulk.openweathermap.org/sample/

関数の大枠はこんな感じ。

```weather.js
function getWeatherEmoji() {
  var weatherApiUrl = "http://api.openweathermap.org/data/2.5/weather";
  var cityId = "1850144";　//Tokyo-to
  var apiKey = "api_key"; //先ほど取得したAPI key

  var response = UrlFetchApp.fetch(weatherApiUrl + "?id=" + cityId + "&appid=" + apiKey);
  var result = JSON.parse(response.getContentText("UTF-8"));
}
```
必要な情報からURLを作り、GASで定義されている関数である`UrlFetchApp.fetch`によってリクエストを送ってます。
取得できた結果から文字情報をとりだしてJSON形式にパースしたものを、変数`result`に格納してます。resultの中身はこのようになっています。

```result.js
 {
   visibility = 10000, 
   timezone = 32400, 
   main = {
     temp = 292.98,
     temp_min = 290.93,
     humidity = 88,
     pressure = 1014,
     temp_max = 295.37
   }, 
   clouds = {
     all = 75
   }, 
   sys = {
     country = JP,
     sunrise = 1574544323,
     sunset = 1574580624,
     id = 8077,
     type = 1
   }, 
   dt = 1574573372, 
   coord = {
     lon = 139.69,
     lat = 35.69
   }, 
   weather = [{
     icon = 04d,
     description = broken clouds,
     main = Clouds,
     id = 803
   }], 
   name = Tōkyō - to,
   cod = 200,
   id = 1850144,
   base = stations, 
   wind = { speed = 1}
 }
```

色々な情報を取得できていますが、今回やりたいことは天気をslackのemojiに対応させることなので、大雑把に晴れとか雨とかの情報がわかれば十分です。
**icon**の上二桁の数字が大体の天気をあわらしているようなので、これにemojiを対応させる実装にしました。
![スクリーンショット 2019-11-24 14.53.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287471/12b6b645-ad5a-69e0-2a52-d1d07a9b940f.png)

処理の部分はこんな感じ

```js

  var weatherIcon = result.weather[0].icon;
  var weatherSlackEmojiHash = {
    "01": ":sunny:",
    "02": ":mostly_sunny:",
    "03": ":sun_behind_cloud:",
    "04": ":cloud:",
    "09": ":rain_cloud:",
    "10": ":partly_sunny_rain:",
    "11": ":lightning:",
    "13": ":snowman:",
    "50": ":foggy:"
  }
  var weatherEmoji = weatherSlackEmojiHash[weatherIcon.slice(0,2)] || ":globe_with_meridians:" ;
  return weatherEmoji;
```

天気を表してるslackのemojiを、iconの上二桁と対応させるhashマップを作り、これを用いて変換しています。hashの中にない場合は適当なemojiを返すようにしました。

この処理を先ほどの関数に追加して、現在の天気を取得してslackのemojiを返却する関数を作ることができました。

### slackのstatusを変更
最後に、先ほどの関数を用いてslackのAPIにリクエストを送りstatusを変更する処理です。
実装はこんな感じ。

```main.js
function main() {
  var weatherEmoji = getWeatherEmoji(); //先ほど定義した関数によりemojiを取得
  var payload = {
    "profile": {"status_emoji": weatherEmoji}
  };
  var options = {
    "method" : "POST",
    "headers" : {
      "content-type": "application/json",
      "Authorization": "Bearer {{slack_token}}" //slackAPIのtoken
    },
    "payload" : JSON.stringify(payload)
  };
  var url = "https://slack.com/api/users.profile.set";
  var response = UrlFetchApp.fetch(url,options);
}
```

APIにより決まっている形でリクエストを投げます。
`UrlFetchApp.fetch`は2番目の引数にoptionを取ることができます。
https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app

ここでハマったのは`headers`の部分。`Authorization`に`Bearer {api_key}`と指定してやることでリクエストを投げることができます。**OAuth2.0**の仕様？のようで、このあたりをよくわかっておらず詰まりました。

ここで作成したスクリプトにトリガーを設定して定期的に実行してやることで、slackのstatusで天気予報をすることができました！

![スクリーンショット 2019-11-24 19.26.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287471/b8983469-4f66-373a-ba39-1eb2f1dac443.png)




## 感想
今は本当に多種多様なAPIが公開されていて、また無料で利用できるものも多く、恵まれた環境だなと思います。「こんなものがあったいいな！」を自分で作ることができるのはとても楽しいですね。エンジニアになってよかったです。

まとめると

**かがくの　ちからって　すげー！**