Azure Functions + C# を利用してAlexaスキルを開発してみる
とても悲しいはまり方をしたので、自分みたいな人を減らすためにblogに書いておきます。
Azure Functionsご存知ですか?AWS Lambdaみたいなやつですね。
料金もどちらのサービスもほぼ同じですね。
Bot Servicesを使ってみていたのでせっかくならC#を覚えながらやってみようと思いAlexaをしゃべらせてみることにしました。
前提
- クライアントOSはWindows 10
- Visual Studio 2017 Community Edtion以上がインストール済であること
- C#開発環境が整っていること
- Azure のアカウントが作成済みであること
Visual StudioにAzure開発を追加インストールする
Visual Studioで新しいプロジェクトをつくる
- ファイル → 新規作成 → 【プロジェクト】をクリックします。
- Azure Functions C#を選択し、【OK】をクリックします。
- 必ずAzure Functions v2を選択し、【OK】をクリックします。
これにきづかず、Azure Functions v1で作成してしまったがために、3日くらい悩んでました・・・orz
Azure Functions v1を選んでしまうとどうなるのか
その1.起動時にILoggerというタイプはないよ。とかいわれる
[2019/01/05 12:28:40] The following 1 functions are in error: [2019/01/05 12:28:40] Alexa: Microsoft.Azure.WebJobs.Host: Error indexing method 'SmartSpeakerEndpoint.Alexa'. Microsoft.Azure.WebJobs.Host: Cannot bind parameter 'log' to type ILogger. Make sure the parameter Type is supported by the binding. If you're using binding extensions (e.g. ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. config.UseServiceBus(), config.UseTimers(), etc.).
その2.AlexaからJSONが流れてくるはずなのですが、エラーもでずうんともすんともいわない。
ちょまどさんの登壇資料をみたり色々な方のサンプルソースをコピペしてみたりしてもまったく状況が改善しませんでした
GitHub内のIssueでこんなものを見つけv2?となり、よくよくみたらそこに選択肢があるのか!!!となりました。
その時見たIssueはこちら! → https://github.com/Azure/Azure-Functions/issues/900
新規作成したFunctionsを動かしてみる
- 先ほど作成したプロジェクト上で、F5キーを押すか、デバッグ → 【デバッグの開始】をクリックします。
(Web Job Tools~をインストール等が表示された場合はインストールしてください。)
- Azure Functionsのロゴが表示されずらずらと流れていきます。
- 起動が完了するとURLが表示されます。
- ブラウザを開き、アドレス欄に下記のように入力します。
http://localhost:7071/api/Function1?name=Alexa - 以下のように、Hello, Alexaと表示されればOKです。
サーバレスな簡単なアプリができあがりました!
- 確認が完了したらVS2017に戻りデバッグを停止させておきましょう
白枠で囲った部分の停止ボタンを押します。
Alexa developer consoleでスキルをつくる
Alexa developer consoleにサインイン
- alexa developer consoleにアクセスしサインインします。
- 【スキルの作成】をクリックします。
- スキル名を入力し、デフォルトの言語が日本語になっていることを確認し、【スキルを作成】をクリックします。
サンプル発話を登録する
呼び出し名をつける
Alexa応答コードをC#で記述する
Alexa.NETを追加する
有志の方が作成されているAlexa.NETというライブラリをNuGetで追加します。
- ツール → NuGetパッケージマネージャー → 【ソリューションのNuGetパッケージの管理】をクリックします。
またはプロジェクト → NuGetパッケージの管理をクリックします。
- 参照タブをクリックし、検索窓に「Alexa.Net」と入力します。
Alexa.NETを選択し、【インストール】をクリックします。
C#でコードを書く
- Function1.csを開きます。
- Alexa.NETを使うために、下記を追加します。すでにusing ~があるのでその下に追記してください。
using Alexa.NET.Request; using Alexa.NET.Request.Type; using Alexa.NET.Response;
- 自動生成されたコードを削除します。
以下の部分を削除します。log.LogInformation("C# HTTP trigger function processed a request."); string name = req.Query["name"]; string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); dynamic data = JsonConvert.DeserializeObject(requestBody); name = name ?? data?.name; return name != null ? (ActionResult)new OkObjectResult($"Hello, {name}") : new BadRequestObjectResult("Please pass a name on the query string or in the request body");
- Alexaから流れてくるJSONを受け取り解析して応答させるコードを書きます。
コード全体は以下のようになります。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; namespace FunctionApp1 { public static class Function1 { private static string IntroductionMessage = "システム運用日記にようこそ!"; [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; default: skillResponse.Response.OutputSpeech = new PlainTextOutputSpeech { Text = "すみません。わかりません", }; break; } return new OkObjectResult(skillResponse); } } }
ngrokを利用してAlexaから応答されるか確認する
実行して確認したいところですが、このままでは外部からはアクセスできないので、ngrokというものを利用してトンネル経由でアクセスができるようにします。
- ngrokのホームページにいきアカウントを作成します。
すでに持っている方はログインします。 - ngrokをダウンロードします。
- ダウンロードしたファイルを解凍し、適当なパスにコピーします。
- コマンドプロンプトを開き、ngrokのページで指示されている通りauthtokenを入力し紐づけます。
./ngrok authtoken <<アクセストークン>>
- 次に以下のように入力し、ngrokを起動します。
ngrok http 7071 --host-header localhost
Azure Functions用のローカルデバッグ環境の場合localhostからしか受け付けない状態で起動するためhost-headerオプションを指定します。
※設定ファイルを編集することでlocalhost以外からでも受け付けるようにすることは可能です。 - 表示されたhttpsのURLをコピーします。
- 前項で作成していたプロジェクトのデバッグを開始します。
- ブラウザを開いて、コピーしたURLを開きます。
- ngrok側のプロンプトに下記のように表示されてくるはずです。
- ブラウザ上には、こんな感じに表示されますね。
Alexa developer consoleからエンドポイントを設定する
- 左ペインから「エンドポイント」を選択します。
- HTTPSを選択し、デフォルトの地域に先ほどコピーしたURLを貼り付け、後ろに先ほどデバッグを開始したときに表示されるURLを入力します。
例) https://hogehoge.ngrok.io/api/Function1
下段にある証明書は、「開発用のエンドポイントは、証明機関が発行したワイルドカード証明書をもつドメインのサブドメインです」を選択します。
これを選択していなかったために、ここでもはまりました・・・orz
- 上のほうにある【エンドポイントを保存】をクリックします。
- 左ペインから呼び出し名を選択します。
- 【モデルをビルド】をクリックします。
- 通知で「正常にビルドされました」と表示されるまで待ちます。
シミュレータを利用して正常に動作するか確認する
- 「テスト」タブをクリックします。
- 「開発中」を選択します。
- 呼び出し名で設定した名前+”を開いて”と入力します。
今回の場合は、「システム運用日記を開いて」と入力しエンターキーを押します。
- 「システム運用日記へようこそ!」と表示&聞こえてくれば成功です!
Azure Functionsへデプロイする
- ソリューションエクスプローラーで、プロジェクトを右クリックし【発行】をクリックします。
- 【開始】をクリックします。
- 「新規作成」を選択し、【発行】をクリックします。
- アプリ名やリソースグループ等を選択または新規作成し、【作成】をクリックします。
- 作成が完了するとホストURLが表示されます。
Azure Portal上から確認する
- Azure Portalを開き、ログインします。
- 左ペインから「Function App」を選択します。
表示カスタマイズ等していて表示されていない場合には検索窓にFunction Appと入力し表示します。
- 先ほど発行した名前のFunction Appが登録されていることが確認できます。
- 展開するとFunction1という関数があることもわかります。
AlexaのエンドポイントをAzure Function側へ変更する
- 先ほど展開した「Function1」をクリックします。
- 【関数のURLの取得】をクリックします。
- 【コピー】をクリックします。
- エンドポイントに先ほどコピーしたURLを入力し保存後、モデルをビルドします。
- シミュレータを利用し正常に動作するかテストします。
これでサーバを構築することなくAlexaからの応答がAzure Functionに流れAlexaをしゃべらせることができます!
あとは色々とコードを編集し、スキルをつくっていくと色々なことができるようになりますね!
参考URL
https://github.com/chomado/SmartSpeakerGetLatestArticle