Quick Links for Alexa のURLにパラメータを設定し、スキル内でパラメータ値を受け取るカスタムタスクの実装


# はじめに

:xmas-wreath1: カスタムタスクは、Timer APIが発表された時から興味があったんだけど、Timer APIでは、`Direct Skill Connections（まだ開発者プレビュー）` は申し込んでも許可されないようだし、使い方が見出せず静観していたのだが、`Quick Links for Alexa` でもカスタムタスクが使用できるということだったので、現在公開済みのスキルに対してカスタムタスクを追加してみることにした。

# Quick Links for Alexa とは

`Quick Links for Alexa` 用のURLを作成することで、そのURLを実行するとスキルが起動できるという仕組み。簡単に言うと、Webサイト、モバイルアプリ、SNSやメールなどにスキルのURLリンクを貼っておくと、そこからAlexaスキルが起動できるというもの。

<img width="70%" alt="Quick Links for Alexa" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/56f8733d-e648-3614-930f-3000873bdf71.png">

### 前提条件

**1. Quick Links for Alexa**

- スキルが公開されている。（カスタムスキルのみ）
- 対応デバイスは、 Echo、Echo Tap、Echo Dot、Echo Show、Echo SpotおよびFire TV。
- 子ども向けスキルでは使用できない。

**2. Quick Links for Alexa カスタムタスク　（外部からパラメータを受け取る場合）**

- 上記の `1. Quick Links for Alexa` に加えて以下の条件が必要。
- カスタムタスクが実装されている。（Amazon定義済みタスクは使用不可　※カスタムタスクであること）
- タスク定義ファイル内に設定する `x-amzn-alexa-access-scope` が `public` に設定されている。


# Quick Links for Alexa の使い方

公開されているスキルの `スキルID` を以下の構文に埋め込むだけ。ただ、これでは外部からパラメータを渡すことはできない。日本のスキルストアの場合、domainは `co.jp` です。

> ***URL構文***
> https\://alexa-skills.amazon.<b>&lt;domain&gt;</b>/apis/custom/skills/<b>&lt;skill-id&gt;</b>/launch

このURLをクリックすることでスキルが起動できる。または、Alexaデバイスに通知を送信して近くのデバイスからスキルを起動することができるようになる。

:point_right:クリック:point_right:　[くすりの時間（Quick Links for Alexa）](https://alexa-skills.amazon.co.jp/apis/custom/skills/amzn1.ask.skill.b6c9a68a-cdbc-4426-9d4f-e31cb69cca86/launch)

![くすりの時間 Quick Links for Alexa](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/11dbc194-260b-87e4-afb3-184c006c4497.png)

スマホでURLリンクをクリックするとアレクサアプリでも起動可能（下図の左　※今後、動作が変わる可能性はある）。`このデバイス` をクリックすることでアレクササプリでスキルが起動できる。アレクサアプリはEcho扱いなのだろうか。だた `Quick Links for Alexa` のURLをショートURLにしてしまうと間に画面（下図の右）が１枚はさまってしまうみたい。

　<img width="30%" border="1" alt="アレクサアプリでも起動できる" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/540d5479-b5f4-9981-1f7d-51ab3dd7a67b.png">　<img width="30%" border="1" alt="ショートURLはダメ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/c3d52add-23b0-b958-1ef4-8180786da29b.png">


# 外部からパラメータを渡すためには

上記の方法ではパラメータを渡すことはできない。どうするかというと `カスタムタスク` をスキル内に作成し、`カスタムタスク` 経由で `Quick Links for Alexa` を起動する必要がある。URLの構文も以下のように変わる。

> ***URL構文***
> https\://alexa-skills.amazon.<b>&lt;domain&gt;</b>/apis/custom/skills/<b>&lt;skill-id&gt;</b>/tasks/<b>&lt;custom-task-name&gt;</b>/versions/<b>&lt;task-version&gt;</b>?<b>&lt;task-input-key1&gt;</b>=<b>&lt;task-input-value1&gt;</b>&amp;<b>&lt;task-input-key2&gt;</b>=<b>&lt;task-input-value2&gt;</b>

<img width="70%" alt="Quick Links for Alexa パラメータ渡し" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/113f87b1-38d5-fc51-848a-33acce0bb4a7.png">

### パラメータを渡すと何が良いのか？

ただスキルを起動するだけではなく、以下のようなことができるようになる。

- 識別コードを設定することで、どのリンクから起動されたか判別する。
- リンクからのみ実行できる特別ステージを開始する。
- パラメータに見合った応答を返却する。　・・・などなど

:point_right:クリック:point_right:　[素数サイコロ（Quick Links for Alexa パラメータ渡し）](https://alexa-skills.amazon.co.jp/apis/custom/skills/amzn1.ask.skill.f40f1525-e285-481a-b0c0-4a4665be4fb1/tasks/Campaigns/versions/1?CampaignsCode=Qiita)

![素数サイコロ Quick Links for Alexa パラメータ渡し](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/81e4594a-8792-6936-53bf-edb7344ff4ed.png)

赤枠を見るとパラメータが表示されていることが確認できる。


# 実装方法

カスタムタスクを使用するためには、カスタムスキルにタスク定義を追加してあげる必要がある。ここではカスタムスキルを実装公開した以下の情報をベースに説明を進める。タスク名やキーは任意の文字列に変更可能。

|スキル名|タスク名|バージョン|キー|値|
|:--|:--|:--|:--|:--|
|素数サイコロ|Campaigns|1|CampaignsCode|Qiita|

タスク名は二種類あるので注意が必要。Amazonのドキュメントでは `Campaigns` を内部タスク名、 `amzn1.ask.skill.f40f1525-e285-481a-b0c0-4a4665be4fb1.Campaigns` をタスク名と表現しているみたい。


### スキル側の設定

**注）`Alexa Developer Console` の画面からはカスタムタスクを追加できない（※そのうち追加されると思う）。**

カスタムタスクを実装するには、今のところ `skill-package` に タスク定義を追加して、`ASK CLI v2` を使って `ask deploy` しないとスキルにカスタムタスクを追加することはできない。

**スキルパッケージにカスタムタスクを追加する**

`ask new` しても `tasks` フォルダと `Campaigns.1.json` ファイルは生成されないので、手作業で追加する。

> ***タスク定義ファイル名***
> <b>&lt;custom-task-name&gt;</b>.<b>&lt;task-version&gt;</b>.json

```:スキルプロジェクト
Project Name:
├─.ask
├─lambda
└─skill-package
    ├─assets
    ├─interactionModels
    └─tasks                   ←‐‐‐  フォルダを作成する（固定）
            Campaigns.1.json  ←‐‐‐  タスク定義ファイルを作成する
```

**タスク定義ファイルにタスクの情報を記載する**

タスク定義ファイルの書き方は[<b>こちら</b>](https://qiita.com/takara328/items/f09677ce993beb030354)を参照。

**skill.json にカスタムタスクを追加する**

```JSON:skill.json
    "apis": {
      "custom": {
        "tasks": [
          {
            "name": "Campaigns",
            "version": "1"
          }
        ]
```

### Lambda側の処理

**LaunchRequestHandler にコード追加**

カスタムタスクのリクエストタイプは `LaunchRequest` のため、`LaunchRequestHandler` で `task` を取得する。
`handlerInput.requestEnvelope.request.task` にタスクの情報が入ってくる。タスク情報がない場合は、`undefined` 。


```JavaScript:LaunchRequestHandler
const LaunchRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
  },
  handle(handlerInput) {
    task = handlerInput.requestEnvelope.request.task;

    if (task.name === 'amzn1.ask.skill.f40f1525-e285-481a-b0c0-4a4665be4fb1.Campaigns') {
      :
      タスク固有の処理
      :
    }
```

`Quick Links for Alexa` のURLをクリックすると、Alexaスキルには以下のようなリクエストが送られてくる。リクエストタイプ `LaunchRequest` にタスク情報 `task` が含まれていることがわかる。

```JSON:ログ（handlerInputの一部を抜粋）
    "request": {
        "type": "LaunchRequest",
        "requestId": "amzn1.echo-api.request.0324d47b-90c7-43fe-bb39-f3c2f582853a",
        "timestamp": "2020-11-16T02:44:17Z",
        "locale": "ja-JP",
        "shouldLinkResultBeReturned": false,
        "task": {
            "name": "amzn1.ask.skill.f40f1525-e285-481a-b0c0-4a4665be4fb1.Campaigns",
            "version": "1",
            "input": {
                "CampaignsCode": "ABC"
            }
        }
    }
```

**値の取得方法**

URLに設定されたキー `CampaignsCode` に対する値が取得できる。

```JavaScript:LaunchRequestHandler
    const campaignsCode = handlerInput.requestEnvelope.request.task.input.CampaignsCode;
```

タスク定義ファイルに `CampaignsCode` を定義する。今回は `String` にした。他の型を使用する場合はドキュメント参照。

```JSON:Campaigns.1.json（タスク定義ファイルより抜粋）
    "components": {
      "schemas": {
        "Input": {
          "type": "object",
          "properties": {
            "CampaignsCode": {
              "type": "string"
            }
          }
        },
```

# テスト

公開されてるスキルじゃないと、`Quick Links for Alexa` のURL経由で動作を確認することはできない。そのため、SMAPIを使って疑似的にリクエストを送信して動作することを確認する。

### リクエストファイルを作る

`Quick Links for Alexa` のカスタムタスクは、`LaunchRequest` で受け取られるようなので、それに合わせたリクエストファイル `input.json` を作成してテストする。

```JSON:input.json（この程度の記載でテスト可能）
{
	"version": "1.0",
	"session": {
		"new": true,
		"sessionId": "amzn1.echo-api.session.9cd00055-00a1-4f60-b0a7-6bb642ee396d",
		"application": {
			"applicationId": "amzn1.ask.skill.f40f1525-e285-481a-b0c0-4a4665be4fb1"
		},
		"user": {
			"userId": "amzn1.ask.account.AENXBNJYJI3VAT4UOIJV6F3ZXM263GSPNZCOUYOMU7D4I35M3BF36FI2GI37OWTCXPHS5DHFBD4V3TR2RDDY5BDUSLAQCO5C7AOYUXWJ5MWALROWCG5SEFDBXE7PCKKO2VDAMPKIAFNMQWJBTMN6ZVB72YPUWJ2V43ZBCGUMUIZ6XBYWYO7QW7O3YRL7HWKOZHPKUF27GJJV4XI"
		}
	},
	"context": {
		"System": {
			"application": {
				"applicationId": "amzn1.ask.skill.f40f1525-e285-481a-b0c0-4a4665be4fb1"
			},
			"user": {
				"userId": "amzn1.ask.account.AENXBNJYJI3VAT4UOIJV6F3ZXM263GSPNZCOUYOMU7D4I35M3BF36FI2GI37OWTCXPHS5DHFBD4V3TR2RDDY5BDUSLAQCO5C7AOYUXWJ5MWALROWCG5SEFDBXE7PCKKO2VDAMPKIAFNMQWJBTMN6ZVB72YPUWJ2V43ZBCGUMUIZ6XBYWYO7QW7O3YRL7HWKOZHPKUF27GJJV4XI"
			}
		}
	},
	"request": {
		"type": "LaunchRequest",
		"requestId": "amzn1.echo-api.request.83e6229b-87ab-4d4b-b103-5b1dadf35755",
		"timestamp": "2020-11-14T13:05:08Z",
		"locale": "ja-JP",
		"task": {
			"name": "amzn1.ask.skill.f40f1525-e285-481a-b0c0-4a4665be4fb1.Campaigns",
			"version": "1",
			"input": {
				"CampaignsCode": "ABCD"
			}
		}
	}
}
```

### SMAPIでカスタムタスクの動作確認をする

`invoke-skill-end-point` を使ってエンドポイントに対して `input.json` を送信する。スキル内で `ABCD` が取得できたら成功。DynamoDBに値を格納するかログなどに出力するようにしておくと確認しやすい。

> ***invoke-skill-end-point***
> ask smapi invoke-skill-end-point -s <b>&lt;skill-id&gt;</b> --stage development --skill-request-body file:./<b>input.json</b> --endpoint-region <b>FE</b>

このときリージョンにエンドポイントが設定されていないとエラーになるので、`skill.json` でのリージョンを設定しておく。`invoke-skill-end-point` のオプションで指定したリージョンが `FE` であれば `regions` にも `FE` を設定する必要がある。設定してないと動かない。

```JSON:skill.json
    "apis": {
      "custom": {
        "endpoint": {
          "uri": "arn:aws:lambda:us-east-1:023972248338:function:ask-custom-SosuDice-default"
        },
        "regions": {
          "FE": {
            "endpoint": {
              "uri": "arn:aws:lambda:us-east-1:023972248338:function:ask-custom-SosuDice-default"
            }
          }
        },
```


`Alexa Developer Console` でエンドポイントを確認し、`極東` にURLが設定されていればＯＫ。

![Alexa Developer Console でエンドポイントを確認](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/8acc1b47-3f56-fbd1-b795-56dbeb270c54.png)


### 公開スキルで動作確認する

スキルが公開されたら、URLを組み立てて実際にブラウザなどで実行して動作確認をする。以下のような感じでURLを作成する。値を `ABCD` とは別の文字列 `Qiita` にして、スキル内でその値が取得できたら成功。

> ***URL組み立て***
> [http\://alexa-skills.amazon.co.jp/apis/custom/skills/<b>amzn1.ask.skill.f40f1525-e285-481a-b0c0-4a4665be4fb1</b>/tasks/<b>Campaigns</b>/versions/<b>1</b>?<b>CampaignsCode</b>=<b>Qiita</b>](https://alexa-skills.amazon.co.jp/apis/custom/skills/amzn1.ask.skill.f40f1525-e285-481a-b0c0-4a4665be4fb1/tasks/Campaigns/versions/1?CampaignsCode=Qiita)

# おわりに

起動するまでにちょっともっさりした感じはあるけど、スキルの起動方法の選択肢が増えた事でいままでとは違うスキルの作り方ができると感じた。これアイデア次第でおもしろいスキルが作れるんじゃないかなと思った。

# 参考

- [カスタムタスクのQuick Linkを作成する（Amazon公式）](https://developer.amazon.com/ja-JP/docs/alexa/custom-skills/create-a-quick-link-for-your-custom-task.html)
- [スキルにカスタムタスクを追加する（Amazon公式）](https://developer.amazon.com/ja-JP/docs/alexa/custom-skills/implement-custom-tasks-in-your-skill.html)
- [スキルパッケージAPIリファレンス（Amazon公式）](https://developer.amazon.com/ja-JP/docs/alexa/smapi/skill-package-api-reference.html)
- [ASK CLIコマンドリファレンス（v2）（Amazon公式）](https://developer.amazon.com/ja-JP/docs/alexa/smapi/ask-cli-command-reference.html)
- [Alexa カスタムタスクの実装と動作確認方法](https://qiita.com/takara328/items/f09677ce993beb030354)
- [素数サイコロ（Alexaスキル）](https://amzn.to/35Th2sX)
- [くすりの時間（Alexaスキル）](https://amzn.to/3fnSR8Z)