PythonでAlexa Hello World

# GitHub.com の[ASK SDK for Python (JA)](https://alexa-skills-kit-python-sdk.readthedocs.io/ja/latest/DEVELOPING_YOUR_FIRST_SKILL.html#id3)のチュートリアルの補足

[初めてのスキル開発](https://alexa-skills-kit-python-sdk.readthedocs.io/ja/latest/DEVELOPING_YOUR_FIRST_SKILL.html#id1)の[AWS Lambda用にコードを準備する](https://alexa-skills-kit-python-sdk.readthedocs.io/ja/latest/DEVELOPING_YOUR_FIRST_SKILL.html#aws-lambda)のセクションで、SkillのLambdaコードをパッケージ化してデプロイする手順があるのですが、この手順が少し説明不足なので補足。

1. ローカルでskillという作業用のフォルダを作る。
1. skillフォルダ内に移動し、hello_world.py 新規で作成する。    

    ```
    skill
    └── hello_world.py 
    ```

1. Pythonのスキルのコードは、クラスベースとデコレーターベースの二つの作り方があるが、どちらでも良い。クラスベースの方が、ASK SDK for Node.jsの書き方に似てるので、個人的にはわかりやすいと思う。
1. 手順通り hello_world.pyの中身を記述。[こちら](https://github.com/alexa/skill-sample-python-helloworld-classes/blob/master/lambda/py/hello_world.py)のコードを丸ごとコピペでもOK。
1. さて、ここで「AWS Lambda用にコードを準備する」の手順に入るが、ここでは**特定のフォルダへのSDKセットアップ**を詳しく解説
2. ターミナルを開き、skillフォルダに移動する。

    ```
    $ cd Skill
    ```

3. pipコマンドで、ask-sdk-core SDKライブラリをダウンロードする。必要な依存ライブラリも一緒にダウンロードされる。

    ```
    $ pip install ask-sdk-core -t .
    ```

    現時点で中身はこんな感じになる。

    ```
    .
    ├── __pycache__
    ├── ask_sdk_core
    ├── ask_sdk_core-1.10.2.dist-info
    ├── ask_sdk_model
    ├── ask_sdk_model-1.14.0.dist-info
    ├── ask_sdk_runtime
    ├── ask_sdk_runtime-1.10.2.dist-info
    ├── bin
    ├── certifi
    ├── certifi-2019.6.16.dist-info
    ├── chardet
    ├── chardet-3.0.4.dist-info
    ├── dateutil
    ├── hello_world.py
    ├── idna
    ├── idna-2.8.dist-info
    ├── python_dateutil-2.8.0.dist-info
    ├── requests
    ├── requests-2.22.0.dist-info
    ├── six-1.12.0.dist-info
    ├── six.py
    ├── urllib3
    └── urllib3-1.25.3.dist-info
    ````

1. ここからAWS Lambdaのエンドポイントにデプロイするためのパッケージ(ZIPファイル)を作成する。zipファイルの中身にはskillフォルダを含めてはいけないことに注意。直下にライブラリ群が入っている必要がある。また、ライブラリ群とhello_world.pyファイルも同じ階層でなければならない。

    ````
    $ zip ../skill.zip -r .
    ````

    これで一つ上の階層にskill.zipファイルが作成される。
    
    ```
    ├── skill
    │   ├── __pycache__
    │   ├── ask_sdk_core
    │   ├── hello_world.py
    │   <省略>
    └── skill.zip
    ```

1. 作成したzipファイルをAWS LambdaにアップロードすればOK
    
    ![UploadZip.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63960/23796d47-070e-4e5b-43c6-ce13eb1e1023.png)


## オマケ

公式のhello_world.pyを日本語で動くようにしたソースです。

### hello_world.py
```python
from ask_sdk_core.dispatch_components import AbstractExceptionHandler
from ask_sdk_model.ui import SimpleCard
from ask_sdk_model import Response
from ask_sdk_core.handler_input import HandlerInput
from ask_sdk_core.utils import is_request_type, is_intent_name
from ask_sdk_core.dispatch_components import AbstractRequestHandler
from ask_sdk_core.skill_builder import SkillBuilder


class LaunchRequetHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return is_request_type("LaunchRequest")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        speech_text = "ようこそ、アレクサスキルキットへ、こんにちは、と言ってみてください。"

        handler_input.response_builder.speak(speech_text).ask(speech_text).set_card(
            SimpleCard("ハローパイソン", speech_text)).set_should_end_session(False)

        return handler_input.response_builder.response


class HelloWorldIntentHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        return is_intent_name("HelloWorldIntent")(handler_input)

    def handle(self, handler_input):
        speech_text = "こんにちは"

        handler_input.response_builder.speak(speech_text).set_card(
            SimpleCard("ハローパイソン", speech_text)).set_should_end_session(True)

        return handler_input.response_builder.response


class HelpIntentHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        return is_intent_name("AMAZON.HelpIntent")(handler_input)

    def handle(self, handler_input):
        speech_text = "こんにちは、と言ってみてください。"

        handler_input.response_builder.speak(speech_text).ask(speech_text).set_card(
            SimpleCard("ハローパイソン", speech_text)).set_should_end_session(False)

        return handler_input.response_builder.response


class CancelAndStopIntentHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        return is_intent_name("AMAZON.CancelIntent")(handler_input) or is_intent_name("AMAZON.StopIntent")(handler_input)

    def handle(self, handler_input):
        speech_text = "さようなら"

        handler_input.response_builder.speak(speech_text).set_card(
            SimpleCard("ハローパイソン", speech_text)).set_should_end_session(False)

        return handler_input.response_builder.response


class SessionEndedRequestHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        return is_request_type("SessionEndedRequest")(handler_input)

    def handle(self, handler_input):

        # Nothing to do

        return handler_input.response_builder.response


class AllExceptionHandler(AbstractExceptionHandler):
    def can_handle(self, handler_input, exception):

        return True

    def handle(self, handler_input, exception):
        # CloudWatch Logsに例外を書き出す
        print(exception)

        speech_text = "すみません。もう一度言ってください"
        handler_input.response_builder.speek(speech_text).ask(speech_text)
        return handler_input.response_builder.response


# Lambda Handlers
sb = SkillBuilder()
sb.add_request_handler(LaunchRequetHandler())
sb.add_request_handler(HelloWorldIntentHandler())
sb.add_request_handler(HelpIntentHandler())
sb.add_request_handler(CancelAndStopIntentHandler())
sb.add_request_handler(SessionEndedRequestHandler())
sb.add_exception_handler(AllExceptionHandler())

handler = sb.lambda_handler()
```

## モデル ja-JP.json
```json
{
    "interactionModel": {
        "languageModel": {
            "invocationName": "ハローパイソン",
            "intents": [
                {
                    "name": "AMAZON.CancelIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.HelpIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.StopIntent",
                    "samples": []
                },
                {
                    "name": "HelloWorldIntent",
                    "slots": [],
                    "samples": [
                        "ハロー",
                        "こんにちは",
                        "ハローと言って",
                        "こんにちはと言って"
                    ]
                },
                {
                    "name": "AMAZON.NavigateHomeIntent",
                    "samples": []
                }
            ],
            "types": []
        }
    }
}
```