LoginSignup
4
3

More than 5 years have passed since last update.

AzureFunctionsを使ってAlexaのカスタムスキル作成備忘録

Last updated at Posted at 2019-01-31

はじめに

TechSummitのちょまどさんセッション(ちょまどさんのブログ)に触発されて、Alexaを購入しました。
が、特にカスタムすることなく、音楽を聴く置物と化していました。
話は変わりますが、私の上司にヨーグルトが大好きな方がいます。
そこで、Alexaを使っておススメされたこれまでのヨーグルト通知機能を作成してみることにしました。

環境

  • Visual Studio 2017 Professional(Community Edtion以上)
    • 言語:C#
  • Azureサブスクリプション

なかなかうまくいかずに苦戦していましたが、
この方の記事を見ながらやったらあっさりできました。感謝です。
Azure Functions + C# を利用してAlexaスキルを開発してみる

AzureFunctions側の作成

AzureFunctionsのソリューションを作成

新規作成→プロジェクトより、Azure Functionsを選択します。
image.png

上記、記事の方もおっしゃっているように、AzureFunctions v2を選択します。
image.png

OKを押せば、ソリューションの完成です。

処理

まず、NuGetパッケージマネージャーよりAlexa.NETを追加します。
image.png

作成したソリューションに下記コードをかきました。

Function1.cs
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

using Alexa.NET.Request;
using Alexa.NET.Request.Type;
using Alexa.NET.Response;
using System.Linq;
using AlexaSkill.Models;

namespace AlexaSkill
{
    public static class Function1
    {
        private static string HelloMessage { get; } = "こんにちは!ヨーグルトスキルです!";
        private static string IntroductionMessage = "今日のヨーグルトスキルです!おすすめのヨーグルトを教えてと話しかけてください。";
        private static string ErrorMessage { get; } = "すみません、わかりませんでした!";

        [FunctionName("Function1")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            string requestJson = "";
            using (var sr = new StreamReader(req.Body))
            {
                requestJson = await sr.ReadToEndAsync();
            }
            SkillRequest skillRequest = JsonConvert.DeserializeObject<SkillRequest>(requestJson);
            var skillResponse = new SkillResponse
            {
                Version = "1.0",
                Response = new ResponseBody()
            };
            switch (skillRequest.Request)
            {
                case LaunchRequest lr:
                    skillResponse.Response.OutputSpeech = new PlainTextOutputSpeech
                    {
                        Text = IntroductionMessage,
                    };
                    break;
                // インテント使って返事されるやつ
                case IntentRequest ir:
                    {
                        var r = HandleIntent(ir.Intent.Name);
                        skillResponse.Response.OutputSpeech = new PlainTextOutputSpeech
                        {
                            Text = r.ResponseMessage,
                        };
                        skillResponse.Response.ShouldEndSession = r.ResponseFinishFlg;
                    }
                    break;

                default:
                    skillResponse.Response.OutputSpeech = new PlainTextOutputSpeech
                    {
                        Text = "すみません。わかりません",
                    };
                    break;
            }
            return new OkObjectResult(skillResponse);
        }


        //インテントの処理を記述
        private static HandleIntentResult HandleIntent(string intent)
        {
            switch (intent)
            {
                case "HelloIntent":
                    return new HandleIntentResult
                    {
                        ResponseMessage = HelloMessage,
                        ResponseFinishFlg = false,
                    };
                case "AskYogurutoIntent":
                    {
                        string[] yogurutWord = new[] { "ローソンの飲むりんごヨーグルト", "セブンの期間限定ヨーグルト", "ごろごろ果実のはっさく" };
                        // ランダムソート
                        string[] yogurutWords = yogurutWord.OrderBy(i => Guid.NewGuid()).ToArray();
                        return new HandleIntentResult
                        {
                            ResponseMessage = yogurutWords[0],
                            ResponseFinishFlg = false,
                        };
                    }
                case "AMAZON.StopIntent":
                    return new HandleIntentResult
                    {
                        ResponseMessage = "また明日",
                        ResponseFinishFlg = true,
                    };
                default:
                    return new HandleIntentResult
                    {
                        ResponseMessage = ErrorMessage,
                        ResponseFinishFlg = false,
                    };
            }

        }
    }
}
HandleIntentResult.cs
namespace AlexaSkill.Models
{
    class HandleIntentResult
    {
        public string ResponseMessage { get; set; }
        public bool ResponseFinishFlg { get; set; }
    }
}

基本的にちょまどさんのコードに一部変更を加えているのみの実装です。
LaunchRequestが「Alexa,~開いて!」と言った後に走るみたいです。
一方、IntentRequestは、後ほど設定するAlexa Developer Consoleの
インテントに設定した言葉が読み取られると、走るみたいです。
今回は、とりあえずAlexaに言って欲しい言葉を配列にいれて、ランダムに返してくるようにしました。

発行

AzureFunctionsを先にAzureで作成しておくという手段もありますが、新規作成にて発行しました。

image.png

Alexa Developer Consoleにて設定の作成

公式の説明がわりとわかりやすいです。
Alexaスキル開発トレーニング
ここでは軽く説明したいと思います。

  1. alexa developer consoleにログインします。

  2. 上の方に「あなたのAlexaダッシュボード」欄があると思うので、そちらからSkillsを選択。

  3. スキルの作成を選択。

  4. スキル名や呼出名を指示に従って入力していきます。

  5. インテントを追加します。インテントを追加することによって、
    先ほどコードにかいたIntentRequest部分が走ります。
    追加したインテントはこちら
    image.png
    テストするとこんな感じでJsonに送られます。
    image.png

  6. 追加が完了したら、モデルを保存し、モデルをビルドします。

  7. 最後にエンドポイントの設定をします。
    image.png
    デフォルトの地域の値は下記のように取得します。

    1. Azureポータルへ移動し、今回追加したAzureFunctionsを選択します。 image.png
    2. 関数のURLの取得をクリックします。 image.png
    3. URLをコピー
      image.png

終わったら再びモデルを保存し、ビルドします。

テスト

テストタブを選択し、スキルテストが有効になっているステージを開発中とします。
image.png

はまったこと

AMAZON.StopIntent(もとからあるもの)に「また明日」というセリフを言って、
スキルを終了して欲しかったのですが、
スキルが終わらずに永遠に「こんにちは!」と言い続けていました。
調べたところ、スキルを終わらすには、shouldEndSessionをtrueにする必要があったそうですね。。
しばらくAlexaのコンセントを抜いてテストをしていました。
標準ビルドインテント

さいごに

Alexaが音楽を聴くただの置物から卒業できそうです。

参考

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3