MicrosoftBotFrameworkのRestAPIを使ったリアクションBot

[Microsoft Cognitive Services & Bot Framework Advent Calendar 2016](http://qiita.com/advent-calendar/2016/cogbot)の1日目の枠で投稿させていただきます。

少し間が空いてしまいましたが、前回の記事「[MicrosoftBotFrameworkのRestAPIを使ったTimerBot](http://qiita.com/nyasba/items/268524b2349cfe54600d)」の続編です。
宣言通り、話しかけると何らかの返事をする形式のリアクションbotを作成したので紹介します

とはいえ、その間にAzureBotServiceが発表され、C#の経験がなくとも簡単なBotはできるようになってきたので、本内容とあわせてAzureBotServiceを検討してみることをまずお勧めしておきます。
その場合は、こちらも参考に。[AzureBotServiceのSkypeBotを試してみた](http://qiita.com/nyasba/items/641fa81c86bf549cd450)

## システム構成

botに話しかけられた時のEndpointを用意する必要があるのでAPIGatewayを使っています。

```
botframework -> API gateway -> lambda(認証)
                            -> lambda(投稿) -> botframework -> skype
```

### 呼び出されるLambda関数の作成

今回はbotが話しかけられたことをきっかけにLambdaが実行されるため、INPUTパラメータが存在します。
グループチャットでbotに話しかけた場合、botframeworkからは以下のような情報が通知されます。

```json
{
    "type": "message",
    "id": "・・・",
    "timestamp": "2016-12-01T15:46:15.17Z",
    "serviceUrl": "https://skype.botframework.com",
    "channelId": "skype",
    "from": {
        "id": "29:・・・",
        "name": "nyasba"
    },
    "conversation": {
        "isGroup": true,
        "id": "19:・・・@thread.skype"
    },
    "recipient": {
        "id": "28:・・・",
        "name": "botname"
    },
    "text": "<at id=\"28:・・・\">@bot</at> hello",
    "entities": [
        {
            "mentioned": {
                "id": "28:・・"
            },
            "text": "<at id=\"28:・・・\">@bot</at>",
            "type": "mention"
        }
    ]
}
```

これはLambdaではeventに格納されているので、それを使ってアプリケーションを作成します。
Skypeへ投稿する部分は[前回の記事](http://qiita.com/nyasba/items/268524b2349cfe54600d)を参照してください。

```py
﻿# -*- coding: utf-8 -*-

from skype_adapter import SkypeAdapter
import re
import skype_config as s

def lambda_handler(event, context):
	"""Lambda使う場合のエントリポイント"""
	
	# ChatroomのID
	conversation_id = event['body']['conversation']['id']

	# Botに話しかけた人の名前
	from_user = event['body']['from']['name']

	# [参考]
	# メッセージ(bot名を指定している部分を除去して使う)
	input = event['body']['text']
	r = re.compile("(.*)(</at> )(.*)")
	input_message = r.match(input).group(3)
	print input_message
	
	# Skypeへ投稿する
	SkypeAdapter(s).postConversation(conversation_id, "@" + from_user + "　だが断る！")

```


### APIgatewayの設定

1. AWSマネジメントコンソールに入る
2. APIGatewayを選択
3. APIの作成
4. bot名に相当するリソースを作成　※botごとにAPIを分けるならリソース作成は不要
5. POSTメソッドを作成
    * 統合タイプ：Lambda関数
    * 作成したLambda関数のあるリージョン、関数名を指定する

これで以下のような画面が表示されるようになったかと思います
![apigateway.png](https://qiita-image-store.s3.amazonaws.com/0/47589/142865a1-1c27-74bb-4eb4-cf86d6af2ffe.png)

次は[統合リクエスト]から本文マッピングテンプレートの追加を行います。
Content-typeはApplication/jsonとして、テンプレートは以下のようにします。APIへのインプットのbodyとheaderを引き渡すような形ですね。

```json
{
  "body" : $input.json('$'),
  "headers": {
    #foreach($param in $input.params().header.keySet())
    "$param": "$util.escapeJavaScript($input.params().header.get($param))" #if($foreach.hasNext),#end
    #end  
  }
}
```

これでAPIの基本はできました。

### 認証用のLambda関数の作成

こちらは以下のテンプレートを参考にして作成するといいと思います。

* api-gateway-authorizer-python
* api-gateway-authorizer-nodejs

実装するロジックはAccessTokenの検証です。
以下に詳細が書かれていますので参考にしていただければと思います。

* [Azure AD を使った API 開発 (access token の verify)](https://blogs.msdn.microsoft.com/tsmatsuz/2015/02/17/azure-ad-service-access-token-validation-check/)
* [BUILD BOT with Microsoft Bot Framework Rest Api](https://blogs.msdn.microsoft.com/tsmatsuz/2016/08/19/build-skype-bot-with-microsoft-bot-framework-oauth-and-rest-api/)

＃と仕組みは紹介したものの実際のコードがないのは、認証はほしいけどまあいいか・・とやる気が出ず、作ってないからです(；-ω-)aﾞ 


### APIGatewayへ認証を組み込み

APIを作っただけでは認証は「なし」なので、認証を組み込みます。

APIを選択して、「オーソライザー」を作成し、先ほど作成したLambda関数を指定します。IDトークンのソースは```method.request.header.Authorization```を指定することで、認証トークンを先ほど作成したLambda関数に渡すことができるようになります。

このようにして作ったオーソライザーをAPIに組み込みます。以下の「メソッドリクエスト」で認証の選択肢に作ったオーソライザーが表示されているのでそれを選択するだけです。
![apigateway.png](https://qiita-image-store.s3.amazonaws.com/0/47589/142865a1-1c27-74bb-4eb4-cf86d6af2ffe.png)


### APIGatewayのデプロイ

APIGatewayにはデプロイという概念が存在します。Lambdaでは存在しないのでいつもはまるポイントなのですが、デプロイしないと使えません。
「アクション」から「APIのデプロイ」を選択して、デプロイします。ステージを聞かれると思うので、「prod」など適当なものを入れておいてください。
デプロイするとやっとAPIのEndpointが表示されます。こんなのです → ```https://XXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/prod```

### botへのエンドポイントURLの設定

botアカウントへ先ほど取得したエンドポイントURLを設定します。MicrosoftBotFrameworkのMyBotの画面から設定するだけですが、リソース名を付けるのを忘れないように。

## 動作確認

MyBotからbotをコンタクトに追加して、```@bot hello```で話しかけます。
このとき、@を押すとbot名が選択できるようになるのでそちらから実行する必要があります。文字列のコピペだとbotくんは反応しませんので悪しからず。「だが断る」と言われれば成功です。


明日は[annie](http://qiita.com/annie)さんです。