Alexa(アレクサ)に駅しりとりスキルをローンチするまで

[ヴァル研究所 Advent Calendar 2017](https://qiita.com/advent-calendar/2017/val)の23日目はアマゾンのAlexaに関する記事です。

今回はスキルの審査を通過するためのノウハウなどをまとめました。

【追記】
2018/1/18に発表された**「Amazon Alexa スキル」 2017年12月の人気ランキング**で、なんと「駅しりとり powered by 駅すぱあと」が**<font color=red>5位</font>**となりました。
多くの方に遊んでいただけたようで、とても嬉しいです。
この記事ではリリースまでに行った対策などもまとめてありますので、参考になればと思います。
https://prtimes.jp/main/html/rd/p/000000781.000004612.html

# Alexaって何？

[**Alexa**(アレクサ)](https://www.amazon.co.jp/b?node=5262653051)はAmazonが開発した**AIアシスタント**です。
ユーザーはAlexaに話しかけるだけで、さまざまな情報を音声で案内してくれます。

また、デバイスは[**Amazon Echo**(アマゾンエコー)](https://www.amazon.co.jp/dp/B07456NLT6/)が有名ですね。
Amazon Echoは**スマートスピーカー**(AIスピーカー)です。

Alexaがスマートスピーカーのことだと勘違いしている人も多いと思いますが、**AlexaはAmazon Echo以外のデバイスでも利用可能**です。
※ただし、デバイスがAlexaに対応している必要があります。

## スキルって何？

Alexaで利用できる**さまざまな機能**のことです。
音楽の再生やニュース、天気予報、交通情報の読み上げなどもあります。

# 駅しりとりって何？

**駅すぱあと**に収録している9000を超える**駅名**をつなげていく言葉遊びゲームです。
Alexaと交互に駅名を言いあって、より長く続けていくことが目的です。

しかも、<font color=red>**無料**</font>で<font color=red>**広告もありません**</font>。気軽に遊べます。

[**「駅しりとり powered by 駅すぱあと」**の紹介ページ](https://ad-info.val.jp/lp/alexa/ekispert_shiritori/)

## 駅しりとりはAlexaのローンチスキルとしてリリースしました

ご縁があってローンチスキルを開発することになり、**駅すぱあとを使ったゲームは何ができるか？**ということで駅名を使ったしりとりゲームを考えました。

実は、ずいぶん昔にFlashゲームとしてリリースしたことがあり、提案資料でもこのゲームを見せてました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/153241/9614f5ea-37d3-2b0f-3582-34804843c617.png)

しかも、このゲーム、今でも遊べます(もちろん無料)。
~~(ただ、ブラウザによってはFlashが動かないので、見れなかった方、ごめんなさい)~~

https://ekiworld.net/service/sier/webservice/free_provision.html

そんなこんなで、トントン拍子で進んでローンチスキルとなりました。
[サードパーティのAlexa対応サービスデモ](https://youtu.be/x3Wbv5Dovbw?t=1m43s)でも紹介されましたので、知っている方もいらっしゃるのではないでしょうか。

ちなみに、12月11日の時点でしりとり的なゲームは**駅しりとりが唯一**です。
知育ゲームとして、パーティーゲームとして、鉄分多めのストイックなゲームとして遊んでいただけたらと思います。

# スキル開発で困ったこと

## 日本語ドキュメントが少ない

これはローンチとしてはあるあるですよね。
ここが一番苦労した点です。

ただ、その状態でも[GitHubのサンプル](https://github.com/alexa/)は大変参考になりました。

コードって開発者にとって共通言語みたいで、大変わかりやすかったです。

## ステータス管理する方法がわからない

基本的にはLambdaとDynamoDBは親和性高いので、データの保存はDynamoDBでOKです。
ただ、しりとりって、**「今日はここまで、続きは明日」**ということは無いじゃないですか。
言葉が出てこなかったら、その場で終わりです。
そういう一時的な状態はattributesに登録しておけば問題ありません。

```js
Object.assign(this.attributes, {
    "status": "start"
});
```

取り出すときも、こんな感じで変数に入れます。

```js
var status = this.attributes.status ? this.attributes.status : "";
```

これで、DynamoDBのキャパシティを消費しないし、レスポンスも早くなるし、一石二鳥です。

## セッションを開きっぱなしと閉じる動作の違いがよくわからない

Alexaが発話して終わる場合、例えば、「*あなたの負けです。次は頑張りましょう*」みたいなユーザーの返答を求めない場合は**tell**を使います。

```js
this.emit(':tell', 'ずっと俺のターン');
```

この場合、「*ずっと俺のターン*」とAlexaが発話した後、Alexaは会話をやめます。
ユーザーが再度会話を始めたい場合「*アレクサ、とっくにライフは0よ*」など、ウェイクワードを入れる必要があります。

※ウェイクワードはAlexaとの会話を始めるキーワードです。デフォルトは「*アレクサ*」が設定されています。

逆に、ユーザーの返答を求める場合、例えば「*ゲームやりますか？どうしますか？*」みたいな場合は**ask**を使います。

```js
this.emit(':ask', 'しりとりゲームやりますか？', '『はい』か『イエス』で答えてください');
```

この場合、「*しりとりゲームやりますか？*」とAlexaが発話した後、ユーザーに選択権が与えられ、ウェイクワードなしに「*はい*」と答えることができます。
また、ユーザーの応答がない場合、「*『はい』か『イエス』で答えてください*」とさらにAlexaが発話するので、ユーザーが応答します。

セッションが開いている場合、Amazon Echoだと**ライトリングが光りますので**、見た目で判断可能です。

※プログラムは一例です

## 変数を保持した状態でDynamoDBにデータを保存する方法がわからない

DynamoDBへの保存は、**アロー関数**で簡単に実装できます。
また、セッション変数なども引き継げます。

```js
var handlers = {
    "AMAZON.StopIntent": function () {
        putDynamoDB(this.event.session.sessionId, this.event.session.user.userId).then(() => {
            this.emit(':tell', 'ミッションコンプリート');
        });
    }
};
```

また、**Promise**を使ってDynamoDBへ保存するコードも書きます。
ちなみに、DynamoDBへのアクセス部分は**dynamodb-doc**を使えば簡単に実装できます。

```js
var doc = require('dynamodb-doc');
var dynamodb = new doc.DynamoDB();

function putDynamoDB(sessionId, userId) {
    return new Promise((resolve, reject) => {
        let log_params = {
            TableName: 'user_log_table'
        };
        log_params.Item = {};
        log_params.Item.sessionId = sessionId;
        log_params.Item.userId = userId;
        dynamodb.putItem(log_params, function (err, data) {
            if (err) {
                reject();
            } else {
                resolve();
            }
        });
    });
}
```

## カードを送る方法がわからない

カードを送る場合は**tellWithCard**を使います。
**発話内容**、**カードのタイトル**、**本文**を渡せば大丈夫です。

```js
this.emit(':tellWithCard', 'お代官様、これを', '山吹色のお菓子', 'ワイロ');
```

この場合、Alexaは「*お代官様、これを*」と言いつつ、タイトルが**山吹色のお菓子**の**ワイロ**という内容のカードをユーザーに送ります。

カードは**Alexaアプリ**等で確認ができます。

# スキルが認証されるためのコツ

**アマゾンの審査怖い**と思っている方、駅しりとりのノウハウを教えちゃいます。

## まずはバグを出さない

Lambdaから正しく応答を返さないと**エラーとなって会話が途切れます**ので、これは絶対です。

駅しりとりでは一切しりとりをせずに「*ギブアップ*」と言うと0回カウントで**undefinedエラー**となり落ちてしまう現象がありました。
しかも、「*始める場合はスタート、使い方を知りたい場合はヘルプと言ってね*」と質問しているにもかかわらず、ユーザーが無視して「*ギブアップ*」と言うことは想定していませんでした。

必ず、サンプル発話に登録したフレーズはどれが来ても良いようにしましょう。

## サンプル発話には考えられるすべてのパターンを登録しよう

例えばしりとりをやめる「*ギブアップ*」という言葉、駅しりとりでは下記のように様々なパターンを用意しています。
やはり個人差で言い違いがあるので、できる限りスキル側で吸収するようにしましょう。

* ストップ
* ギブアップ
* 降参
* 降参する
* 降参するよ
* 降参した
* 降参だ
* 降参だよ
* 降参だね
* 降参です
* 降参ですね
* 降参ですよ
* 降参します
* 負けた
* 負けだ
* 負けです
* 負けですね
* 負けですよ
* 負けました
* 諦める
* 諦めた
* 諦めます
* 思いつかない
* 思いつかないよ
* 思いつかないです
* 思いつきません
* 思い浮かばない
* 思い浮かばないよ
* 思い浮かばないです
* 思い浮かびません
* 知りません
* 知らない
* 知らないよ
* わかりません
* わからない
* わからないよ
* もういい
* もういいよ
* もういいです
* もうやめ
* もうやめて
* もうやめる
* もうやめます
* ダメだ
* ダメだよ
* ダメです
* もうダメだ
* もうダメだよ
* もうダメです
* しりとりに負けた
* しりとりに負けです
* しりとりに負けました
* しりとりを諦める
* しりとりを諦めた
* しりとりを諦めます
* 駅が思いつかない
* 駅が思いつかないよ
* 駅が思いつきません
* 駅が思い浮かばない
* 駅が思い浮かばないよ
* 駅が思い浮かびません
* 駅名が思いつかない
* 駅名が思いつかないよ
* 駅名が思いつきません
* 駅名が思い浮かばない
* 駅名が思い浮かばないよ
* 駅名が思い浮かびません
* 駅がわからない
* 駅がわからないよ
* 駅がわかりません
* 駅名がわからない
* 駅名がわからないよ
* 駅名がわかりません
* しりとりはもういい
* しりとりはもういいよ
* しりとりはもういいです
* しりとりをやめ
* しりとりをやめて
* しりとりをやめる
* しりとりをやめます

## スキルを開始したら、メインの動作までスムーズに移行するようにしよう

最初は「*駅しりとりです。始める場合はスタートと言ってください*」というように実装していました。
しかし、**しりとりを始めるためにわざわざスタートっていうのは面倒**という意見もあり、「*駅しりとりです。早速始めます。東京*」みたいにスキルを開いてからすぐにしりとりを始めるようにしました。

## 単に自己満足だとすぐ辞めちゃう

みんなでワイワイ楽しめれば大丈夫だと思っていたのですが、やはり何かの目標がないとすぐ飽きてしまう可能性も高いです。
そのため、急遽ランキング機能を作りました。

ちなみに、**猛者たちのランキング**です。

|順位|続いた駅数|
|:-:|:-:|
|<font color=red>**1位**</font>|<font color=red>**120駅**</font>|
|2位|110駅|
|3位|108駅|
|4位|106駅|
|5位|104駅|

もう、みんなすごすぎ！
※2017年12月21日現在

## できる限り会話を続くようにしよう

先ほどのランキングも、最初は順位を言って終わりにしてました。
ただ、それだと「*ふ～～～ん*」で終わってしまい、会話が途切れます。

そのため、「*現在のランキングです。あなたも挑戦しますか？*」みたいに会話を続くように変更しました。
これであれば、ウェイクワードなしの「*はい*」でしりとりが始められます。

# 駅しりとりで~~もてあそぶ~~遊ぶポイント

ここからは、読んでくれた方にこっそりと教える駅しりとりを遊ぶコツです。

## 駅名を調べるチート術

まずは[駅すぱあと for web](https://roote.ekispert.net/ja/)を開きましょう。
そして、おもむろに**Alexaの言った駅名の最後の文字を平仮名で入れます**。
候補が絞り込めない場合は**さらに何文字か入れます**。
そして、カーソルを合わせると・・・

![image.png](https://qiita-image-store.s3.amazonaws.com/0/153241/73d12923-7991-b422-5575-04f3af1e6809.png)

なんと、**駅名の読み方**がツールチップに表示されているではありませんか！
これで、あなたはもう負けなしです。

## 遊んでる人を負かすコツ

ほかの人もプレイさせて、どれだけ無残に負けるか、見てみたくなるじゃないですか。
そこで、遊んでいる人に悪魔のささやきをやってみましょう。

さて、とある日、ヴァルの女性社員に駅しりとりをやってもらいました。

:speaker:アレクサ「*下井草*」(しもいぐさ)
:woman:女性社員「*さいたま*」
:speaker:アレクサ「*そんな駅名ありません*」
:imp:悪魔のささやき「*さいたまじゃなくて、<font color=Red>さいたま新都心</font>だよ*」(さいたましんとしん)
:woman:女性社員「*さいたま新都心*」
:speaker:アレクサ「*ん、がつきましたね。あなたの負けです*」
:woman:女性社員「:scream:」

<font color=Red>イヤッッホォォォオオォオウ！</font>

すみません、やりすぎました。
(あの時はすまんかった)

※お子さんと遊んでいる際は優しくヒントをあげてください。

# まとめ

Alexaのスキル開発は難しいように見えて、しっかりとポイントを押さえておけば差し戻しも少なくてスムーズにリリースできます。
また、クリスマスプレゼント用にAmazon Echoを買った方、駅しりとりは無料のスキルですので、ぜひぜひ、遊んでみてください。

今年もあとわずかですが、よいお年をお迎えください。