🍣

低気圧で頭痛がひどいのでslackで頭痛ーるを表示するappをlambdaで作った

2020/11/14に公開

仕上がり

最終的にはこんなのが出来上がります。

slackで頭痛ーるを表示する

低気圧で頭痛がひどい。今日も低気圧で体調を持っていかれてしまいました。

ありがたいことに頭痛ピープル向けに「頭痛ーる」というツールが公開されています。

https://zutool.jp/

株式会社ポッケさんありがとう...

とはいえ、頭痛ーるのサイトを毎度観に行くのが面倒。

ということでslackで表示できるようにしました。

構成は以下。シンプルです

https://github.com/ShuzoN/zut/blob/master/index.js

コードはここに置いてあります。

詳細の実装は省いて

  • 頭痛ーるAPIの使い方
  • aws lambdaへのデプロイ
  • slack appでの連携

について書き、真似できるように大枠の作り方を書いていきます。

頭痛ーるAPIの使い方

頭痛ーるのサイトでdeveloper toolを開くと数字のpathに対するリクエストを送っているxhrがあります。

これが天気、気圧情報を取ってくるリクエストです。

場所

例えば東京都調布市の場合は以下。

https://zutool.jp/api/getweatherstatus/13208
{place_name: "東京都調布市", place_id: "208", prefectures_id: "13", dateTime: "2020-11-13 22",}

どうやら

prefectures_id + place_id でurlが設計されてるようですね。

試しに 13209にすると 東京都町田市になります。

こんな感じで大体連番になってるので、curlで問合せして今回は都内のidを割り出します。
対応させるとこんな感じです。

"千代田": 13101
"中央": 13102
"港": 13103
"新宿": 13104
...

都内対応表: https://github.com/ShuzoN/zut/blob/master/locations.js

昨日、今日、明日、明後日

町田市を例に見てみます。

1リクエストで昨日、今日、明日、明後日の天気と気圧が取れます。
ここでは端折っていますが、0-23時の間で1時間おきの情報を取得できます。

$ curl https://zutool.jp/api/getweatherstatus/13209 | jq
{
  "place_name": "東京都町田市",
  "place_id": "209",
  "prefectures_id": "13",
  "dateTime": "2020-11-13 22",
  "yesterday": [
    {
      "time": "0",
      "weather": "100",
      "temp": "7.1",
      "pressure": "1033.4",
      "pressure_level": "0"
    },
    ...
    {
      "time": "23",
      "weather": "200",
      "temp": "10.8",
      "pressure": "1030.1",
      "pressure_level": "4"
    }
  ],
  "today": [
    ...
  ],
  "tommorow": [
    ...
  ],
  "dayaftertomorrow": [
    ...
  ]
}

天気と気圧レベル

先ほどのリクエストでは1時間ごとに以下の情報を取得できます。

  • 天気
  • 温度
  • 気圧
  • 気圧レベル

天気と気圧レベルは頭痛ーる特有のマジックナンバーが当たっています。
これ関してはslack絵文字を対応させることで処理します。

    {
      "time": "0",
      "weather": "100",
      "temp": "7.1",
      "pressure": "1033.4",
      "pressure_level": "0"
    }

天気(weather)は以下のような対応になっています。 https://github.com/ShuzoN/zut/blob/master/weather.js

exports.get = function (weatherType) {
  if (weatherType === "100") { // 晴
    return ":sunny:";
  }
  if (weatherType === "200") { // 曇り
    return ":cloud:";
  }
  if (weatherType === "300") { // 雨
    return ":umbrella:";
  }

  return ":innocent:"; // 例外
};

気圧レベル(pressure_level) は以下のような対応になっています。

(1は本家頭痛ーるでもokになっているため合わせています)

exports.get = function (pressureLevelType) {
  if (pressureLevelType === "0") { // 🆗
    return ":ok:";
  }
  if (pressureLevelType === "1") { // 🆗 
    return ":ok:";
  }
  if (pressureLevelType === "2") { // ⤵︎
    return ":arrow_heading_down:";
  }
  if (pressureLevelType === "3") { // ⚠️
    return ":warning:";
  }
  if (pressureLevelType === "4") { // 💣
    return ":bomb:";
  }

  return ":innocent:"; // 例外 😇
};

整形してresponseを返す

あとは 頭痛ーるから情報の取得 -> 整形すればok

コード: https://github.com/ShuzoN/zut/blob/master/zutool.js

今回は業務時間に合わせて 実行当日の8-20時の天気、気圧情報を整形します。

exports.formatter = function (json) {
    return json.today
      .filter((h) => h.time > 7 && h.time < 21)
      .map((h) => {
        return `${h.time}${weather.get(h.weather)} ${h.temp}${
          h.pressure
      }hPa ${pressureLevel.get(h.pressure_level)}`;
    });
}

aws lambdaへのデプロイ

ファイル zip

実行環境としてlambdaを利用します。

今回はjsで実装しています。 1scriptだと可読性が低かったためファイル分割を行っております。

複数ファイルをデプロイするためにはzip化する必要があるため、

プロジェクトルートで以下のコマンドを実行します。

zut というプロジェクト名で作っていますが、なんでもいいです。

$ zip -r zut.zip .

aws lambdaで関数作成

こんな感じでnode12が動くlamdbaを作ります。

zipファイルのアップロードをします。
今回はデプロイフローなどは組まず手動で済ませます。

aws api gatewayでエンドポイントの作成

lambdaのデプロイだけではapiとして動作しません。
トリガーをクリックし、api gatewayを追加します。

apiを作ったら /zut2パスへ post メソッドの追加を行います

これでpostリクエストに対応できるようになりました。

ゴールは slack からのpost requestです。

SlackのOutgoing WebHooksは application/x-www-form-urlencoded で送られるため、これに対応します。

統合リクエストに飛んで

マッピングテンプレートを以下のように設定します。
これはhttp content typeを見てlambdaに送信する情報を指定したテンプレートに合わせて書き換えてくれる機能です。

application/x-www-form-urlencoded
##  See http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
##  This template will pass through all parameters including path, querystring, header, stage variables, and context through to the integration endpoint via the body/payload
#set($allParams = $input.params())
{
"body-json" : {"body" :$input.json('$')},
"params" : {
#foreach($type in $allParams.keySet())
    #set($params = $allParams.get($type))
"$type" : {
    #foreach($paramName in $params.keySet())
    "$paramName" : "$util.escapeJavaScript($params.get($paramName))"
        #if($foreach.hasNext),#end
    #end
}
    #if($foreach.hasNext),#end
#end
},
"stage-variables" : {
#foreach($key in $stageVariables.keySet())
"$key" : "$util.escapeJavaScript($stageVariables.get($key))"
    #if($foreach.hasNext),#end
#end
},
"context" : {
    "account-id" : "$context.identity.accountId",
    "api-id" : "$context.apiId",
    "api-key" : "$context.identity.apiKey",
    "authorizer-principal-id" : "$context.authorizer.principalId",
    "caller" : "$context.identity.caller",
    "cognito-authentication-provider" : "$context.identity.cognitoAuthenticationProvider",
    "cognito-authentication-type" : "$context.identity.cognitoAuthenticationType",
    "cognito-identity-id" : "$context.identity.cognitoIdentityId",
    "cognito-identity-pool-id" : "$context.identity.cognitoIdentityPoolId",
    "http-method" : "$context.httpMethod",
    "stage" : "$context.stage",
    "source-ip" : "$context.identity.sourceIp",
    "user" : "$context.identity.user",
    "user-agent" : "$context.identity.userAgent",
    "user-arn" : "$context.identity.userArn",
    "request-id" : "$context.requestId",
    "resource-id" : "$context.resourceId",
    "resource-path" : "$context.resourcePath"
    }
}

はい、というわけで以下のパスでpost bodyが入ってくるようにマッピングしておきます。

{"body-json" : {"body" :$input.json('$')}}

APIへのデプロイを忘れると反映されないので気をつけてくださいね。

$ curl https://<your>.execute-api.ap-northeast-1.amazonaws.com/default/zut2 -d '{"text": "渋谷"}'


{"response_type":"in_channel","blocks":[{"type":"section","text":{"type":"mrkdwn","text":"8時 :sunny: 15.7℃ 1026hPa :ok:\n9時 :sunny: 16.8℃ 1026.4hPa :ok:\n10時 :sunny: 17.8℃ 1026.3hPa :ok:\n11時 :sunny: 18.6℃ 1026hPa :ok:\n12時 :sunny: 19.3℃ 1025.4hPa :ok:\n13時 :sunny: 19.7℃ 1025hPa :ok:\n14時 :sunny: 19.9℃ 1025.1hPa :ok:\n15時 :sunny: 19.5℃ 1025.5hPa :ok:\n16時 :sunny: 18.6℃ 1026.2hPa :ok:\n17時 :sunny: 17.2℃ 1027.1hPa :ok:\n18時 :sunny: 15.7℃ 1028hPa :ok:\n19時 :sunny: 14.2℃ 1028.9hPa :ok:\n20時 :sunny: 12.8℃ 1029.6hPa :ok:"}}]}%  

これで curl - aws apigateway + lambda - 頭痛ーる の連携は終わりましたね。

slackとaws lambdaを紐づける

https://api.slack.com/apps/

slack appを作成します。 zut とでも名付けましょう。

Slash Commands -> create new command と進みます。

request urlは先ほど作成した aws api gatewayのurlを入力します。

これで作成できました。次はworkspaceへの登録です。

install app -> install app to workspace とすすみ、追加したいworkspaceにアプリを入れます。

対象のworkspaceで /zut と入力すると対応する市区の名前が出てきます。

あとは /zut 渋谷 のように打てば利用できます。

めでたしめでたし。ということで今回は以上です。

皆さんも使ってみてくださいね

Discussion