AlexaSkillsKitでセッション情報を持たせる。

# 会話の流れ
user:  「Alexa, アトリビュートを開いて」
Alexa: 「星座を三回聞きます。適当な星座を言ってください。」
user:  「いて座」
Alexa: 「いて座ですね。二つ目をおっしゃってください。」
user:  「うお座」
Alexa: 「いて座とうお座です。あと一つおっしゃってください。」
user:  「かに座」
Alexa: 「いて座とうお座とかに座ですね。終了します。」

# セッション情報の持たせ方
```js
this.attributes['attri1'] = attri1
```
こんな感じで持たせられる。
次以降の会話では、

```js
var attri1 = this.attributes['attri1']
```
みたいな感じで参照できる。

# 同じインテントを区別しなきゃいけない問題
上の会話の例だと、ユーザーの発言はすべて星座1単語で構成されて、同じ`HoroscopeIntent`というインテントである。
同じインテントなのに、なぜ段階によってAlexaは違う返しができているのかというと**ステート**という概念を取り入れているからである。
ちゃんとした会話が成り立ったら、次のステージへ移る的な概念。
そう。これがないとAlexaは最初の発言の「いて座ですね。二つ目をおっしゃってください。」を繰り返すばかりだ。

# Lambda
```js
"use strict";
const Alexa = require('alexa-sdk');

// ステートの定義
const states = {
  FIRSTSTATE: '_FIRSTSTATE',
  SECONDSTATE: '_SECONDSTATE'
};

exports.handler = function(event, context, callback) {
  var alexa = Alexa.handler(event, context);
  // alexa.appId = process.env.APP_ID;
  alexa.registerHandlers(handlers, firstHandlers, secondHandlers); // 既存のハンドラに加えてステートハンドラ(後半で定義)も登録
  
  alexa.execute();
};
var handlers = {
  'LaunchRequest': function () {
    this.emit('AMAZON.HelpIntent');
  },
  'AMAZON.HelpIntent': function () {
    this.emit(':ask', '星座を三回聞きます。適当な星座を言ってください。');
  },
  'AMAZON.StopIntent': function(){
      this.emit(':tell', 'ありがとうございました。');
  },
  'HoroscopeIntent': function () {
    var attri1 = this.event.request.intent.slots.StarSign.value; //ユーザが発言発言した内容を取得。
    this.handler.state = states.FIRSTSTATE; // ステートをセット
    this.attributes['attri1'] = attri1; // 最初の星座をアトリビュートに保存
    var message = attri1 + 'ですね。二つ目をおっしゃってください。';
    this.emit(':ask', message); //2つ目を聞く
    console.log(message);
  }
};
// ステートハンドラの定義
var firstHandlers = Alexa.CreateStateHandler(states.FIRSTSTATE, {
  'HoroscopeIntent': function() {
    this.handler.state = states.SECONDSTATE; //２つ目の星座を扱うためにステートをチェンジ
    var attri1 = this.attributes['attri1']; // アトリビュートattri1を参照
    var attri2 = this.event.request.intent.slots.StarSign.value; //ユーザーが発言した2個目の星座を取得して格納
    this.attributes['attri2'] = attri2;
    var message = attri1 + 'と' + attri2 + 'です。あと一つおっしゃってください。'
    this.emit(':ask', message); //3つ目を聞く
    console.log(message);
  },
  //ユーザーがちゃんと星座を言ってくれなかった時
  'Unhandled': function() {
    var reprompt = '二つ目をおっしゃってください。';
    this.emit(':ask', reprompt, reprompt);
  }
});

var secondHandlers = Alexa.CreateStateHandler(states.SECONDSTATE,{
    'HoroscopeIntent': function(){
        this.handler.state = ''; //ステートを初期状態に戻す
        this.attributes['STATE'] = undefined;
        var attri1 = this.attributes['attri1'];
        var attri2 = this.attributes['attri2'];
        var attri3 = this.event.request.intent.slots.StarSign.value; //三つ目の星座を取得
        var message = attri1 + 'と' + attri2 + 'と' + attri3 + 'ですね。終了します。';
        this.emit(':tell', message);
    },
    'Unhandled': function(){
    var reprompt = '三つ目をおっしゃってください。';
    this.emit(':ask', reprompt, reprompt);
    }
})
```

## 上から説明
```JS:ステートの定義
const states = {
  FIRSTSTATE: '_FIRSTSTATE',
  SECONDSTATE: '_SECONDSTATE'
};
```
使用するステートを定義している。

```JS
alexa.registerHandlers(handlers, firstHandlers, secondHandlers);
```
使用するハンドラーを引数に渡している。
順番はどうでもいいみたい。
ハンドラー自体はこの下で定義している。

```JS
var handlers = {
(...省略...)
  'HoroscopeIntent': function () {
    var attri1 = this.event.request.intent.slots.StarSign.value; //ユーザが発言発言した内容を取得。
    this.handler.state = states.FIRSTSTATE; // ステートをセット
    this.attributes['attri1'] = attri1; // 最初の星座をアトリビュートに保存
    var message = attri1 + 'ですね。二つ目をおっしゃってください。';
    this.emit(':ask', message); //2つ目を聞く
    console.log(message);
  }
};
```
5行目の`this.handler.state = states.FIRSTSTATE;`でステートをセットしている。
6行目の`this.attributes['attri1'] = attri1;`でユーザーが最初に発言した星座をこの後の対話でも残せるように保存している。
実際にレスポンスで帰ってくるJSONは
```
"attributes": {
  "attri1": "いて座",
  "STATE": "_FIRSTSTATE"
},
```
こんな感じ。もちろん他のたくさんの情報はあるけど。
8行目の`this.emit(':ask', message)`は`tell`じゃなくて`ask`だとAlexaがもう一度聞き取り体制に入る。
つまり、「いて座ですね。二つ目をおっしゃってください」と発言してから、そこで会話は終わらないで、もう一回会話に入る。

```js:ステートハンドラの定義
var firstHandlers = Alexa.CreateStateHandler(states.FIRSTSTATE, {
  'HoroscopeIntent': function() {
    this.handler.state = states.SECONDSTATE; //２つ目の星座を扱うためにステートをチェンジ
    var attri1 = this.attributes['attri1']; // アトリビュートattri1を参照
    var attri2 = this.event.request.intent.slots.StarSign.value; //ユーザーが発言した2個目の星座を取得して格納
    this.attributes['attri2'] = attri2;
    var message = attri1 + 'と' + attri2 + 'です。あと一つおっしゃってください。'
    this.emit(':ask', message); //3つ目を聞く
    console.log(message);
  },
  //ユーザーがちゃんと星座を言ってくれなかった時
  'Unhandled': function() {
    var reprompt = '二つ目をおっしゃってください。';
    this.emit(':ask', reprompt, reprompt);
  }
});
```
1行目の`CreateStateHandler`関数でハンドラーを定義している。
第一引数に`states.FIRSTSTATE`を指定している。先ほどの`this.handler.state = states.FIRSTSTATE`でステートを定義しているので次のハンドラーはこれに結び付くとされる。
ほかはまあ、上と同じ。

```js:二つ目のハンドラー定義
var secondHandlers = Alexa.CreateStateHandler(states.SECONDSTATE,{
    'HoroscopeIntent': function(){
        this.handler.state = ''; //ステートを初期状態に戻す
        this.attributes['STATE'] = undefined;
        var attri1 = this.attributes['attri1'];
        var attri2 = this.attributes['attri2'];
        var attri3 = this.event.request.intent.slots.StarSign.value; //三つ目の星座を取得
        var message = attri1 + 'と' + attri2 + 'と' + attri3 + 'ですね。終了します。';
        this.emit(':tell', message);
    },
    'Unhandled': function(){
    var reprompt = '三つ目をおっしゃってください。';
    this.emit(':ask', reprompt, reprompt);
    }
})
```
これもほぼ最初のハンドラー定義と同じだが、3行めでステートを初期化している。出ないと二つ目のステートが永遠と続いてしまう。

## おわりに
こんなくだらないスキルでも申請したら通るのか試してみたいと思います。

# 参照
https://developer.amazon.com/ja/blogs/alexa/post/cddf2434-42ee-422f-886d-5be9c36dba14/jp-training-chapter3