Lambda + Apex + API Gateway Slackで緊急連絡先を表示するslashコマンドを作ろう

# 概要
* Slackで`/tel_list`と打ったら緊急連絡先が出るようにする
* serverless実装

# 構成図
<img width="780" alt="スクリーンショット 2017-02-20 13.59.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/29405/739be8b8-34bd-c733-773d-eab6ce735fd8.png">

* slackでtel_list打つ
* api gatewayがPOSTリクエスト受け取る
* 指定したlambdaを実行。そこでKMSでslackのtoken認証する
* lambdaでpython実行し、slackに結果を送る

# 内容
## ①apexでlambda関数を管理する
* ここではKMSのtokenがまだできてないので、コードは書きません
* apexの使い方やインストール等は[こちら](http://qiita.com/sion_cojp/items/cf203c9ff8473f934512)をご覧ください

```bash
$ mkdir apex_slack_tel_list && cd apex_slack_tel_list
$ apex init
Project name: apex_slack_tel_list
Project description: apex_slack_tel_list is able to show TEL list when anyone was in trouble

### 一度deployする
$ cd apex_slack_tel_list
$ apex deploy
```

## ②IAM（KMS）の設定
* IAM->暗号化キー->リージョンを選んでください->キーの作成
    * エイリアス：apex_lambda_slack_tel_list
    * キーマテリアルオリジン：KMS
    * 次へ
        * タグの追加はなしで次のステップへ
            * apex_slack_tel_list_lambda_function
            * キーの管理者が削除できるようにをチェック入れる
            * 次のステップへ
                * apex_slack_tel_list_lambda_function
                * 次のステップへ
                    * 完了

![スクリーンショット 2017-02-20 14.21.27.png](https://qiita-image-store.s3.amazonaws.com/0/29405/bce82e9d-3289-7506-c9ad-1559fb05126d.png)

* 完了したらここのARN部分を控えてください。
* `arn:aws:kms:リージョン名:数字:key/key_id`となってます。

## ③IAMのroleにARNに対する権限追加
* IAM->ロール->apex_slack_tel_list_lambda_function->ポリシー名->編集


```json
        {
            "Effect": "Allow",
            "Action": [
                "kms:Decrypt"
            ],
            "Resource": [
                "arn:aws:kms:ap-northeast-1:xxxxxxxxxx:key/xxxxxxxxxxxxx"
            ]
        }
```

## ④SlackでSlashコマンドの登録
* slackのapp->Apps & Integration->Slashで検索->Add Configuration->`/tel_list` で登録
    * URL:https://xxxxxx.execute-api.xxxxxxx.amazonaws.com/prod/slack
        * こちらはAPI Gatewayの設定が終わったら登録します。
    * METHOD: POST
    * Customize Name: 緊急連絡先一覧
    * Show this command in the autocomplete listにチェック
    * Description: 緊急連絡先一覧

![スクリーンショット 2017-02-20 14.24.58.png](https://qiita-image-store.s3.amazonaws.com/0/29405/cc928e9a-4bfa-2e25-2c76-243da86c849e.png)

* 登録が終わったらtokenを控えてください

## ⑤Slackのtokenを暗号化
```bash
$ aws kms encrypt --key-id arnに書いてるkey_id --plaintext="slackのtoken"
{
    "KeyId": "arn:aws:kms:ap-northeast-1:xxxxxxxx:key/xxxxxxxxxxxx",
    "CiphertextBlob": "暗号化されたtoken"
```

## ⑥apexでコードを書いてdeployする
```py
$ vim functions/hello/main.py
# -*- coding: utf-8 -*-
import boto3
from base64 import b64decode
from urlparse import parse_qs
import logging

ENCRYPTED_EXPECTED_TOKEN = "暗号化されたtoken"

kms = boto3.client('kms')
expected_token = kms.decrypt(CiphertextBlob = b64decode(ENCRYPTED_EXPECTED_TOKEN))['Plaintext']

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def handle(event, context):
    req_body = event['body']
    params = parse_qs(req_body)
    token = params['token'][0]
    if token != expected_token:
        logger.error("Request token (%s) does not match exptected", token)
        raise Exception("Invalid request token")
    tel_list = [
        'sion:000-0000-0000',
        'cojp:111-1111-1111',
    ]

    format_tel_list = '\n'.join(tel_list)
    return { "text": "```%s```" % (format_tel_list) }
```
```bash
### いらないファイルを削除し、最終的にはこんなディレクトリになります
$ tree
├── functions
│   └── hello
│       └── main.py
└── project.json

### deploy
$ cd apex_slack_tel_list
$ apex deploy
```

* lambdaからテストしてみましょう。成功するはずです。

## ⑦API GatewayでAPIを作成
* API GATEWAY->API->APIの作成
    * 新しいAPI
    * API名：apex_slack_tel_list
    * 説明：lambda経由でslackで緊急連絡先を表示させてます
    * 作成を押す

<img width="1051" alt="スクリーンショット 2017-02-17 15.51.10.png" src="https://qiita-image-store.s3.amazonaws.com/0/29405/84ff14fd-27a6-1c7c-8d96-29ad2a4325a7.png">


## ⑧リソースを作成する
* apex_slack_tel_list->アクション->リソースの作成
    * リソース名：slack
    * リソースパス：slack
    * リソースの作成

<img width="832" alt="スクリーンショット 2017-02-20 14.38.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/29405/8caaf902-b0a1-89a3-951a-f5434ac8b38e.png">

## ⑨メソッドを作成する
* POST
* apex_slack_tel_list->アクション->メソッドの作成
    * Lambda関数
    * region: 選んでください
    * Lambda関数： apex_slack_tel_list_hello
    * 保存を押す
* slack/->出来上がったPOSTをクリック->統合リクエスト->本文マッピングテンプレート
    * application/x-www-form-urlencoded
    * テンプレートに書きを登録し保存。（今回は不要ですが、urlDecodeで日本語をPOSTしてもエラーが起きないようにしてます）


```json
{ "body": $util.urlDecode($input.json("$")) }
```

## ⑩API-Gatewayのdeployする
* リソース->アクション->APIのdeploy
    * デプロイされるステージにprod
    * デプロイ
* 作成が終わったら、ステージ->prod->URLの呼び出し　のurlを保存。
* これを先ほどのslackに登録してください。

## ⑪実際にslackで打ってみよう
```
/tel_list
```
* すると下記のように自分自身だけに緊急連絡先が表示されるかと思います。

<img width="482" alt="スクリーンショット 2017-02-20 14.49.35.png" src="https://qiita-image-store.s3.amazonaws.com/0/29405/1b6a2da7-5e4d-28d0-b768-e4bc06b6f5a6.png">

# 最後に
* Hubot用にサーバを立てなくて良いし、serverless便利