Alexa Skills Kit SDK for Node.js ざっくり訳

このポストは [alexa\-skills\-kit\-sdk\-for\-nodejs](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs)についてのものだが、[V２](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/tree/2.0.x)がリリースされ、すでに過去のバージョンについてのものになった。バージョンが上がり、SDKの機能も実装スタイルも変わったので、リポジトリの[V2のドキュメントの日本語訳](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/wiki/%5BJapanese%5D-Home)を参考にされるのがよいと思う。

v1についてのドキュメントを参照するならば、
2018年3月、リポジトリに[日本語版](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/blob/master/Readme.jp.md)が追加されたのでそちらを参照いただきたい。

---

以下は私が[alexa\-skills\-kit\-sdk\-for\-nodejs/Readme\.md](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/blob/c5f9f935c4c42686ae2a2d0373fb26010f7a1473/Readme.md)をざっくり訳したもの。v1のものである。

## Overview

Alexa SDKチームは新しいAlexa Node.js SDKをリリースする。これは開発者による開発者のためのオープンソースのSDKだ。

Alexa Skill Kit、Node.jsそしてLambdaの組み合わせは最も人気あるAlexaスキル開発のソフトウェアスタックだ。
イベントドリブンでノンブロッキングなI/Oを持つNode.jsはAlexaスキルによく合っており、Node.jsはオープンソースライブラリの巨大なエコシステムでもある。加えて、AWS Lambdaは100万リクエスト/月が無料でほとんどの開発者にとって十分な無料枠だ。また、Alexaトリガーは信頼できるので、LambdaのSSL証明書の管理もいらない。

AWS Lambdaと Node.js、Alexa Skills Kitを使ったAlexaスキルのセットアップはシンプルだ。しかもコードも少なくて済む。Alexa SDKチームはAlexa Skills Kit SDK specifically for Node.js を共通の問題を簡単に実装し、スキルのロジックに注力できるように作った。

新しいalexa-sdkを使えば、早くそして、不要な複雑さを避けた開発ができる。
SDKは次の特徴がある。

- npmパッケージなのでどんなNode.js環境にも容易にデプロイできる
- 組み込みイベントを使ってAlexaのレスポンスを生成できる
- 新規セッションと未処理イベントのためのキャッチオールイベント
- インテント処理ベースのインベントマシーンを作るヘルパ関数
- 属性をDynamoDBに永続化できるシンプルな設定
- すべての音声出力は自動的にSSMLでラップされる
- `this.event` と `this.context`を通じて、Lambdaのイベントとコンテキストも利用できる
- 組み込み関数をオーバーライドして状態管理やレスポンスの生成を柔軟に変更できる。たとえば、S3に状態を保存することも可能だ。

## Setup Guide
alexa-sdkは[Github](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs)からすぐに利用できる。次のコマンドでインストールできる。

```
npm install --save alexa-sdk
```
## Getting Started: Writing a Hello World Skill
### Basic Project Structure
HelloWorldスキルでは次が必要。

- スキルのエントリーポイント。
    - すべての必要なパッケージ、イベントの受信、appIdの設定、dynamoDBテーブルの設定、ハンドラの登録などが含まれる。
- それぞれのリクエストを扱うハンドラ関数

### Set Entry Point
上記のため、次の内容でindex.jsを作る

```javascript:index.js
const Alexa = require('alexa-sdk');

exports.handler = function(event, context, callback) {
    const alexa = Alexa.handler(event, context, callback);
    alexa.appId = APP_ID // APP_ID は Amazon developer consoleでスキルを作ると発行されるskill id
    alexa.execute();
};
```
これで、alexa-sdkをインポートして、Alexaオブジェクトを生成できる。

### Implement Handler Functions
次に必要なのは、スキルのイベントとインテントを処理すること。Alexa-sdkを使えばインテントを簡単に扱える。
ハンドラ関数をindex.jsに実装すればよい。あるいは別のファイルに実装してインポートしても良い。
例えば、`HelloWorldIntent` ハンドラを作るには2つの実装方法がある。

```javascript
const handlers = {
    'HelloWorldIntent' : function() {
        //emit response directly
        this.emit(':tell', 'Hello World!');
    }
};
```
あるいは次の様にも書ける。

```javascript
const handlers = {
    'HelloWorldIntent' : function() {
        //build response first using responseBuilder and then emit
        this.response.speak('Hello World!');
        this.emit(':responseReady');
    }
};
```

Alexa-sdk では `responseBuilder` の `speak`/`listen` で生成する発話レスポンスオブジェクトを `tell`/`ask`を使って実装することもできる。

```javascript
this.emit(':tell', 'Hello World!'); 
this.emit(':ask', 'What would you like to do?', 'Please say that again?');
```
これは次と同じだ。

```javascript
this.response.speak('Hello World!');
this.emit(':responseReady');

this.response.speak('What would you like to do?')
             .listen('Please say that again?');
this.emit(':responseReady');
```

`:ask`/`listen` と `:tell`/`speak`の違いは `:tell`/`speak`がユーザのインプットの待受をやめてセッションを終了するということだ。
次節でレスポンスオブジェクトを作る場合の2つの方法の違いについて比較する。

ハンドラは互いにリクエストを送り合うことができる。ハンドラのチェーンも可能だ。
次の例では `LaunchRequest` と `HelloWorldIntent` が同じ'Hello World'を返す。

```javascript
const handlers = {
    'LaunchRequest': function () {
    	this.emit('HelloWorldIntent');
    },

    'HelloWorldIntent': function () {
    	this.emit(':tell', 'Hello World!');
    }
};
```

イベントハンドラーを作ったら、それを `registerHandlers` でAlexaオブジェクトに登録する必要がある。
index.js に次のように追加する

```javascript:index.js
const Alexa = require('alexa-sdk');

exports.handler = function(event, context, callback) {
    const alexa = Alexa.handler(event, context, callback);
    alexa.registerHandlers(handlers); //<= ここを追加
    alexa.execute();
};
```
複数のハンドラを一度に登録することもできる。

```javascript
alexa.registerHandlers(handlers, handlers2, handlers3, ...);
```
ここまでのステップを終えれば、スキルはデバイスで動作する。

## Response vs ResponseBuilder
Node.js SDK では[response objects](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#Response%20Format)を生成する2つの方法がある。

一つ目の方法は `this.emit(:${action}, 'responseContent')` だ。
以下に一般的なスキルのレスポンスの完全なリストの例を示す。

|Response Syntax | Description |
|----------------|-----------|
| this.emit(':tell',speechOutput);|Tell with [speechOutput](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#outputspeech-object)| 
|this.emit(':ask', speechOutput, repromptSpeech);|Ask with [speechOutput](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#outputspeech-object) and [repromptSpeech](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#reprompt-object)|
|this.emit(':tellWithCard', speechOutput, cardTitle, cardContent, imageObj);| Tell with [speechOutput](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#outputspeech-object) and [standard card](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#card-object)|
|this.emit(':askWithCard', speechOutput, repromptSpeech, cardTitle, cardContent, imageObj);| Ask with [speechOutput](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#outputspeech-object), [repromptSpeech](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#reprompt-object) and [standard card](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#card-object)|
|this.emit(':tellWithLinkAccountCard', speechOutput);|  Tell with [linkAccount card](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#card-object), for more information, click [here](https://developer.amazon.com/docs/custom-skills/link-an-alexa-user-with-a-user-in-your-system.html)|
|this.emit(':askWithLinkAccountCard', speechOutput);| Ask with [linkAccount card](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#card-object), for more information, click [here](https://developer.amazon.com/docs/custom-skills/link-an-alexa-user-with-a-user-in-your-system.html)|
|this.emit(':tellWithPermissionCard', speechOutput, permissionArray);| Tell with [permission card](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#session-object), for more information, click [here](https://developer.amazon.com/docs/custom-skills/configure-permissions-for-customer-information-in-your-skill.html)|
|this.emit(':askWithPermissionCard', speechOutput, repromptSpeech, permissionArray)| Ask with [permission card](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#session-object), for more information, click [here](https://developer.amazon.com/docs/custom-skills/configure-permissions-for-customer-information-in-your-skill.html)|
|this.emit(':delegate', updatedIntent);|Response with [delegate directive](https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html#delegate) in [dialog model](https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html#dialog-model-required)|
|this.emit(':elicitSlot', slotToElicit, speechOutput, repromptSpeech, updatedIntent);|Response with [elicitSlot directive](https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html#elicitslot) in [dialog model](https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html#dialog-model-required)|
|this.emit(':elicitSlotWithCard', slotToElicit, speechOutput, repromptSpeech, cardTitle, cardContent, updatedIntent, imageObj);| Response with [card](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#card-object) and [elicitSlot directive](https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html#elicitslot) in [dialog model](https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html#dialog-model-required)|
|this.emit(':confirmSlot', slotToConfirm, speechOutput, repromptSpeech, updatedIntent);|Response with [confirmSlot directive](https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html#confirmslot) in [dialog model](https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html#dialog-model-required)|
|this.emit(':confirmSlotWithCard', slotToConfirm, speechOutput, repromptSpeech, cardTitle, cardContent, updatedIntent, imageObj);| Response with [card](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#card-object) and [confirmSlot directive](https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html#confirmslot) in [dialog model](https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html#dialog-model-required)|
|this.emit(':confirmIntent', speechOutput, repromptSpeech, updatedIntent);|Response with [confirmIntent directive](https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html#confirmintent) in [dialog model](https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html#dialog-model-required)|
|this.emit(':confirmIntentWithCard', speechOutput, repromptSpeech, cardTitle, cardContent, updatedIntent, imageObj);| Reponse with [card](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#card-object) and [confirmIntent directive](https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html#confirmintent) in [dialog model](https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html#dialog-model-required)|
|this.emit(':responseReady');|レスポンスを作る前でなく、後に呼ぶこと。これはAlexaサービスに処理を返す。また、saveStateを呼ぶ。オーバーライド可能|
|this.emit(':saveState', false);|`this.attributes`の内容と現在のハンドラの状態をDynamoDBに保存する。前もって作ったレスポンスをAlexaサービスに送る。もし別の永続化を使いたければオーバーライド可能。第２引数は任意で、`true`にすると強制保存する|
|this.emit(':saveStateError'); |状態保存時にエラーが発生すれば呼ばれる。エラー処理をオーバーライドできる|

もし、自分でレスポンス生成を実装するなら、`this.response`を使う。`this.response`は
レスポンスの属性をそれぞれ設定する関数群を持っている。
Alexa Skills Kit組み込みの音声プレイヤー、動画ブレイヤーをサポートする時に便利だ。
レスポンスを作ったら、`this.emit(':responseReady')`を実行すれば、Alexaに送信される。
`this.response`の関数はチェーン可能なので、1行に実装もできる。

次は responseBuilder を使う場合のレスポンス作成例のリストだ。

|Response Syntax | Description |
|----------------|-----------|
|this.response.speak(speechOutput);| 最初の発話出力を [speechOutput](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#outputspeech-object)に設定する|
|this.response.listen(repromptSpeech);| 聞き返しの発話出力を [repromptSpeech](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#reprompt-object)にセットし `shouldEndSession`を `false`にする。この関数が呼ばれなければ, `this.response` の shouldEndSession は `true`となる.|
|this.response.cardRenderer(cardTitle, cardContent, cardImage);| レスポンスに [standard card](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#card-object)を追加する。 `cardTitle`, `cardContent` and `cardImage` で構成される。|
|this.response.linkAccountCard();| レスポンスに [linkAccount card](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#card-object) を追加する。 [こちら](https://developer.amazon.com/docs/custom-skills/link-an-alexa-user-with-a-user-in-your-system.html)を参照のこと|
|this.response.askForPermissionsConsentCard(permissions);| [perimission](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#session-object)を求めるカードをレスポンスに追加する。 [こちら](https://developer.amazon.com/docs/custom-skills/configure-permissions-for-customer-information-in-your-skill.html)を参照のこと|
|this.response.audioPlayer(directiveType, behavior, url, token, expectedPreviousToken, offsetInMilliseconds);(Deprecated) |[AudioPlayer directive](https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html) をパラメータとともにレスポンスに追加する|
|this.response.audioPlayerPlay(behavior, url, token, expectedPreviousToken, offsetInMilliseconds);| [AudioPlayer directive](https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html) をパラメータとともに追加し、[`AudioPlayer.Play`](https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#play)をセットする|
|this.response.audioPlayerStop();|  [AudioPlayer.Stop directive](https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#stop)を追加する|
|this.response.audioPlayerClearQueue(clearBehavior);| [AudioPlayer.ClearQueue directive](https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#clearqueue)を追加し、 directiveのclearBehaviorをセットする|
|this.response.renderTemplate(template);| [Display.RenderTemplate directive](https://developer.amazon.com/docs/custom-skills/display-interface-reference.html) をレスポンスに追加する|
|this.response.hint(hintText, hintType);|  [Hint directive](https://developer.amazon.com/docs/custom-skills/display-interface-reference.html#hint-directive) をレスポンスに追加する|
|this.response.playVideo(videoSource, metadata);| [VideoApp.Play directive](https://developer.amazon.com/docs/custom-skills/videoapp-interface-reference.html#videoapp-directives)をレスポンスに追加する|
|this.response.shouldEndSession(bool);| shouldEndSession を追加する|

レスポンスを生成したら、`this.emit(':responseReady')`を呼ぶだけで送信される。
次の2つの例はレスポンスオブジェクトを生成する。

```javascript
//Example 1
this.response.speak(speechOutput)
            .listen(repromptSpeech);
this.emit(':responseReady');
//Example 2
this.response.speak(speechOutput)
            .cardRenderer(cardTitle, cardContent, cardImage)
            .renderTemplate(template)
            .hint(hintText, hintType);
this.emit(':responseReady');
```
responseBuilder はレスポンスオブジェクトを生成する柔軟な方法なので、利用をおすすめする。

### Tips
- レスポンスイベントが`:ask`, `:tell`, `:askWithCard`などを発行するとき、Lambda関数は`context.succeed()`を実行する。`callback`関数を渡していなければ、バックグラウンドのタスクの処理がすぐに停止する。終了していない非同期のジョブは完了せず、その後のコードも実行されない。`：saveState`のような応答のないイベントは除く。
- リクエストをある状態から他の状態に「送る」ことをインテントのフォワーディングといい、`this.handler.state` に送り先の状態をセットする。そして、もしターゲットの状態が””なら、`this.emit("TargetHandlerName")`を実行する。他の状態なら `this.emitWithState("TargetHandlerName")` を実行する。

- prompt と reprompt の値はSSMLのタグで囲われる。XMLとして意味を持つ文字はエスケープしておく必要がある。例えば、`this.emit(":ask", "I like M&M's")`を実行するなら、`&`は`&amp;`にエスケープしないとエラーになる。`<` を `&lt;`, `>` を `&gt;`とエスケープするのも同様。

## Standard Request and Response
Alexa は HTTPS でスキルサービスと通信する。ユーザがAlexaスキルを実行すると スキルサービスは HTTP POSTでJSONを受け取る。リクエストにはロジックを動かし、JSONのレスポンスを返すのに必要なパラメータが含まれる。Node.jsがネイティブにJSONを処理できるのでAlexa Node.js SDKではJSONのシリアライゼーション処理が不要となっている。Alexaがユーザの要求に応えられるよう適切なレスポンスを返さねばならない。リクエストのJSONの仕様は[こちら](https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#request-format)を参照.

レスポンスには次の属性を含む必要がある。
- OutputSpeech
- Reprompt
- Card
- List of Directives
- shouldEndSession

次は発話とカード両方を含む例だ

```javascript
const speechOutput = 'Hello world!';
const repromptSpeech = 'Hello again!';
const cardTitle = 'Hello World Card';
const cardContent = 'This text will be displayed in the companion app card.';
const imageObj = {
	smallImageUrl: 'https://imgs.xkcd.com/comics/standards.png',
	largeImageUrl: 'https://imgs.xkcd.com/comics/standards.png'
      };
this.response.speak(speechOutput)
             .listen(repromptSpeech)
             .cardRenderer(cardTitle, cardContent, imageObj);
this.emit(':responseReady');
```

## Interfaces 

### AudioPlayer Interface
スキルのレスポンスに次のディレクティブをそれぞれ含めることができる。

- `PlayDirective`
- `StopDirective`
- `ClearQueueDirective`

次の例は`PlayDirective` で音声を再生する

```javascript
const handlers = {
    'LaunchRequest' : function() {
        const speechOutput = 'Hello world!';
        const behavior = 'REPLACE_ALL';
        const url = 'https://url/to/audiosource';
        const token = 'myMusic';
        const expectedPreviousToken = 'expectedPreviousStream';
        const offsetInMilliseconds = 10000;
        this.response.speak(speechOutput)
                     .audioPlayerPlay(behavior, url, token, expectedPreviousToken, offsetInMilliseconds);
        this.emit(':responseReady');
    }
};
```
上の例では最初に`speechOutput`を発話してから音声再生を試みる。

[AudioPlayer](https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html)インターフェースを使ったスキルを開発するとき、再生状態を変更するために `playback`リクエストを送信する。それぞれのイベントに対応したハンドラ関数を実装することができる。

```javascript
const handlers = {
    'PlaybackStarted' : function() {
    	console.log('Alexa begins playing the audio stream');
    },
    'PlaybackFinished' : function() {
    	console.log('The stream comes to an end');
    },
    'PlaybackStopped' : function() {
    	console.log('Alexa stops playing the audio stream');
    },
    'PlaybackNearlyFinished' : function() {
    	console.log('The currently playing stream is nearly complate and the device is ready to receive a new stream');
    },
    'PlaybackFailed' : function() {
    	console.log('Alexa encounters an error when attempting to play a stream');
    }
};
```

`AudioPlayer`インターフェースのドキュメントは[こちら](https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html)

注意： `imgObj`に関する仕様は[こちら](https://developer.amazon.com/docs/custom-skills/include-a-card-in-your-skills-response.html#creating-a-home-card-to-display-text-and-an-image)

### Dialog Interface
`Dialog`インターフェースはAlexaとユーザのマルチターン会話を管理するディレクティブを提供する。ユーザのリクエストに応えるための情報を尋ねるのに使える。ダイアログディレクティブについて詳しくは[Dialog Interface](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/dialog-interface-reference)と[Skill Editor](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/ask-define-the-vui-with-gui)を参照のこと。

現在の`dialogState`を見るには`this.event.request.dialogState`を参照する。

#### Delegate Directive
ユーザーとのダイアログで次のターンを処理するコマンドをAlexaに送信する。スキルがダイアログモデルを実装しており、ダイアログの状態(`dialogState`)が`STARTED` か `IN_PROGRESS`なら、このディレクティブが使える。もし `dialogState` が `COMPLETED`ならこのディレクティブを発行できない。

`this.emit(':delegate')`を使うとディレクティブの応答をAlexaに任せることができる

```javascript
const handlers = {
    'BookFlightIntent': function () {
        if (this.event.request.dialogState === 'STARTED') {
            let updatedIntent = this.event.request.intent;
            // Pre-fill slots: update the intent object with slot values for which
            // you have defaults, then emit :delegate with this updated intent.
            updatedIntent.slots.SlotName.value = 'DefaultValue';
            this.emit(':delegate', updatedIntent);
        } else if (this.event.request.dialogState !== 'COMPLETED'){
            this.emit(':delegate');
        } else {
            // All the slots are filled (And confirmed if you choose to confirm slot/intent)
            handlePlanMyTripIntent();
        }
    }
};
```

#### Elicit Slot Directive
あるスロットの値をユーザから聞き出すようをAlexaに指示する。`slotToElicit`で聞き出すスロットの名前を指定する。スロットの値を聞き出すためのフレーズを`speechOutput`に指定する。

`this.emit(':elicitSlot', slotToElicit, speechOutput, repromptSpeech, updatedIntent)`あるいは`this.emit(':elicitSlotWithCard', slotToElicit, speechOutput, repromptSpeech, cardTitle, cardContent, updatedIntent, imageObj)`を使ってスロットを聞き出す指示を送る。

`this.emit(':elicitSlotWithCard', slotToElicit, speechOutput, repromptSpeech, cardTitle, cardContent, updatedIntent, imageObj)`を使う場合、`updatedIntent` と `imageObj`は任意で指定する（`null`を渡したり、設定しなくても良い）。

```javascript
const handlers = {
    'BookFlightIntent': function () {
        const intentObj = this.event.request.intent;
        if (!intentObj.slots.Source.value) {
            const slotToElicit = 'Source';
            const speechOutput = 'Where would you like to fly from?';
            const repromptSpeech = speechOutput;
            this.emit(':elicitSlot', slotToElicit, speechOutput, repromptSpeech);
        } else if (!intentObj.slots.Destination.value) {
            const slotToElicit = 'Destination';
            const speechOutput = 'Where would you like to fly to?';
            const repromptSpeech = speechOutput;
            const cardContent = 'What is the destination?';
            const cardTitle = 'Destination';
            const updatedIntent = intentObj;
            // An intent object representing the intent sent to your skill.
            // You can use this property set or change slot values and confirmation status if necessary.
            const imageObj = {
                smallImageUrl: 'https://imgs.xkcd.com/comics/standards.png',
                largeImageUrl: 'https://imgs.xkcd.com/comics/standards.png'
            };
            this.emit(':elicitSlotWithCard', slotToElicit, speechOutput, repromptSpeech, cardTitle, cardContent, updatedIntent, imageObj);
        } else {
            handlePlanMyTripIntentAllSlotsAreFilled();
        }
    }
};
```

#### Confirm Slot Directive
これはAlexaにあるスロットの値の値を確認してからダイアログを続行するように指示する。`slotToConfirm`で確認するスロットの名前を指定する。ユーザに確認する際の台詞は`speechOutput`で指定する。

`this.emit(':confirmSlot', slotToConfirm, speechOutput, repromptSpeech, updatedIntent)` または `this.emit(':confirmSlotWithCard', slotToConfirm, speechOutput, repromptSpeech, cardTitle, cardContent, updatedIntent, imageObj)`を使って、スロットを確認するように指示する。

`this.emit(':confirmSlotWithCard', slotToConfirm, speechOutput, repromptSpeech, cardTitle, cardContent, updatedIntent, imageObj)`を使う場合、`updatedIntent` と `imageObj`は任意だ。`null`を渡すか、何も渡さなければ指定しないことになる。

```javascript
const handlers = {
    'BookFlightIntent': function () {
        const intentObj = this.event.request.intent;
        if (intentObj.slots.Source.confirmationStatus !== 'CONFIRMED') {
            if (intentObj.slots.Source.confirmationStatus !== 'DENIED') {
                // Slot value is not confirmed
                const slotToConfirm = 'Source';
                const speechOutput = 'You want to fly from ' + intentObj.slots.Source.value + ', is that correct?';
                const repromptSpeech = speechOutput;
                this.emit(':confirmSlot', slotToConfirm, speechOutput, repromptSpeech);
            } else {
                // Users denies the confirmation of slot value
                const slotToElicit = 'Source';
                const speechOutput = 'Okay, Where would you like to fly from?';
                this.emit(':elicitSlot', slotToElicit, speechOutput, speechOutput);
            }
        } else if (intentObj.slots.Destination.confirmationStatus !== 'CONFIRMED') {
            if (intentObj.slots.Destination.confirmationStatus !== 'DENIED') {
                const slotToConfirm = 'Destination';
                const speechOutput = 'You would like to fly to ' + intentObj.slots.Destination.value + ', is that correct?';
                const repromptSpeech = speechOutput;
                const cardContent = speechOutput;
                const cardTitle = 'Confirm Destination';
                this.emit(':confirmSlotWithCard', slotToConfirm, speechOutput, repromptSpeech, cardTitle, cardContent);
            } else {
                const slotToElicit = 'Destination';
                const speechOutput = 'Okay, Where would you like to fly to?';
                const repromptSpeech = speechOutput;
                this.emit(':elicitSlot', slotToElicit, speechOutput, repromptSpeech);
            }
        } else {
            handlePlanMyTripIntentAllSlotsAreConfirmed();
        }
    }
};
```

#### Confirm Intent Directive
これはAlexaにスキルの処理実行前にすべての情報を確認するためのものだ。`speechOutput`でユーザへの問いかけを指定する。ユーザは問いかけのすべての値を確認する必要がある。

`this.emit(':confirmIntent', speechOutput, repromptSpeech, updatedIntent)` 
または
`this.emit(':confirmIntentWithCard', speechOutput, repromptSpeech, cardTitle, cardContent, updatedIntent, imageObj)`
を使う。

`this.emit(':confirmIntentWithCard', speechOutput, repromptSpeech, cardTitle, cardContent, updatedIntent, imageObj)`では、`updatedIntent` and `imageObj`は任意で指定する。

```javascript
const handlers = {
    'BookFlightIntent': function () {
        const intentObj = this.event.request.intent;
        if (intentObj.confirmationStatus !== 'CONFIRMED') {
            if (intentObj.confirmationStatus !== 'DENIED') {
                // Intent is not confirmed
                const speechOutput = 'You would like to book flight from ' + intentObj.slots.Source.value + ' to ' +
                intentObj.slots.Destination.value + ', is that correct?';
                const cardTitle = 'Booking Summary';
                const repromptSpeech = speechOutput;
                const cardContent = speechOutput;
                this.emit(':confirmIntentWithCard', speechOutput, repromptSpeech, cardTitle, cardContent);
            } else {
                // Users denies the confirmation of intent. May be value of the slots are not correct.
                handleIntentConfimationDenial();
            }
        } else {
            handlePlanMyTripIntentAllSlotsAndIntentAreConfirmed();
        }
    }
};
```
`Dialog`インターフェースについてのドキュメントは[こちら](https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html)

### Display Interface
Alexa provides several `Display templates` to support a wide range of presentations. Currently, there are two categories of `Display templates`:

Alexaは、さまざまなプレゼンテーションをサポートするため、いくつかの`Display templates`を提供する。現状、`Display templates`には2つのカテゴリーがある。

- `BodyTemplate` はテキストを画像を表示する。それらは選択的には表示できない。現状、4つのオプションがある。
  + `BodyTemplate1`
  + `BodyTemplate2`
  + `BodyTemplate3`
  + `BodyTemplate6`
  + `BodyTemplate7`
- `ListTemplate`はスクロールできるリストを表示する。それぞれ、テキストと任意で画像を表示できる。現状、２つのオプションがある。
  + `ListTemplate1`
  + `ListTemplate2`

Developers must include `Display.RenderTemplate` directive in their skill responses.
Template Builders are now included in alexa-sdk in the templateBuilders namespace. These provide a set of helper methods to build the JSON template for the `Display.RenderTemplate` directive. In the example below we use the `BodyTemplate1Builder` to build the `Body template`.

`Display.RenderTemplate`ディレクティブをレスポンスに含める必要がある。
alexa-sdk ではtemplateBuildersにテンプレートビルダーが含まれている。`Display.RenderTemplate`ディレクトのためにJSONのテンプレートを作るヘルパーメソッドがある。
次の例は`BodyTemplate1Builder`で`Body template`を作るものだ。

```javascript
const Alexa = require('alexa-sdk');
// utility methods for creating Image and TextField objects
const makePlainText = Alexa.utils.TextUtils.makePlainText;
const makeImage = Alexa.utils.ImageUtils.makeImage;

// ...
'LaunchRequest' : function() {
	const builder = new Alexa.templateBuilders.BodyTemplate1Builder();

	const template = builder.setTitle('My BodyTemplate1')
							.setBackgroundImage(makeImage('http://url/to/my/img.png'))
							.setTextContent(makePlainText('Text content'))
							.build();

	this.response.speak('Rendering a body template!')
				.renderTemplate(template);
	this.emit(':responseReady');
}
```

画像とテキストフィールドオブジェクトを作るためのユーティリティメソッドもあり、`Alexa.utils`に含まれている。

```javascript
const ImageUtils = require('alexa-sdk').utils.ImageUtils;

// Outputs an image with a single source
ImageUtils.makeImage(url, widthPixels, heightPixels, size, description);
/**
Outputs {
    contentDescription : '<description>'
    sources : [
        {
            url : '<url>',
            widthPixels : '<widthPixels>',
            heightPixels : '<heightPixels>',
            size : '<size>'
        }
    ]
}
*/

ImageUtils.makeImages(imgArr, description);
/**
Outputs {
    contentDescription : '<description>'
    sources : <imgArr> // array of {url, size, widthPixels, heightPixels}
}
*/


const TextUtils = require('alexa-sdk').utils.TextUtils;

TextUtils.makePlainText('my plain text field');
/**
Outputs {
    text : 'my plain text field',
    type : 'PlainText'
}
*/

TextUtils.makeRichText('my rich text field');
/**
Outputs {
    text : 'my rich text field',
    type : 'RichText'
}
*/

```

次の例では、 `ListTemplate1Builder` と `ListItemBuilder` で ListTemplate1 を作っている。

```javascript
const Alexa = require('alexa-sdk');
const makePlainText = Alexa.utils.TextUtils.makePlainText;
const makeImage = Alexa.utils.ImageUtils.makeImage;
// ...
'LaunchRequest' : function() {
    const itemImage = makeImage('https://url/to/imageResource', imageWidth, imageHeight);
    const listItemBuilder = new Alexa.templateBuilders.ListItemBuilder();
    const listTemplateBuilder = new Alexa.templateBuilders.ListTemplate1Builder();
    listItemBuilder.addItem(itemImage, 'listItemToken1', makePlainText('List Item 1'));
    listItemBuilder.addItem(itemImage, 'listItemToken2', makePlainText('List Item 2'));
    listItemBuilder.addItem(itemImage, 'listItemToken3', makePlainText('List Item 3'));
    listItemBuilder.addItem(itemImage, 'listItemToken4', makePlainText('List Item 4'));
    const listItems = listItemBuilder.build();
    const listTemplate = listTemplateBuilder.setToken('listToken')
    										.setTitle('listTemplate1')
    										.setListItems(listItems)
    										.build();
    this.response.speak('Rendering a list template!')
    			.renderTemplate(listTemplate);
    this.emit(':responseReady');
}
```

`Display.RenderTemplate`ディレクティブをEchoのようなディスプレイのないデバイスに送るとinvalid directive erroｒ が発生する。どのディレクティブをサポートするかはデバイスのsupportedInterfaces属性を確認すればよい。

```javascript
const handler = {
    'LaunchRequest' : function() {

    this.response.speak('Hello there');

    // Display.RenderTemplate directives can be added to the response
    if (this.event.context.System.device.supportedInterfaces.Display) {
        //... build mytemplate using TemplateBuilder
        this.response.renderTemplate(myTemplate);
    }

    this.emit(':responseReady');
    }
};
```

同様に動画は VideoAppがデバイスでサポートされているかどうかチェックすれば良い。

```javascript
const handler = {
    'PlayVideoIntent' : function() {

    // VideoApp.Play directives can be added to the response
    if (this.event.context.System.device.supportedInterfaces.VideoApp) {
        this.response.playVideo('http://path/to/my/video.mp4');
    } else {
        this.response.speak("The video cannot be played on your device. " +
        "To watch this video, try launching the skill from your echo show device.");
    }

        this.emit(':responseReady');
    }
};
```

`Display`インターフェースについてのドキュメントは[こちら](https://developer.amazon.com/docs/custom-skills/display-interface-reference.html)を参照のこと。

### Playback Controller Interface
`PlaybackController`インターフェースはユーザがデバイス上のボタンやリモコンを操作した際にリクエストを送信できるようにする。これらは「Alexa, 次の曲」のような音声コマンドとは異なる。`PlaybackController`リクエストを扱えるようにするために`PlaybackController`インターフェースをAlexa Node.js SDKを使って実装する必要がある。

```javascript
const handlers = {
    'NextCommandIssued' : function() {
        //Your skill can respond to NextCommandIssued with any AudioPlayer directive.
    },
    'PauseCommandIssued' : function() {
        //Your skill can respond to PauseCommandIssued with any AudioPlayer directive.
    },
    'PlayCommandIssued' : function() {
        //Your skill can respond to PlayCommandIssued with any AudioPlayer directive.
    },
    'PreviousCommandIssued' : function() {
        //Your skill can respond to PreviousCommandIssued with any AudioPlayer directive.
    },
    'System.ExceptionEncountered' : function() {
        //Your skill cannot return a response to System.ExceptionEncountered.
    }
};
```

`PlaybackController`インターフェースに関するドキュメントは[こちら](https://developer.amazon.com/docs/custom-skills/playback-controller-interface-reference.html)を参照のこと。

### VideoApp Interface
Echo showで動画を見るために`VideoApp.Launch`ディレクティブを送る。Alexa Node.js SDKでは`responseBuilder` でJSONレスポンスを生成できる。

次は動画を再生する例だ。

```javascript
//...
'LaunchRequest' : function() {
    const videoSource = 'https://url/to/videosource';
    const metadata = {
    	'title': 'Title for Sample Video',
    	'subtitle': 'Secondary Title for Sample Video'
    };
    this.response.playVideo(videoSource, metadata);
    this.emit(':responseReady');
}
```

`VideoApp`インターフェースのドキュメントは[こちら](https://developer.amazon.com/docs/custom-skills/videoapp-interface-reference.html)

### Skill and List Events
スキル開発者はAlexaスキルイベントを直接組み込むことができる。もし、スキルがイベントを購読するとイベント発生時に通知される。

スキルサービスでイベントを利用するためには、[Add Events to Your Skill With SMAPI](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/add-events-to-your-skill-with-smapi)にかかれているとおり、Alexa Skill Management API (SMAPI)へのアクセスを設定する必要がある。

Skill and List Events come out of session. Once your skill has been set up to receive these events. You can specify behaviour by adding the event names to your default event handler.

Skill and List Eventsはセッションを終了させる。一旦、スキルがイベントを受けるよう設定されると、デフォルトのイベントハンドラにイベント名を追加するとその振る舞いを定義できる。

```javascript
const handlers = {
    'AlexaSkillEvent.SkillEnabled' : function() {
        const userId = this.event.context.System.user.userId;
        console.log(`skill was enabled for user: ${userId}`);
    },
    'AlexaHouseholdListEvent.ItemsCreated' : function() {
        const listId = this.event.request.body.listId;
        const listItemIds = this.event.request.body.listItemIds;
        console.log(`The items: ${JSON.stringify(listItemIds)} were added to list ${listId}`);
    },
    'AlexaHouseholdListEvent.ListCreated' : function() {
        const listId = this.event.request.body.listId;
        console.log(`The new list: ${JSON.stringify(listId)} was created`);
    }
    //...
};

exports.handler = function(event, context, callback) {
    const alexa = Alexa.handler(event, context, callback);
    alexa.registerHandlers(handlers);
    alexa.execute();
};
```

[sample skill and walk-through](https://github.com/Alexa/alexa-cookbook/tree/master/context/skill-events)をスキルイベントの参考にしてほしい

## Services

### Device Address Service

Alexa NodeJS SDKは`DeviceAddressService`ヘルパークラスを提供している。これはユーザのデバイスの住所情報を取得するためのDevice Address APIユーティリティだ。現在、次のメソッドが用意されている[^1]。

```javascript
getFullAddress(deviceId, apiEndpoint, token)
getCountryAndPostalCode(deviceId, apiEndpoint, token)
```

[^1]: `getFullAddress(deviceId, apiEndpoint, token)`のレスポンスは次の通り。見ての通り,端末に登録されていない項目は`null`が返ってくるのでそのハンドリングも必要になる。サービスシミュレータではちゃんと動かないので注意。

  ```
  {
    "addressLine1": null,
    "addressLine2": null,
    "addressLine3": null,
    "districtOrCounty": null,
    "stateOrRegion": null,
    "city": null,
    "countryCode": "JP",
    "postalCode": "XXX-XXXX"
  }
  ```
`apiEndpoint` と `token`はリクエストの`this.event.context.System.apiEndpoint`と`this.event.context.System.user.permissions.consentToken`から取得できる。
`deviceId` はリクエストの`this.event.context.System.device.deviceId`から取得できる。

```javascript
const Alexa = require('alexa-sdk');

'DeviceAddressIntent': function () {
    if (this.event.context.System.user.permissions) {
        const token = this.event.context.System.user.permissions.consentToken;
        const apiEndpoint = this.event.context.System.apiEndpoint;
        const deviceId = this.event.context.System.device.deviceId;

        const das = new Alexa.services.DeviceAddressService();
        das.getFullAddress(deviceId, apiEndpoint, token)
        .then((data) => {
            this.response.speak('<address information>');
            console.log('Address get: ' + JSON.stringify(data));
            this.emit(':responseReady');
        })
        .catch((error) => {
            this.response.speak('I\'m sorry. Something went wrong.');
            this.emit(':responseReady');
            console.log(error.message);
        });
    } else {
        this.response.speak('Please grant skill permissions to access your device address.');
        this.emit(':responseReady');
    }
}
```


### List Management Service
Alexa ユーザは ２つのデフォルトリストが利用できる。Alexa ToDoリストと Alexa ショッピングリストだ。
加えてスキルからカスタムリストを作って管理できる。

Alexa NodeJS SDKは`ListManagementService`ヘルパクラスをデフォルトリストやカスタムリストを扱いやすくするために用意している。
現在、次のメソッドがある。

````javascript
getListsMetadata(token)
createList(listObject, token)
getList(listId, itemStatus, token)
updateList(listId, listObject, token)
deleteList(listId, token)
createListItem(listId, listItemObject, token)
getListItem(listId, itemId, token)
updateListItem(listId, itemId, listItemObject, token)
deleteListItem(listId, itemId, token)
````

`token`はリクエストの`this.event.context.System.user.permissions.consentToken`から取得できる
`listId`は`GetListsMetadata`を使って、`itemId`は`GetList`を使ってそれぞれ取得できる。

````javascript
const Alexa = require('alexa-sdk');

function getListsMetadata(token) {
    const lms = new Alexa.services.ListManagementService();
    lms.getListsMetadata(token)
    .then((data) => {
        console.log('List retrieved: ' + JSON.stringify(data));
        this.context.succeed();
    })
    .catch((error) => {
        console.log(error.message);
    });
};
````

### Directive Service

`enqueue(directive, endpoint, token)`

これはスキル実行中に非同期にAlexaデバイスにディレクティブを返す。現在、発話ディレクティブのみサポートしている。
SSML (MP3 audioを含む)とテキスト形式をサポートしている。スキルが有効な時にディレクティブは元のデバイスに返される。`apiEndpoint` と `token`パラメータはそれぞれリクエストの`this.event.context.System.apiEndpoint` と `this.event.context.System.apiAccessToken`から取得できる。

- レスポンスの発話は６００文字まで。
- SSML内の音声ファイルは３０秒まで
- ディレクティブの数は制限がない。必要なら、複数のリクエストを送信しても良い。
- ディレクティブサービスには重複処理が含まれていない。それで同じディレクティブを複数回受信する可能性があるため、リトライ処理はお勧めしない。

```javascript
const Alexa = require('alexa-sdk');

const handlers = {
    'SearchIntent' : function() {
        const requestId = this.event.request.requestId;
        const token = this.event.context.System.apiAccessToken;
        const endpoint = this.event.context.System.apiEndpoint;
        const ds = new Alexa.services.DirectiveService();

        const directive = new Alexa.directives.VoicePlayerSpeakDirective(requestId, "Please wait...");
        const progressiveResponse = ds.enqueue(directive, endpoint, token)
        .catch((err) => {
            // catch API errors so skill processing an continue
        });
        const serviceCall = callMyService();

        Promise.all([progressiveResponse, serviceCall])
        .then(() => {
            this.response.speak('I found the following results');
            this.emit(':responseReady');
        });
    }
};

```

## Extend Features

### Skill State Management
Alexa-sdk は受信したインテントを適切なハンドラに渡すためにステートマネージャを使う。セッション属性に文字列として保存された状態が、スキルの現在の状態を示す。組み込みのインテントルーティングでも、インテント名に状態文字列を付加することで同様の実装が可能だが、alexa-sdkのステートマネージャを使って実装することができる。

[highlowgame](https://github.com/alexa/skill-sample-nodejs-highlowgame/blob/master/lambda/custom/index.js)をは状態管理の例だ。このスキルではユーザが数を予想し、Alexaが大きいか小さいかや、ユーザが何回プレイしたかを答える。
'start' と 'guess'の２つの状態を持っている。

```javascript
const states = {
	GUESSMODE: '_GUESSMODE', // User is trying to guess the number.
	STARTMODE: '_STARTMODE' // Prompt the user to start or restart the game.
};
```
newSessionHandlersのNewSessionハンドラは入力インテントや起動リクエストのショートカットで、ルーティングを行っている。

```javascript
const newSessionHandlers = {
    'NewSession': function() {
        if(Object.keys(this.attributes).length === 0) { // Check if it's the first time the skill has been invoked
            this.attributes['endedSessionCount'] = 0;
            this.attributes['gamesPlayed'] = 0;
        }
        this.handler.state = states.STARTMODE;
        this.response.speak('Welcome to High Low guessing game. You have played '
                        + this.attributes['gamesPlayed'].toString() + ' times. Would you like to play?')
                    .listen('Say yes to start the game or no to quit.');
        this.emit(':responseReady');
    }
};
```

新規セッションの場合、スキルの状態が`this.handler.state`によって、簡単に`STARTMODE`に設定されていることに注意。スキルの状態は自動的にセッション属性として保存される。もし、DynamoDBを使えばセッションを超えて保存することもできる。

`NewSession`はキャッチオールな振る舞いで、よいエントリーポイントだが、必須ではないことに注意しよう。`NewSession`はその名前を持つハンドラが定義されている場合にのみ呼び出される。それぞれの状態はもし、組み込みの永続化を利用するなら呼び出されるそれぞれの`NewSession`ハンドラを定義することができる。上の例では異なる`NewSession`の振る舞いを`states.STARTMODE`と`states.GUESSMODE`に実装でき、柔軟性を得ることができる。

スキルのさまざまな状態に対応するインテントを定義するには、 `Alexa.CreateStateHandler`関数を使う。
スキルが特定の状態にあるときだけ動くインテントハンドラが定義でき、これは大きな柔軟性を与える。

例えば、上記で定義した`GUESSMODE`状態では、質問に回答するユーザを扱う。次の様にStateHandlersを使う。

```javascript
const guessModeHandlers = Alexa.CreateStateHandler(states.GUESSMODE, {

'NewSession': function () {
    this.handler.state = '';
    this.emitWithState('NewSession'); // Equivalent to the Start Mode NewSession handler
},

'NumberGuessIntent': function() {
    const guessNum = parseInt(this.event.request.intent.slots.number.value);
    const targetNum = this.attributes['guessNumber'];

    console.log('user guessed: ' + guessNum);

    if(guessNum > targetNum){
        this.emit('TooHigh', guessNum);
    } else if( guessNum < targetNum){
        this.emit('TooLow', guessNum);
    } else if (guessNum === targetNum){
        // With a callback, use the arrow function to preserve the correct 'this' context
        this.emit('JustRight', () => {
            this.response.speak(guessNum.toString() + 'is correct! Would you like to play a new game?')
                        .listen('Say yes to start a new game, or no to end the game.');
            this.emit(':responseReady');
        });
    } else {
        this.emit('NotANum');
    }
},

'AMAZON.HelpIntent': function() {
    this.response.speak('I am thinking of a number between zero and one hundred, try to guess and I will tell you' +
    ' if it is higher or lower.')
                .listen('Try saying a number.');
    this.emit(':responseReady');
},

'SessionEndedRequest': function () {
    console.log('session ended!');
    this.attributes['endedSessionCount'] += 1;
    this.emit(':saveState', true); // Be sure to call :saveState to persist your session attributes in DynamoDB
},

'Unhandled': function() {
    this.response.speak('Sorry, I didn\'t get that. Try saying a number.')
                .listen('Try saying a number.');
    this.emit(':responseReady');
}
});
```

一方、`STARTMODE`の`StateHandlers`を次の様に定義する。

```javascript
const startGameHandlers = Alexa.CreateStateHandler(states.STARTMODE, {

    'NewSession': function () {
        this.emit('NewSession'); // Uses the handler in newSessionHandlers
    },

    'AMAZON.HelpIntent': function() {
        const message = 'I will think of a number between zero and one hundred, try to guess and I will tell you if it' +
        ' is higher or lower. Do you want to start the game?';
        this.response.speak(message)
                    .listen(message);
        this.emit(':responseReady');
    },

    'AMAZON.YesIntent': function() {
        this.attributes['guessNumber'] = Math.floor(Math.random() * 100);
        this.handler.state = states.GUESSMODE;
        this.response.speak('Great! ' + 'Try saying a number to start the game.')
                    .listen('Try saying a number.');
        this.emit(':responseReady');
    },

    'AMAZON.NoIntent': function() {
        this.response.speak('Ok, see you next time!');
        this.emit(':responseReady');
    },

    'SessionEndedRequest': function () {
        console.log('session ended!');
        this.attributes['endedSessionCount'] += 1;
        this.emit(':saveState', true);
    },

    'Unhandled': function() {
        const message = 'Say yes to continue, or no to end the game.';
        this.response.speak(message)
                    .listen(message);
        this.emit(':responseReady');
    }
});
```

`AMAZON.YesIntent`と`AMAZON.NoIntent`は`guessModeHandlers` では定義されていない。それは、この状態では'yes' や 'no'の返答には反応しなくていいからだ。それらのインテントは`Unhandled`ハンドラで受けられる。

また、`NewSession` と `Unhandled`ハンドラの振る舞いがこのゲームの両方の状態で異なっているのにも気付いたか？
このゲームでは、 `newSessionHandlers`オブジェクトで定義された` NewSession`ハンドラを呼び出して状態をリセットする。 それを定義することなく、alexa-sdkは現在の状態のインテントハンドラを呼び出す。
`alexa.execute()`を呼ぶ前に各状態のハンドラを登録するを忘れないように。見つからないよ。

セッションを終了するとき、属性は自動的に保存される。しかし、ユーザがセッションを終了するなら、強制的に保存するように`:saveState`イベントを発行する(`this.emit(':saveState', true)`)必要がある。ユーザによるセッション「終了」やタイムアウト時に呼ばれる`SessionEndedRequest`ハンドラでそれを実装する。上の例を確認してほしい。

もし明確に状態をリセットするなら、次のコードでできる。

```javascript
this.handler.state = '' // delete this.handler.state は参照エラーの原因になる。
delete this.attributes['STATE'];
```

### Persisting Skill Attributes through DynamoDB
後で使うためにセッション属性値をストレージに保存したいと思うことも多い。Alexa-sdk では [Amazon DynamoDB](https://aws.amazon.com/dynamodb/)(NoSQL データベース・サービス)に１行のコードで保存できるように実装されている。

alexa.executeを実行する前に、DynamoDBのテーブル名をalexaオブジェクトにセットするだけだ。

```javascript
exports.handler = function (event, context, callback) {
    const alexa = Alexa.handler(event, context, callback);
    alexa.appId = appId;
    alexa.dynamoDBTableName = 'YourTableName'; // That's it!
    alexa.registerHandlers(State1Handlers, State2Handlers);
    alexa.execute();
};
```
そして、後必要なのは、alexaオブジェクトの属性プロパティに値をセットすることだけだ。`put`と`get`にも分かれていない。

```javascript
this.attributes['yourAttribute'] = 'value';
```

あらかじめ[マニュアルでテーブルを作る](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/SampleData.CreateTables.html) ことができる。あるいは Lambda 関数に DynamoDBの [テーブル作成権限](http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_CreateTable.html)を与えておいても良い。その場合、自動的に作成される。最初の呼び出し時にテーブルの作成に１分かそこらかかることを忘れないように。もし、テーブルをマニュアルで作るなら、プライマリキーは文字列で"userId"をしておくこと。

注意：lambdaでスキルをホストして、スキルの属性をDynamoDBに保存するなら、lambdaの実行ロールがDynamoDBへのアクセス権を持っていることを確認すること。

### Adding Multi-Language Support for Skill

Hello World のサンプルを示す。すべてのユーザに示す言語の文字列を次のフォーマットで定義する。

```javascript
const languageStrings = {
    'en-GB': {
        'translation': {
            'SAY_HELLO_MESSAGE' : 'Hello World!'
        }
    },
    'en-US': {
        'translation': {
            'SAY_HELLO_MESSAGE' : 'Hello World!'
        }
    },
    'de-DE': {
        'translation': {
            'SAY_HELLO_MESSAGE' : 'Hallo Welt!'
        }
    }
};
```

Alexa-sdkで国際化機能を有効にするために、上で作ったオブジェクトをリソースにセットすること。

```javascript
exports.handler = function(event, context, callback) {
    const alexa = Alexa.handler(event, context);
    alexa.appId = appId;
    // To enable string internationalization (i18n) features, set a resources object.
    alexa.resources = languageStrings;
    alexa.registerHandlers(handlers);
    alexa.execute();
};
```

一旦、各言語の文字列を定義して有効にすると、`this.t()`関数でそれらの文字列にアクセスできる。受け付けたリクエストのロケールにあった言語で文字列が取得できる。

```javascript
const handlers = {
    'LaunchRequest': function () {
        this.emit('SayHello');
    },
    'HelloWorldIntent': function () {
        this.emit('SayHello');
    },
    'SayHello': function () {
        this.response.speak(this.t('SAY_HELLO_MESSAGE'));
        this.emit(':responseReady');
    }
};
```
マルチリンガルなスキルの開発とデプロイについて[こちら](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/developing-skills-in-multiple-languages)を参照のこと。

### Device ID Support
ユーザがAlexaスキルを有効にすると、スキルはユーザに許可を求めることができる。許可されれば、Alexaデバイスに紐付いたアドレス情報が得られる。
このアドレス情報をスキルの機能やユーザ体験の向上に使うことができる。

`deviceId`は各リクエストのcontextオブジェクトに含まれ、`this.event.context.System.device.deviceId`でインテントハンドラからアクセス可能。スキル内でどうやってdeviceIdとAddress APIを使うかは[Address API sample skill](https://github.com/alexa/skill-sample-node-device-address-api)を参照。

### Speechcons (Interjections)
[Speechcons](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/speechcon-reference)はAlexaの発生をより自然にするための単語やフレーズだ。それを使うためにSSMLマークアップをテキストに含めて発行すれば良い。

* `this.emit(':tell', 'Sometimes when I look at the Alexa skills you have all taught me, I just have to say, <say-as interpret-as="interjection">Bazinga.</say-as> ');`
* `this.emit(':tell', '<say-as interpret-as="interjection">Oh boy</say-as><break time="1s"/> this is just an example.');`

_Speechcons(感嘆符)は英語（US/UK/India）とドイツ語でサポートされている_

## Setting up your development environment

Alexa Skills Kitを始めるためのさらなる情報は 次の資料を参照のこと

- [Alexa Dev Chat Podcast](http://bit.ly/alexadevchat)
- [Alexa Training with Big Nerd Ranch](https://developer.amazon.com/public/community/blog/tag/Big+Nerd+Ranch)
- [Alexa Skills Kit (ASK)](https://developer.amazon.com/ask)
- [Alexa Developer Forums](https://forums.developer.amazon.com/forums/category.jspa?categoryID=48)
- [Training for the Alexa Skills Kit](https://developer.amazon.com/alexa-skills-kit/alexa-skills-developer-training)