Lambda + docomo自然対話API(雑談) でLINEbotをつくってみた

docomo自然対話APIを利用して、雑談するLINEbotを作ってみました。
AWS Lambda + Pythonでサーバレスで簡単な構成を目指します。

*2018年9月現在、docomo自然対話APIは無償提供されていますが、10月より有償化されるようです。

(2019.4.5 追記）
2019年6月28日を以って自然対話APIは提供終了されます。
一部のサービスは、雑談対話サービス「かたらい」（https://www.katar.ai/ ）に移行されるようです。

# 開発環境
* MacOS High Sierra 10.13.3
* Python3.6
* AWS Lambda
* LINE Messaging API
* [Docomo 自然対話API(雑談API)](https://dev.smt.docomo.ne.jp/?p=docs.api.page&api_name=natural_dialogue&p_name=api_usage_scenario)


## 事前準備
必要な情報として、

* LINEbotのアクセストークン
* 雑談対話APIのAPI key、appID

を取得します。
### 1. AWSへの登録
登録方法は割愛しますが、Lambdaは1Mリクエスト/月まで無料で利用できます。

### 2. LINE botの準備
LINE Messaging APIを利用するために、LINEのアカウント(普段使っているアカウントでOK）で
[LINE Developers](https://developers.line.me/ja/)への登録が必要です。

登録後は、

* プロバイダーの新規作成
* 新規チャネルでMessaging APIの利用登録
* プランはDeveloper Trial

チャンネルが作成できたら基本情報の設定で以下を実行

* アクセストークンの発行
* Webhook送信 -> 利用する
* 自動応答メッセージ -> 利用しない

### 3. 雑談対話APIの準備
[docomo Developer support](https://dev.smt.docomo.ne.jp/?p=index)のアカウント登録(個人利用でOK)

* マイページより新規API利用申請
* 自然対話APIの雑談対話を利用する
* 利用申請後、承認されるとマイページにAPI keyが表示される

雑談の対話情報はappIDで管理されるようなので、各利用ユーザごとにappIDを取得するべきなのですが、
今回はとりあえず動かすために、１つのappIDを取得して、それを直接Lambdaの環境変数に入れ込みました。。。

appIDは以下のcurlをターミナルで叩くことで取得できました。

```
curl -d '{"botId": "Chatting","appKind": "Smart Phone"}' -H "Content-Type: application/json" -X POST https://api.apigw.smt.docomo.ne.jp/naturalChatting/v1/registration?APIKEY=YOUR_API_KEY
```

以上で準備は完了です。

## ソースコード

lambdaにデプロイするコードを書いていきます。
LINE側からtextが送られてきた場合は、雑談対話APIに渡してリプライをする、
スタンプが送られてきた場合は同じものを返すようにしました。（デフォルトのスタンプに限る）

```main.py
# -*- Coding: utf-8 -*-

import os
import urllib.request
import json

def lambda_handler(request, context):

    for event in request['events']:
        
        # endpoint
        url = 'https://api.line.me/v2/bot/message/reply'
        
        # request header
        headers = {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer ' + os.environ['CHANNEL_ACCESS_TOKEN']
        }

        # text or sticker
        message_type = event['message']['type']

        text = ""
        stickerId = ""
        packageId = ""

        if message_type == "text":
            text = docomo_chatting(event)

        elif message_type == "sticker":
            stickerId = event['message']['stickerId']
            packageId = event['message']['packageId']

        # request body
        body = {
            'replyToken': event['replyToken'],
            'messages': [
                {
                    "type": message_type,
                    "text": text,
                    "stickerId": stickerId,
                    "packageId": packageId
                }
            ]
        }

        # post
        req = urllib.request.Request(url, data=json.dumps(body).encode('utf-8'), method='POST', headers=headers)
        with urllib.request.urlopen(req) as res:
            response_body = res.read().decode("utf-8")


    return {'statusCode': 200, 'body': '{}'}

def docomo_chatting(event):
    
    # endpoint
    endpoint = 'https://api.apigw.smt.docomo.ne.jp/naturalChatting/v1/dialogue?APIKEY=REGISTER_KEY'
    url = endpoint.replace('REGISTER_KEY', os.environ['DOCOMO_API_KEY'])

    # request json
    text = event['message']['text']
    headers = {"Content-Type":"application/json"}
    body ={
        "language":"ja-JP",
        "botId":"Chatting",
        "appId":os.environ['DOCOMO_APP_ID'],
        "voiceText": text,
        "clientData":{
        "option":{
            "mode":"dialog",
            "t":"kansai"
        }
        },
        "appRecvTime":"2018-09-01 00:00:00",
        "appSendTime":"2018-09-01 00:00:00"
        }

    # post
    r = urllib.request.Request(url, data=json.dumps(body).encode('utf-8'), method='POST', headers=headers)
    with urllib.request.urlopen(r) as r:
        response_body_str = r.read().decode("utf-8")
        response_body = json.loads(response_body_str)
    response = response_body['systemText']['expression']

    return response
```

## Lambdaへコードをデプロイする
上記のコードをLambdaに記載します。

'新しい関数を作成'で関数を作成します。
諸々の設定は以下のようにしました。
![lambda1.png](https://qiita-image-store.s3.amazonaws.com/0/208648/46e5f69c-1ff0-1a04-2a1f-dd392c8f1ee4.png)

関数が作成できたら、ウィンドウ部分にコードを記載します。
pythonファイルをmain.pyとしたので、ハンドラは`main.lambda_handler`とします。

![lambda3.png](https://qiita-image-store.s3.amazonaws.com/0/208648/9f28257b-58be-2485-a3d0-bc62bc6bce4f.png)


事前準備で取得したAPI keyとトークンを環境変数にキーと値をセットします。
![lambda4.png](https://qiita-image-store.s3.amazonaws.com/0/208648/551b41a0-e7e2-2c40-6969-0e65470d4528.png)


## API Gateway, Webhookの設定

次にLINE APIと繋げるためにAPIGatewayの設定をします。
'トリガーの追加'からAPIGatewayを選択します。
![api1.png](https://qiita-image-store.s3.amazonaws.com/0/208648/76c4d3c8-20c8-0dc1-9343-a501f92e168f.png)

追加をすると、APIエンドポイントが取得できるのでコピーしておきます。
![api2.png](https://qiita-image-store.s3.amazonaws.com/0/208648/c9c5486e-940c-1db1-5a55-7e1ddf678fcd.png)


作成したAPI(myChatting-API)の設定画面に移動します。
'アクション' -> 'メソッドの作成'　からPOSTメソッドを作成します。
Lambda関数は今回作成したlambda関数の名前を入れておきます。
![api3.png](https://qiita-image-store.s3.amazonaws.com/0/208648/0dd38de4-a57a-3e66-83ed-cfdcb383f337.png)

メソッドリクエストの設定画面を開き、HTTPリクエストヘッダに`X-Line-Signature`を追加します。
![api4.png](https://qiita-image-store.s3.amazonaws.com/0/208648/66b37467-dcb3-7e91-85b8-f4880c53078e.png)


以上の設定が終わったら'アクション' -> 'APIのデプロイ'をして、API Gatewayの設定は完了です。

最後にLINE DevelopersのWebhookURLにAPIエンドポイントを記載します。
![line1.png](https://qiita-image-store.s3.amazonaws.com/0/208648/b762ea68-40c1-bca6-ad9b-f3397f4e7624.png)


接続確認で成功すればデプロイ完了です。

## 結果
<img width="375" alt="line2.jpeg" src="https://qiita-image-store.s3.amazonaws.com/0/208648/43f46348-6b5a-a0a9-d34b-8dfe6555ef00.jpeg">

雑談対話APIのoptionでkansaiとしたので、だいぶフランクなbotができました。
標準語・関西弁・赤ちゃん語に対応しているようです。

対話内容はちょっと不自然な場合もありますが、とりあえず何か喋り返して欲しいbotを作りたいときに便利ですね。


## 参考
大変参考にさせて頂きました！感謝です！

* [AWS Lambda の Python 3.6 で LINE Bot を動かす](https://blog.5000164.jp/2017/8/14/line-bot/)
* [LINE Botでスプラトゥーン2のステージを答えてくれるBotを作ってみた - Qiita](https://qiita.com/otera05/items/c306eb5745f1a5549abd)
* [Python3でdocomoの自然対話：雑談apiを動かす - Qiita](https://qiita.com/aoyahashizume/items/a3d798bbb5c8d639abaa)
* [API GatewayとLambdaでAPI作成のチュートリアル - Qiita](https://qiita.com/vankobe/items/ab5bc6487c7e07cb3aba)
* [とりあえずdocomo雑談対話APIを使ってみる - qiita](https://qiita.com/ponkio-o/items/df0d64677dcb4b7e8eb3)