AWSを使わずにタダでRubyでAlexaスキル開発をしてみた

これは、KIT Developer Advent Calendar 2017 13日目の記事です。
これに参加するのもこれが最後です。少し寂しいですね。

みなさん。こんにちは。306_sanです。最近流行りのスマートスピーカーで開発してみました。
自分はAmazon Echo dotを購入しました。余談ですがスマートスピーカーの強みって高性能なマイクでの音声認識だと思うのですが、Amazon Echoシリーズが全シリーズ7つのマイクアレイを搭載されており、廉価版であるdotでも高性能なマイクを使った音声認識が可能です(Google Homeと同等だと思ってます)。Google Home miniはマイクが一つしかないので検討してる方は一つの指標にしてもいいかもしれません。

さて、Google HomeではIFTTT→Slack→Rubyで受け取りでいろいろいじれます。
ex. https://youtu.be/8yT5PeZuWJ4
(※投稿者≠私)

しかし、これをAlexaでやろうとした時点では、どうもAlexaのスキルは各国で使えるかどうかの制限がかけられており、IFTTTやNode-Red経由(最近対応しました)では英語しか対応していないため、日本語でやるには自力でJSONを受け取って処理しないといけません。
幸いAlexaには[公式開発チュートリアル](https://developer.amazon.com/ja/alexa-skills-kit/training/building-a-skill)がありますが、このチュートリアルはAWSがないとできないものとなっています。AWSはちょっと大学では使えないので、自力で頑張ってみたのが今回の記事です。チュートリアルを見ずに開発したので詳しいことはよくわかってないままです。コメントどしどしお待ちしております。

参考にした記事:https://www.slideshare.net/tanakataro4/rubyalexa

### とりあえず開発に必要な物
- HTTPS環境とドメイン
    - オレオレ証明書は開発用途ならいいみたいですが、本番はダメみたいです。
    - 自分はサーバーを持っていないのでここで詰みました。
    - 今回はngrokを使って無理やり通信してます。
        - 導入は省略します。
- Ruby
    - 今回はRuby2.3.4を使いました
- 開発用語の知識
    - インテント、カスタムスロットなどスキル開発用語が出てきますがここでは詳しく説明しません。

それでは開発していきます。
目標として↑と同じ機能を持つスキルを開発します。

### Alexa周り
まず、Amazonの開発者登録をします。

<img width="457" alt="スクリーンショット 2017-12-11 13.33.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/79241/aa73aebd-8df2-354f-aa7d-2c449087bac8.png">
Alexa Skill Kitを選択します。

<img width="1205" alt="スクリーンショット 2017-12-11 13.36.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/79241/07eee1ee-f1f8-e767-22af-3311b459504c.png">
新しいスキルを追加するを選択します。

<img width="1039" alt="スクリーンショット 2017-12-11 13.39.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/79241/92d8647e-c0e1-9009-0134-fcf90ae60f01.png">
ここはお好みで

<img width="287" alt="スクリーンショット 2017-12-11 13.44.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/79241/d80c39cf-a8b4-555d-4197-2c08be22f65b.png">

JSONでインテントを記述します。インテントとはメソッドのようなものです。slotsというのはあとで説明するカスタムスロットです。

<img width="814" alt="スクリーンショット 2017-12-11 13.46.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/79241/ca214ea5-c3d8-6c4f-540a-914135a5e533.png">
ここでカスタムスロットを作成します。ざっくりいうと引数のようなものです。今回はコーヒーの量を選択したいので量をカスタムスロットで作成して追加します。

<img width="819" alt="スクリーンショット 2017-12-11 13.54.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/79241/f850af6f-800b-8811-8af8-099d1f44423f.png">
発話とインテントをここで紐付けます。すべてのパターンを洗い出して書いておくといいかと思います。
保存して次へ進みます。

<img width="808" alt="スクリーンショット 2017-12-11 13.56.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/79241/06a65283-a4c6-b5f1-f0b0-705ad49492c0.png">
サービスエンドポイントをHTTPSにして、適当なドメインをここで書いておきます。(ngrokは立ち上がるたびにドメインが変わるのであとで編集します。)

<img width="814" alt="スクリーンショット 2017-12-11 13.59.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/79241/9d931e59-0af5-8fe4-0c89-845b0b76a15d.png">
真ん中を選択して、次に進みます。

<img width="813" alt="スクリーンショット 2017-12-11 14.00.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/79241/d9bcb8c2-d652-c172-8f75-91aaf760ffe5.png">
ここまで来たらとりあえずAlexa周りの設定は完了です。
しかし自分の場合は、この状態でテストできなかったので、次の2つを適当に埋めてSkill Beta testingを有効化させて自分宛てに招待を送って、テストしています。

### Ruby周り
さて、次はバックエンドを書きます。がんばルビィ:lollipop:！(※筆者はラブライブ！(サンシャイン!!も)が大好きです)
gemとして素敵なSinatraベースの素敵なライブラリ「[ralyxa](https://github.com/sjmog/ralyxa)」があるので、これを使います。

```ruby:gemfile
source "https://rubygems.org"
gem 'ralyxa'
gem 'sinatra'
```
んで、bundle installでよしなにします。

フォルダー階層はこんな感じにします

```bash

├── Gemfile
├── Gemfile.lock
├── intents
│   └── test.rb
├── server.rb
└── vendor
    └── path
```
注意してほしいがintentsフォルダーで、この中に対応するインテントを書きます。
ファイル名は別に一致してなくても良いみたいですが、本番時のことを考えると同名にした方がいいかなと思います。
ここではtest.rbでまとめて受け取ります。

```ruby:server.rb
require 'bundler'

Bundler.require
post '/' do
  Ralyxa::Skill.handle(request)
end
```

```ruby:test.rb
# coding: utf-8

intent "LaunchRequest" do
   ask("こんばんは。コーヒーはいかがですか？")
end

intent "drip_coffee" do
  p request.slot_value("size")
  if request.slot_value("size").nil?
    tell("わかりました。コーヒを淹れますね。")
  else
    tell("わかりました。コーヒーを#{request.slot_value("size")}に淹れますね。")
  end
end

intent "SessionEndedRequest" do
  respond("Hello World!")
end
```
こんな感じでかきかきします。askとtellはユーザーの応答を待つかどうかの違いです。書いてないインテントもありますが、これは動かすために必要なインテント(~~おまじない~~)になります。ドキュメントを参照してください。

```bash
bundle exec ruby server.rb -p 8080 -o 0.0.0.0
````
とりあえず、127.0.0.1:8080にアクセスしてサーバーがた立ち上がっている(おそらくエラー画面だけど)のをを確認します。

次に別のターミナルを開いてngrokを立ち上げます。ngorkはポート開放などができなくても中継サーバーがいい感じに外部に公開してくれるサービスです。その為セキュリティのリスクがありますので開発用途に使いましょう。

```bash
$ngrok http 127.0.0.1:8080
```

すると、

```bash
ngrok by @inconshreveable                                       (Ctrl+C to quit)

Session Status                online
Account                       306san (Plan: Free)
Version                       2.2.8
Region                        United States (us)
Web Interface                 http://127.0.0.1:4040
Forwarding                    http://****.ngrok.io -> 127.0.0.1:8080
Forwarding                    https://****.ngrok.io -> 127.0.0.1:8080

Connections                   ttl     opn     rt1     rt5     p50     p90
                              0       0       0.00    0.00    0.00    0.00
```
となるのでこれでもう外部に公開されている状態になりました。httpsのURLを先程の開発者ポータルでも書き換えます。

<img width="1048" alt="スクリーンショット 2017-12-11 14.54.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/79241/b216d404-1df8-08ca-5b32-f9ac3304876e.png">
書き換えて保存します。

試しにテストページに飛んで試してみましょう。

<img width="810" alt="スクリーンショット 2017-12-11 15.00.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/79241/87901804-723f-b60b-6819-c19e9a4f8b7e.png">
良さそうです。あとは、招待メールを送ってスキルを有効化してechoで喋らせてみましょう。
Echoから呼び出す場合は「アレクサ、ほげほげを開いて」と発話すれば大丈夫です。

### まとめ
ralyxaを使えば、Rubyでスキル開発ができる。
ngrokを使えば、ポート開放しなくとも無理やり外部に公開できる。
サーバーなくとも無料でスキル開発ができる!!
最高！
rm mini3をポチったので次はスマートホーム化を目指します