Azure復習☆彡 Azureを使ってポケモンの弱点を教えてくれるボット作成


# はじめに
最近ポケモンの新作にはまってます。しかしブランクがあるといつも弱点を忘れて返り討ちにあいます。ので、なんだっけ？となったときように弱点を教えてくれるボット(Alexaのスキル)を作成しました。その過程を簡単に紹介しつつ、学んだことをアウトプットしていきます。
※最終的なコードはGitHubに上げているので詳しいコードを見たい方はそちらを確認していただければと思います。

# 達成したいこと
すごく簡単なイメージ図。スマートスピーカーはAlexa。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/c8eab34d-cd0f-ab24-5e24-3852156a54f3.png)


※PokeAPIなるものがありますが、情報量が多いのとAzureの復習というテーマなのであえて作ってみました。

# 環境と言語
- windows10
- Visual Studio Professional 2022
- SQL Server Management Studio v18.4 
- .NET 6.0
- C#
- Azure

# 今回使用したAzureのリソース
- Azure Functions
- Azure App Service
- Key Vault
- Azure SQL Server


# DBの作成

## Azure SQL Databaseのリソース作成

リソース追加で「SQL Database」を選択して作成していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/fb423342-d16c-14d9-65ae-e17aa8b71743.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/581fc548-b3d9-2dcf-d9fe-c861a9721110.png)

👆のサーバーは新規で作成しました。「新規作成」をクリックするとこんな風にサーバーが作れます。
個人的なテスト利用なので認証方法は「SQL認証」にしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/a1f595b8-c13d-3c40-25fc-84f138e9c878.png)

データベースの照合順序は仕事でいつもこちらを使用しているのでこちらの設定にしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/98480090-d2e5-9e98-f118-9b4f29801bb5.png)

入力が完了したら「確認および作成」で作成します。

作成が完了したら、作成したSQL Serverのリソースに移動して、「ネットワーク」からアクセス許可を足していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/db4b5b34-f0c6-cab3-3292-41e4dfd92fc7.png)

ファイアーウォール規則に自分が使用しているグローバルIPアドレスを設定します。忘れがちですが、**接続する環境が変わるたびに追加は必要です。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/1090ff9c-87a1-1b94-b007-a26f92107c3b.png)


## SSMSでつないでみて、データを作成する

私は使い慣れているSSMSから作成していくことにしました。

サーバー名・認証方法はAzureで設定した通りでログインできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/59232785-ac99-2d0c-f017-826eeb8479ed.png)

こちらでテーブルとタイプを取得するプロシージャを作成。

TypeというタイプIDとタイプ名を保管したテーブルとTypeCompatibilityというポケモンの相性を保管したテーブルを作成しました。

```sql
ET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[GetTypeCompatibility]
	@TypeName AS [NVARCHAR](50)
AS
	SELECT 
		T2.TypeName as 'CompatibilityType',
		TC.Power as Power
	FROM
		TypeCompatibility TC
	INNER JOIN [dbo].[Type] T 
		ON TC.AttackTypeId = T.TypeId
	INNER JOIN [dbo].[Type] T2 
		ON TC.DefenseTypeId = T2.TypeId
	Where 
		T.TypeName = @TypeName
	;
```


# 弱点を教えてくれるAPI作成
.NET Core 6.0でAPIをつくってApp Serviceに発行しました。
発行方法は公式ドキュメントの[こちら](https://learn.microsoft.com/ja-jp/azure/app-service/quickstart-dotnetcore?tabs=net60&pivots=development-environment-vs)から
**発行したら構成で接続文字列の設定をするのを忘れないようにしましょう！**(忘れていて詰まってた)

## 接続文字列のKey Vault管理
接続文字列にはパスワードなども含まれています。そのためKey Vaultに保管するのがいいです。
ので、リソースにKey Vaultを追加して、シークレットの作成をします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/5ee50ad6-46d6-eee8-fc11-92a378cdb5f3.png)

発行したらシークレット識別子を保存しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/a983cb60-7cc6-e30a-cd3c-3df4347b0f17.png)


続いて発行したApp Serviceの構成へのアクセスを追加するために「アクセスポリシー」を追加します。
アクセス許可はひとまずシークレットの取得です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/62c51f9c-3801-9a80-19dd-278bbf575b88.png)

プリンシパルはApp ServiceのオブジェクトIdで検索をすると、候補に表示されるので追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/88408e79-5270-7530-db86-50f3f14b4635.png)

※オブジェクトIdはApp Serviceの「ID」のところから見れます。なければ追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/b9feee1d-f9cf-d8f8-c139-1f90d806b3ad.png)


作成を押すと追加されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/256c94d9-8ae0-7db7-5309-f480c9ef59b6.png)

最後にApp Serviceの構成の値に追加します。
`「@Microsoft.KeyVault(SecretUri=)」`の＝の後にシークレット識別子を貼りつけます。末尾にあるGuid？みたいのは消して大丈夫です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/874256e5-ce37-234c-dddd-3065d8713959.png)


接続がうまくいくとソースに「キーコンテナーの参照」と表示され、緑チェックがつきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/9e09733a-51a0-d84b-b513-dd2ea9cd1412.png)


## 作成中に困ったこと
.NET 6.0は初めて使用したのですが、そのときに.NET 6.0が参照されなくて困ったので解決した方法をメモしておきます。

発生したエラーメッセージ
> .netframework,version=v6.0" の参照アセンブリが見つかりませんでした。.net 5.0 以上をターゲットにするために、古い .net sdk を使用している可能性があります。visual studio と .net sdk のいずれかまたはその両方を更新してください。

このコマンドをながしたところ解決しました。
```dotnet new globaljson --sdk-version 6.0.100```

# Azure Functionsを使ってAlexaで呼び出してみる
基本的に以前Qiitaでかいた記事通りに作成しているので、詳しい作成方法は[こちら](https://qiita.com/usomaru/items/e6aa15bff6ed53d7ce5a)の記事をご覧ください。

以前の記事ではない任意の言葉(今回でいうとポケモンのタイプ)をFunctions側で取得した方法のみ記載したいと思います。

## Alexaの設定
作成したインテントページのインテントスロットに任意の言葉の名前(今回は「type」にした)を入力して保存します。この画像ではスロットタイプが選択されていますが、スロットタイプはこの後作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/01c6939c-357e-a40c-2333-e83df74b651a.png)

スロットタイプを追加します。スロット値に想定される任意の言葉を追加していきます。今回はポケモンのタイプ全16種類をかきました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/ac69d297-2165-6cc3-04e4-4cad236fdf1b.png)

最後にインテントからサンプル発話を追加します。鍵かっこつきでスロットで入力した値を入れてあげると、そこに任意の言葉が入ったサンプル発話となります。
例えば、{いわ}タイプのポケモンの相性をおしえてという感じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/b0b640f2-40fb-06e6-02df-2c00ed3a27ef.png)


すべて完了したらモデルを保存してビルドします。

## Functions側の取得方法

「これでタイプ名が取得できるはず」というコメントの下が該当コードです。

```C#
case IntentRequest ir:
{
    // これでタイプ名が取得できるはず
    var typeName = ir.Intent.Slots["type"].Value;
    var r = await HandleIntent(ir.Intent.Name, typeName);
                        
    skillResponse.Response.OutputSpeech = new PlainTextOutputSpeech
    {
        Text = r.ResponseMessage
    };
    skillResponse.Response.ShouldEndSession = r.ResponseFinishFlg;
}
break;
```
AlexaがFunctionsにリクエストしているJsonを見るとより分かりやすいと思います。

```Json
"request": {
		"type": "IntentRequest",
		"requestId": "amzn1.echo-api.request.04f1bfe6-5cfe-4b5e-b2ff-d1339841cec3",
		"locale": "ja-JP",
		"timestamp": "2022-12-10T08:58:13Z",
		"intent": {
			"name": "PokemonCompatibility",
			"confirmationStatus": "NONE",
			"slots": {
				"type": {
					"name": "type",
					"value": "ほのお",
					"resolutions": {
						"resolutionsPerAuthority": [
							{
								"authority": "amzn1.er-authority.echo-sdk.amzn1.ask.skill.4fb46ed0-cafa-4977-894b-2388261215fe.TYPE_NAME",
								"status": {
									"code": "ER_SUCCESS_MATCH"
								},
								"values": [
									{
										"value": {
											"name": "ほのお",
											"id": "e95455c77f60d63ec36417a57e51fb9b"
										}
									}
								]
							}
						]
					},
…省略
```
slotsの中に作成したインテントスロットの「type」がありますね。


余談ですが、Functionsのrouteはちゃんと考えて設定してあげないとポータルの関数のところに何も表示されなくなってしまうので注意が必要ということを学びました。
[Azure Functions の HTTP トリガーで、キーが間違っていないのに 401 エラーとなってしまうケース](https://himanago.hatenablog.com/entry/2020/05/14/232355)


# 完成品
呼び出してみるとこんな感じです。タイプ言うだけでも反応しました。
タイプをデータベースにすべてひらがなでいれた(Alexaがどこまで優秀かわからなかった)ので文字としては読みにくいです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201826/ea8e83c4-807a-0cb4-e9f5-6cf4496ce4f5.png)


完成してから効果なしの情報を取得し忘れたことに気づく。。。

# おわりに
ボット作成に思ったより時間がかかってしまいました。が久しぶりにAlexaを起動できたのでよかったです。一時期めっちゃカスタムスキルが流行った？気がしましたが、最近の記事はもうほとんど見かけなかったので、もしやもう時代遅れ…？


# 今年を振り返ってみて
今年特に感じたことは**公式ドキュメントをよく見ろ**です。
公式ドキュメントを隅々までよく見るとだいたい正解がかいてあります。ありがたや。
今年は特にAzureに触れる機会が多かったおかげでだいぶ理解が進んだ年になったと思います。
こうして復習する機会もでき、よいAzureライフを送ることができました。来年もよいAzureライフを送れるように頑張ります。

# 参考
- [Azure SQL DatabaseのMS公式ドキュメント](https://learn.microsoft.com/ja-jp/azure/azure-sql/database/?view=azuresql)
- [スロットを使って任意の入力を受け付けよう](https://thinkit.co.jp/article/14642)
- [Azure Functions の HTTP トリガー](https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-http-webhook-trigger?tabs=in-process%2Cfunctionsv2&pivots=programming-language-csharp)

# GitHub
今回作成したコードはGitHubにあげています。
https://github.com/usomaru/pokemon-compatibility