Microsoft Bot Framework による Cortana スキルの追加方法

# Cortana Skills Kit が Public Preview に
5/10 に Cortana Skills Kit が public preview になり、Microsoft Bot Framework のチャネルの一つとして、パーソナル アシスタントの Cortana が追加されました。
これにより、Windows 10, Android, iOS で利用可能な Cortana に対してスキルを追加可能となり機能を拡張することできるようになりました。
現状では、日本語版の Cortana には、対応していませんが、ここでは、英語版の Cortana を使い、スキルを追加する手順を紹介します。

# Bot Framework のセットアップ

以降の手順では、Visual Studio 2017 を利用します。
Visual Studio 2017 をご利用でない場合は、条件によっては、無償利用が可能なので、以下からダウンロードしてインストールしてください。

- [Visual Studio のダウンロード](https://www.visualstudio.com/ja/downloads/)

以下から、Bot Application テンプレートをダウンロードします。

- [bot framework の Visual Studio テンプレート](http://aka.ms/bf-bc-vstemplate)

ダウンロードした ”Bot Application.zip" を以下のパスに配置します。

- %USERPROFILE%\Documents\Visual Studio 2017\Templates\ProjectTemplates\Visual C#\

このテンプレートの追加により、Visual Studio 2017 の [ファイル] - [新規作成] - [プロジェクト] - [テンプレート] - [Visual C#] - [Bot Application] を選択可能となります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/c9170695-7023-7268-c242-6983b2fa47da.png)

次に、以下から、Bot Framework Emulator をダウンロードしてインストールします。

- [Bot Framework Emulator](https://docs.microsoft.com/en-us/bot-framework/debug-bots-emulator)

# bot の開発とデバッグ実行

Visual Studio 2017 を起動し、[ファイル] - [新規作成] - [プロジェクト] - [テンプレート] - [Visual C#] - [Bot Application] を選択し、[名前] に任意の名前(ここでは、CountBot)を設定し、[OK] を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/081caf60-2e0b-ff8d-747f-0fc767e0124a.png)

ASP.NET Web API のプロジェクトが生成されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/06fe75d7-eeff-0ac9-204e-75966b773b09.png)

[Controllers] - [MessagesController.cs] のソース コードを確認します。

```csharp:MessagesController.cs

[BotAuthentication]
public class MessagesController : ApiController
{
    /// <summary>
    /// POST: api/Messages
    /// Receive a message from a user and reply to it
    /// </summary>
    public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
    {
        if (activity.Type == ActivityTypes.Message)
        {
            await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
        }
        else
        {
            HandleSystemMessage(activity);
        }
        var response = Request.CreateResponse(HttpStatusCode.OK);
        return response;
    }

    private Activity HandleSystemMessage(Activity message)
    {
        if (message.Type == ActivityTypes.DeleteUserData)
        {
            // Implement user deletion here
            // If we handle user deletion, return a real message
        }
        else if (message.Type == ActivityTypes.ConversationUpdate)
        {
            // Handle conversation state changes, like members being added and removed
            // Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info
            // Not available in all channels
        }
        else if (message.Type == ActivityTypes.ContactRelationUpdate)
        {
            // Handle add/remove from contact lists
            // Activity.From + Activity.Action represent what happened
        }
        else if (message.Type == ActivityTypes.Typing)
        {
            // Handle knowing tha the user is typing
        }
        else if (message.Type == ActivityTypes.Ping)
        {
        }

        return null;
    }
}
```

コメントにもありますが、MessagesController.Post メソッドが、Web API "api/Messages" への POST 要求となります。
ユーザーが入力したチャット クライアントからの文字列等のメッセージは、"api/Messages" への POST 要求としてここで処理します。

また、テンプレートで記述されているコードでは、RootDialog クラスへ処理を渡しています。

この RootDialog クラスは、Microsoft.Bot.Builder.Dialogs.IDialog インターフェースを継承したクラスです。会話の単位毎に、IDialog を継承したクラスを定義し、会話内の処理を定義します。

次に、[Dialogs] - [RootDialog.cs] のソース コードを確認します。

```csharp:RootDialog.cs

[Serializable]
public class RootDialog : IDialog<object>
{
    public Task StartAsync(IDialogContext context)
    {
        context.Wait(MessageReceivedAsync);

        return Task.CompletedTask;
    }

    private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
    {
        var activity = await result as Activity;

        // calculate something for us to return
        int length = (activity.Text ?? string.Empty).Length;

        // return our reply to the user
        await context.PostAsync($"You sent {activity.Text} which was {length} characters");

        context.Wait(MessageReceivedAsync);
    }
}
```

IDialog.StartAsync をオーバーライドした RootDialog.StartAsync メソッドが、MessagesController.Post メソッド内の Conversation.SendAsync メソッドからコールされます。
さらに、RootDialog.StartAsync 内では、RootDialog.MessageReceivedAsync がコールされます。

RootDialog.MessageReceivedAsync では、テキストメッセージの文字列長を取得し、返信する処理が定義されています。

[デバッグ] - [デバッグの開始]、もしくは、以下 [デバッグの開始] ボタンから、デバッグ実行を開始します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/d20c552f-fc19-36f0-8239-6e0df72f55b1.png)

ブラウザが起動し、以下のような画面が起動されます。接続先の localhost のポート番号は、環境によって様々です。
このポート番号は、Bot Framework Emulator で利用するので、メモください。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/6b2447be-8db4-1118-aab3-534fc7504f4c.png)

デバッグ実行のまま、Bot Framework Emulator を起動します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/4b90fbe0-9376-f983-680a-a9b06c1027a0.png)

ファイヤーウォールの警告がでる場合は、利用するネットワークに応じて、[アクセスを許可する] を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/ea263eaa-61ec-c0e9-50ae-99ce55e4fd01.png)

"Enter your endpoint URL" 部分に、以下 URL を入力し、[CONNECT] を選択します。

http://localhost:デバッグ実行時に接続したポート番号/api/messages

![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/d570a8af-a24d-4e5f-88a0-68678d889751.png)

[Type your message...] 部に、任意の文字列を入力し、文字列長がメッセージとともに返信されることを確認します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/0bbfe503-d7f8-52c8-dd34-76d5ad6803e6.png)

# bot のクラウドへの公開

## 事前準備
bot をクラウドへ公開するには、以下が必要となります。
bot アプリケーションは、Azure 以外でも ASP.NET Web API が動作する環境であれば何を利用しても構いませんが、ここでの手順では、Azure を利用します。

- Microsoft アカウント
- Azure サブスクリプション

Azure アカウントをお持ちでない場合は、以下から、無料で Azure アカウントを作成できます。

- [無料の Azure アカウントを作成](https://azure.microsoft.com/ja-jp/free/)

アカウント作成時にクレジット カードが必要となりますが、有償のサービスを利用しなければ課金されることはありません。
bot の公開先の　App Service においても有償版を明示的に選ばなければ、課金されることはありません。

##bot 開発ポータルへの bot の登録

以下 bot 開発ポータルへアクセスし、任意の Microsoft アカウントでサインインします。

- https://dev.botframework.com/

[My bots] - [Register] を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/3ac1ed08-9efc-c917-e6d3-8e0363a623bd.png)

[Display name] に表示名、[Bot handle] に一意な名前、[Long description] に説明文を入力します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/ce460c8d-e8a7-09c9-dbaa-6e042e8e6828.png)

(*) [Bot handle] の値は、後ほど利用するのでメモしておいてください。

[Create Microsoft App ID and password] を選択し、"アプリ ID" を発行します。
[アプリ パスワードを生成して続行] を選択します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/5991ccab-24a3-98cf-91ca-9e197bd3b4df.png)

パスワードが生成されるので、パスワードをメモしておきます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/b3ab2918-5036-f2df-92df-c4a82980e34b.png)

アプリ ID も後ほど利用するので、メモしておきます。
[終了してボットのフレームワークに戻る] を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/2df08737-0e46-b149-7edd-66a428765c68.png)

[I agree to the Terms of Use, Privacy Statement, and Code of Conduct for the Microsoft Bot Framework (Preview).] にチェックを入れ、[Register] を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/662a73b4-40c8-9cbd-f6c4-0082186e20e4.png)

bot の登録が完了します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/9acc152d-6ad8-eb6c-f1f1-d2fd213cf825.png)

##bot のクラウドへの発行

ソースコード上の "Web.config" を開きます。
BotId へ "bot handle", MicrosoftAppId へ "アプリ ID", MicrosoftAppPassword へ対応する "パスワード" をそれぞれ設定し、Web.config ファイルを保存します。

```xml:Web.config
<?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=301879
  -->
<configuration>
  <appSettings>
    <!-- update these with your BotId, Microsoft App Id and your Microsoft App Password-->
    <add key="BotId" value="YourBotId" />
    <add key="MicrosoftAppId" value="" />
    <add key="MicrosoftAppPassword" value="" />
  </appSettings>

</configuration>
```

次に、[ソリューション エクスプローラー] のプロジェクトを選択し、右クリックメニューから、[公開...] を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/33fbf574-683b-d544-258b-d5f0655afe8c.png)

[Microsoft Azure App Services(A)]、[新規作成] を選択し、[発行] を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/4ed82d22-77bb-a112-bc3f-4797e54daf15.png)

[App Service の作成] において、各項目を設定します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/f5bb8ecb-6eac-9358-d672-8170d621582f.png)

[App Service プラン] においては、有償プランが選択されている場合があるので、無償プランを利用する場合は、[サイズ] を無料に設定した新しいプランを作成します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/79bbb819-066b-cf65-f09b-d252aac99a56.png)

[作成] を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/c9e4f376-43f1-4008-cb74-dd769cbefdc3.png)

Azure 上への発行が完了すると、ブラウザが起動します。
ここで、デバッグ実行時と同様に接続先の**ホスト名**をメモしておきます。
このホスト名を含めた "https://ホスト名/api/messages" が、、**bot のエンドポイント**となります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/3ae6a045-985e-7d3f-f0a9-ddcafbe5c813.png)

以下 bot 開発ポータル "https://dev.botframework.com/
" へアクセスし、[My bots] から、先ほど登録した bot を選択し、右上メニュー部 [SETTINGS] を選択します。

[Configuration] - [Messaging endpoint] へ、**bot のエンドポイント**を入力し、[Save changes] を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/24751b1f-3104-5fff-ecd4-7df49ab24b0e.png)

次に、右上メニュー部の [Test] ボタンを選択します。
[Chat] ウインドウが起動するので、任意の文字列を入力し、文字列長がメッセージとともに返信されることを確認します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/a8221546-1566-a9fd-00fd-ff5908fb636a.png)

これで、Azure 上にホストした bot アプリケーションを bot 開発ポータルへ登録し、メッセージのやり取りの動作確認を行うことができました。

# Cortana チャネルの設定

[Add a channel] から、[Cortana] を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/64d8b833-3986-ffd5-cfd2-ed77c4082bd2.png)

[Save] を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/2ab1ee72-2080-6999-2386-bbc5ad4d5eb5.png)

[Connect to channels] から、[Manage in Cortana dashboard] を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/30e3c5dc-5575-601e-09ef-bb301f9d0aa4.png)

プレビュープログラムへの同意を行います。[I accept] を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/18174fd4-b569-36db-1777-764306dfecce.png)

Cortana ダッシュボードが表示されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/75786db2-e850-4a10-65ed-4929d517f8f5.png)

[Deploy to group] を選択し、テスト目的で、公開先のアカウントを入力します。複数のユーザーに公開する場合は、セミコロン(;)でセパレートします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/f9ac8d87-aba2-2bd9-3cd8-7ad4ed08f9ae.png)

[Deploy] を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/59e9b38d-db48-371a-9686-474ccfe6b271.png)

ここで、表示される URL へアクセスします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/c9d73c32-1726-a3cf-b56c-1184f697ee2c.png)

[Accept] を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/5b8c24b2-a457-23a3-1a4d-522aebe08f97.png)
テスト グループへ追加された旨のメッセージが表示されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/d154ce0c-cefa-bf27-6cae-5afa78b818d2.png)


## bot の音声対応

RootDialog クラスを変更し、音声出力の対応を行います。
IBotToUser インターフェースの拡張メソッド SayAsync を使います。第二引数が、音声出力の文字列となります。
ここでは、文字列出力と同様の値を音声出力に設定します。
以下の様にコードを変更し、変更を保存します。

```csharp:RootDialog.cs
[Serializable]
public class RootDialog : IDialog<object>
{
    public Task StartAsync(IDialogContext context)
    {
        context.Wait(MessageReceivedAsync);

        return Task.CompletedTask;
    }

    private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
    {
        var activity = await result as Activity;

        // calculate something for us to return
        int length = (activity.Text ?? string.Empty).Length;

        // return our reply to the user
        //await context.PostAsync($"You sent {activity.Text} which was {length} characters");
        string message = $"You sent {activity.Text} which was {length} characters";
        await context.SayAsync(message, message);


        context.Wait(MessageReceivedAsync);
    }
}
```
プロジェクトを選択し、[公開...] を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/3a4d6332-cbbe-9c90-08b9-3cd36d36bab2.png)

[発行] を選択し、Azure 上に変更したアプリケーションを配置します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/6f8dc0b2-e65b-20fa-24f6-459f68086d9a.png)

##Cortana による動作確認

現状の Cortana Skills Kit は、日本語対応ができていないため、英語版の Cortana を有効化します。

[設定] - [時刻と言語] - [地域と言語] を選択します。
[国または地域] - [米国] を選択します。
[言語] - [言語を追加する] を選択し、[English] を選択、[English (United States)] を選択します。
さらに、[English (United States] - [既定として設定する] を選択します。

[設定] - [時刻と言語] - [音声認識] - [音声認識の言語] から、[英語(米国)] を選択します。

OS から、サインアウトし、サインインし、英語版の Cortana が起動することを確認します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/baea0a35-bb0a-c477-88d4-06453418eea5.png)


Cortana を起動し、"ask ボット名 メッセージ" と音声入力します。
[I'll need your permission] - [Agree] を選択します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/e44bafb6-e967-eb1d-9c25-a86c3305b53a.png)

Cortana から音声でメッセージが返されることを確認します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/172205/bca3e5d8-88a3-c77d-6425-eeb93cdcdee2.png)

また、Android, iOS 等の Cortana を利用しても同様に動作することが確認できます。
もちろん、現状では、これらのデバイスにおいても英語版の Cortana でのみ動作します。

# 参考サイト

今回の手順においては、以下のサイトを参考に記載しました。

- [Create a bot with the Bot Builder SDK for .NET](https://docs.microsoft.com/en-us/bot-framework/dotnet/bot-builder-dotnet-quickstart)
- [Debug bots with the Bot Framework Emulator](https://docs.microsoft.com/en-us/bot-framework/debug-bots-emulator)
- [Deploy a bot to Azure from Visual Studio](https://docs.microsoft.com/en-us/bot-framework/deploy-bot-visual-studio)
- [The Cortana Skills Kit](https://docs.microsoft.com/en-us/cortana/getstarted)
- [Cortana skills development: Get started](https://channel9.msdn.com/events/Build/2017/B8030)