Alexa ディスプレイ端末対応時にセッションをクローズする時の注意点

さぁEcho Spotも日本で発売されて、更に盛り上がってきたAlexa界隈！(個人の感じ方によります。)
直近で、ハマって一度リジェクトされたので、備忘録的に残しておきます。
※ask-sdk v2 for node.jsを利用しています

#セッションの継続判定
```index.js
const hogehogeIntentRequest = {
	canHandle(handlerInput) {
		const request = handlerInput.requestEnvelope.request;
		return (request.type === 'IntentRequest' && request.intent.name === 'AMAZON.HogeHoge');
	},
	handle(handlerInput) {
		return handlerInput.responseBuilder
			.speak('ほげほげ')					//インテントが呼び出された直後に発話される内容
			.reprompt('何か問いかけてみてください')	//ユーザーからの反応がない場合に発話される内容
			.getResponse();
	}
}
```
一般的な、Handlerの定義だと上記の様になると思います。
Amazon EchoやEcho dotなどのディスプレイが無いデバイスの場合、**reprompt**の設定の有無によってセッションの継続判定フラグである**ShouldEndSession**の値が自動的に判定されていました。

* reprompt有りの場合...セッションが継続される (ShouldEndSessionがfalseの扱い)
* repromptなしの場合...セッションが終了される (ShouldEndSessionがtrueの扱い)

#ディスプレイ付き端末で、テンプレートを表示した場合
[公式のテンプレートリファレンスはこちらから](https://developer.amazon.com/ja/docs/custom-skills/display-interface-reference.html#use-of-shouldendsession-attribute-and-session-timeouts)

**shouldEndSessionアトリビュートおよびセッションタイムアウトの使用**に記載されていますが、ディスプレイ付きのEcho端末向けにテンプレートを表示し、画面上で表示したテンプレートがアクティブな場合、セッションは開かれたままになります。

要は、**shouldEndSession**の動作を明確にするためにセッションを終了させる場合は**true**、セッションを継続させる場合は、**false**を指定してあげる必要があります。

#shouldEndSessionに値を設定する
ここでまたask-sdkv2の罠が...
**this.response.shouldEndSession**から**handlerInput.responseBuilder.withShouldEndSession**での設定に地味に変わっています。

```index.js:alexa-sdk_v1
this.response.shouldEndSession(bool);
```

```index.js:ask-sdk_v2
return handlerInput.responseBuilder
            .speak('hogehoge')
            .withShouldEndSession(bool)
            .getResponse();
```

まぁここまでは、ドキュメントそのままやん。ワロスワロス。
ドキュメント読めやというマサカリ飛んできそうですが...

#StopIntent , CancelIntent時にはホーム画面へすぐに遷移させる必要がある
この条件がクリア出来ない場合は、リジェクト対象になります。
今回リジェクトの理由としては、以前からStopIntentとCancelIntent時に挨拶的な返答を用意していたため、それを画面上でも表示しようとしたことが原因だと思われます。

```EndIntentRequest.js
const EndIntentRequest = {
	canHandle(handlerInput) {
		const request = handlerInput.requestEnvelope.request;
		return (request.type === 'IntentRequest' && request.intent.name === 'AMAZON.HogeHoge');
	},
	handle(handlerInput) {
		const response.responseBuilder;
		response.speak('ばいばいー');
		response.withShouldEndSession(true);	//意図的に終了させる

		//ディスプレイ端末の判定フラグを取得
		const displayFlg = util.displayDeviceCheck(handlerInput);
		const iconPath = 'https://hogehogehoge/hoge.png'
		
		//ディスプレイ端末である場合は、テンプレートを付与します	
		if (displayFlg) {
			const iconImg = new alexa.ImageHelper()
				.addImageInstance(displayIconPath)
				.getImage();
			response.addRenderTemplateDirective({
				type: 'BodyTemplate7',
				backButton: 'VISIBLE',
				title: 'また来てね！',
				image: iconImg,
				token: 'TOKEN'
			})
		}
		return response.getResponse();
	}
}
```

上記のようにStop,Cancelインテント時に呼び出されるHandlerがあったとします。
終了時にディスプレイ付き端末の場合、何らかの画像と「また来てね！」という表示をさせるような内容になっています。
Stop,Cancelインテント用のHandlerで、この後にセッションが継続されることはないため、**ShouldEndSession**の値は明示的に**true**とし、セッションを終了させるようにしています。

この場合、当初意図した動きとしては、読み上げ中のみテンプレートが表示され、読み上げ完了後にテンプレートが**すぐに**閉じられるものだと考えていました。

実際は、テンプレートが端末の画面上でアクティブであるため、**ShouldEndSession**をtrueにして明示的にセッションを終了させようとしていても、**セッション継続扱い**とされてしまい、30秒間テンプレートが画面上に表示され続けてしまいます。

このことから、下記の特定の条件の場合、ストア申請時にリジェクトを受ける場合があります。

* ディスプレイ付きのEcho端末向けのSkillで、Stop,CancelIntentHandlerの**ShouldEndSession**に明示的にtrueを渡していない。
    * やり取りの中で、テンプレートが1度でも表示されるSkillの場合は、セッション終了にまつわるHandler全てで、 ShouldEndSessionにtrueを渡す必要があります。
* Stop,CancelIntentHandlerで、テンプレートを表示するSkill
    * Stop,CancelIntentはすぐにSkillを閉じる必要があるため、テンプレートを表示すること出来ないようです。

結局の所、Stop,CancelIntent時は、何もせず**ShouldEndSession**にtrue渡すことを忘れないようにすることが大切そうです。

#最後に
Alexa開発者になりませんか？
今ならSkill公開するだけで、Tシャツもらえます。
そして30日間のユニークユーザーが100人超えただけで、Echo Dotがもらえるこの太っ腹！
Alexaの開発者や勉強会が少ないので、ぜひ増えてほしいこの頃です。

[Eventページはこちらから](https://developer.amazon.com/ja/alexa-skills-kit/alexa-developer-skill-promotion)