PythonでClovaスキルを作ってみた

この投稿はLINEBot&Clova Advent Calendar 2018の3日目の投稿です。前日の投稿は
[LINE BOOT AWARDS 2018で超個人的に好きだったサービスまとめ](https://qiita.com/stachibana/items/6159490bfe66a67647dc)でした。どれもクオリティが高く、ファイナル進出してもおかしくないものばかりでした。今回の話は来年のLINE BOOT AWARDSに応募できるような作品を作れるような土台固め的な内容です。自分もここからステップアップできるようにしたいですね。

最近とあるハッカソンでLINE賞をいただき、賞品としてClova Friendsを手に入れました（ありがとうございます）。せっかくいただいたので遊び倒そうと思いClovaスキルを作る方法を調べていたら普段使い慣れているpythonでも作れることが分かったので、Advent Calenderの季節ですし実際に簡単なスキルを作ってみたいと思います。

# 今回作るもの
好きな数字の範囲を指定して、ランダムに数字を取り出すサイコロのスキルを作ってみます。例えば、1から10まででランダムに数字を取り出したい時に「1から１０で設定して」というとランダムで数字を出力するようにします。

# スキルの登録
まずは[Clova Developer Center](https://clova-developers.line.me/#/)にアクセスし、自分のLINEアカウントでログインします。ログインしたらトップページにある「スキルを開発する」をクリックします。

![スクリーンショット 2018-11-11 00.15.10.png](https://qiita-image-store.s3.amazonaws.com/0/170520/70aaadf6-4867-3eb5-1efb-0a06a1080dff.png)

クリックした先のページの一番下にある「LINE Developersでスキルチャンネルを新規作成」ボタンをクリックし、LINE Developersに飛びます。

![スクリーンショット 2018-11-11 00.37.07.png](https://qiita-image-store.s3.amazonaws.com/0/170520/b1d1a1c3-8a00-c3a3-9075-287ca1df3add.png)

もし、すでに作成されたブロバイダーがある時は、そこに追加してもいいですが、今回は新規で作成します。

![スクリーンショット 2018-11-11 00.44.21.png](https://qiita-image-store.s3.amazonaws.com/0/170520/bb6cdc73-597a-b48a-2f98-aed92bd8969a.png)

適当にブロバイダー名をつけて、上図の画面にたどり着いたらClovaスキルを選んでチャンネルを作成します。今回は「サイコロさん」というチャンネル名にします。
![スクリーンショット 2018-11-11 02.10.53.png](https://qiita-image-store.s3.amazonaws.com/0/170520/f2f32216-7b72-688e-f651-72672bbd5716.png)

そのまま、確認ボタンを押して最後まで進めばチャンネルの作成が終わり、Clova Dveloper Centerに戻ります。
##　基本設定
ここからスキルを作成するための設定を行います。基本設定は以下にします。

|  項目  　　|   入力内容   |
|:-----------------|:------------------:|
| タイプ      |   カスタム        |
| Extension ID   |    リバースドメイン形式の文字列。とりあえず「com.自分の名前.clova.skill」 にしておきます。  |
| スキル名          |      サイコロさん     |
| 呼び出し名（メイン）         |  サイコロさん     |
| 呼び出し名（サブ）        | サイコロたん       |
| Audio Playerの使用|  いいえ      |
| 提供者について | 個人を選択→項目を埋める。LINE IDの項目な何もしなくてOKです。 |

##　サーバ設定
ここではherokuで作成したサーバーのURLを入力します。ここはあとで設定しますので、今は適当なURLを設定しておきます。アカウント連携の有無は「いいえ」のままでいいです。

##　配布情報
審査担当者へのコメントやユーザー向けの説明を記入しますが、今回は配布しないので、適当に一言書いておきます。代表サンプル発話は例にしたがって記入します。

##　個人情報の保護および規約同意
以下の項目の設定を行います。
購入/支払い機能はありますか？→「いいえ」
個人情報を取得しますか？→「いいえ」

ここまでできたら、一番下の「対話モデル」をクリックすると、別画面が表示されて、対話モデルを作成できるようになります。

# 対話モデルの作成
それではここから対話モデルを作成していきます。今回は「数字の範囲を指定する」発話を追加していきます。それではカスタムインテントを追加します。インテント名は「callNumber」とします。
![スクリーンショット 2018-11-12 02.42.55.png](https://qiita-image-store.s3.amazonaws.com/0/170520/068b5da2-0ba0-9ed8-5038-69a47dfa99aa.png)

ここで対話モデルを追加します。サンプル発話リストに数字の範囲を指定する発話を何パターンか入力します。
![スクリーンショット 2018-11-12 03.30.48.png](https://qiita-image-store.s3.amazonaws.com/0/170520/79d988d3-6b61-be2a-707a-4e441f37e32c.png)

この時にスロットと言われる、発話したリクエストを処理する時に必要な情報を定義します。今回の場合だと数字を取り出したいので、サンプル発話を追加したら数字をドラックして、スロットを登録します。

![スクリーンショット 2018-11-12 03.55.32.png](https://qiita-image-store.s3.amazonaws.com/0/170520/37c04774-eade-6db3-f87e-aaa8b0392663.png)

今度は、先ほど登録したスロットのタイプを定義していきます。今回はビルトインスロットタイプの中の「CLOVA.NUMBER」を選択します。
![スクリーンショット 2018-11-12 03.26.05.png](https://qiita-image-store.s3.amazonaws.com/0/170520/feee6d2b-17fd-18ac-5d44-ecebfee28ba7.png)

ここまでできたら、左上のビルドボタンを押して、モデルをビルドさせて完了です。

# コーディング
ようやくここからコーディングをしていきます。python+Flaskで開発していきます。

```python:main.py
# coding: utf-8

from flask import Flask, request, jsonify
import os, random
import cek

app = Flask(__name__)

clova = cek.Clova(
    application_id="com.heroku.kmiura.app",
    default_language="ja",
    debug_mode=True)

@app.route('/', methods=['GET', 'POST'])
def lambda_handler(event=None, context=None):
    app.logger.info('Lambda function invoked index()')
    return 'hello from Flask!'

# /clova に対してのPOSTリクエストを受け付けるサーバーを立てる
@app.route('/clova', methods=['POST'])
def my_service():
    body_dict = clova.route(body=request.data, header=request.headers)
    response = jsonify(body_dict)
    response.headers['Content-Type'] = 'application/json;charset-UTF-8'
    return response

# 起動時の処理
@clova.handle.launch
def launch_request_handler(clova_request):
    open_message = "こんにちは，サイコロに設定したい数字を指定してください"
    welcome_japanese = cek.Message(message=open_message, language="ja")
    response = clova.response([welcome_japanese])
    return response

# callNumberIntentが解析されたら実行
@clova.handle.intent("callNumber")
def number_handler(clova_request):
    app.logger.info("Intent started")
    start_num = clova_request.slot_value("startNum")
    end_num = clova_request.slot_value('endNum')
    app.logger.info("startNum: {}, endNum: {}".format(str(start_num), str(end_num)))
    res = decide_num(end_num, start_num)

    message_japanese = cek.Message(message="結果は{}でした。".format(res), language="ja")
    response = clova.response([message_japanese])
    return response

# 終了時
@clova.handle.end
def end_handler(clova_request):
    # Session ended, this handler can be used to clean up
    app.logger.info("Session ended.")

# 認識できなかった場合
@clova.handle.default
def default_handler(request):
    return clova.response("Sorry I don't understand! Could you please repeat?")


def decide_num(start_num, end_num):
    app.logger.info("decide_num started")
    try:
        if start_num > end_num:
            sai_res = random.randint(int(end_num), int(start_num))
        else:
            sai_res = random.randint(int(start_num), int(end_num))
        return str(sai_res)
    except Exception as e:
        app.logger.error("Exception at decide_num: %s", e)
        return "分かりません"

if __name__ == '__main__':
    port = int(os.getenv("PORT", 5000))
    app.debug = True
    app.run(host="0.0.0.0", port=port)

```

```YOUR.EXTENTION.ID ```には、先ほど基本設定で決めたExtentionIDを記入します。細かいコードの解説はしませんが、このコードの中の関数```lambda_handler```はアプリをビルドした時にアプリが正常に起動しているかを確認するために入れているので、スキルのコードとは関係がありません。関数```my_service```でClovaからのリクエストを受けています。

# herokuへのデプロイ
メインのコードがかけたので、herokuにデプロイしていきます。はじめにherokuアプリを作成し、Heroku CLIをインストールしてから、以下のコマンドを実行します。

```
$ heroku login
$ heroku git:clone -a （herokuアプリ名）
$ cd (herokuアプリ名)
```
クローンしたディレクトリ内にpythonコードを保存します。次に同じディレクトリ内にデプロイするのに必要なファイルを作成します。具体的にはpythonのライブラリをインストールするファイルと、heroku上でメインコードを実行するためのファイルです。

```requirements.txt
clova-cek-sdk
flask
```

```:Procfile 
web: python main.py
```
ここまで終わったら、heroku上にデプロイします。

```
$ git add .
$ git commit -am "make it better"
$ git push heroku master
```
それでは、デプロイしたアプリのURLである「https://(アプリ名).herokuapp.com」をブラウザ上でアクセスし、「hello from Flask!」とブラウザに表示されて入れば、アプリは正常にデプロイされています。
今回作成したコードは以下のGithubのレポジトリにあります。
https://github.com/Miura55/hello-clova

# テスト
ここから実際にテストをしていきますが、その前に先ほどClova Developer Centerのサーバー設定にherokuアプリのClovaのPOSTリクエストを受け付ける「https://(アプリ名).herokuapp.com/clova」のリンクを記入します。保存したら対話モデルを開き、作成した対話モデルが返答するかテストします。対話モデルを設定するページの左上にある「テスト」をクリックし、発話のテストします。

![スクリーンショット 2018-11-13 02.50.27.png](https://qiita-image-store.s3.amazonaws.com/0/170520/7f0be80e-f91e-10f6-47d3-ac83bf1233e1.png)

しっかり設定ができていれば、以下の返答が出力されます。

![スクリーンショット 2018-11-13 02.52.41.png](https://qiita-image-store.s3.amazonaws.com/0/170520/86dbc8b9-7080-4378-3a30-d0b492c3b347.png)

## 実機でのテスト
いよいよ実機を使ったテストです。
**Clova、サイコロさんを起動して**
と話かけてみましょう。すると、***「こんにちは，サイコロに設定したい数字を指定してください」***と帰ってきますので、数字の範囲を指定すると返答が帰ってきます。エラーで数字が出力できなくても「わかりませんでした」と返ってきます。

#まとめ
Clovaは、pythonで素人でも簡単にスマートスピーカーのスキルを作成することができて、アイデア次第でいろんなスキルを作れそうだと感じました。LINEと連携すればClovaでしか出来ないようなスキルが出来るのも魅力的です。次のハッカソンで取り入れられるといいなと思います。ただ、herokuで構築すると、スリープ状態になった時にスキルの起動に時間がかかってしまい、タイムアウトでエラーになってしまうのはネックですね。もし、スキルを公開するならAWSなどで構築するのがいいと感じました。