Azure BotService を触った所感

## まえがき
2016年3月30日？に BotFramework が発表されてもう半年が過ぎた。
Azure に BotService なるサービス(現時点ではプレビュー)が生まれているのでファーストインプレッションを書いておく。

## 所感
- Bot開発で WebApp, APIApp, FunctionApp と、どのサービス使うべきか考えなくてよくなった。
- リリース直後の BotFramework 周りはボットの登録は別でやって・・・準備が面倒だったのがポータル内に集約されてラクになった
- コードがその場でいじれるので大したことのないBotならそのまま触れてよい
- とはいえプレビューなのでエディタの補完が効かない点や最後のハマりどころなど、不満はある

## 準備
[Azureポータル](https://portal.azure.com) から Bot Service を作成する。

![bot-service-1.png](https://qiita-image-store.s3.amazonaws.com/0/22059/9b62a77f-1bd8-728c-d8c6-8b47a7cd9662.png)

次に MS の App ID を取得する。
ID とパスワードが発行されるので、画面内に貼り付けるだけ。

![bot-service-1-2.png](https://qiita-image-store.s3.amazonaws.com/0/22059/aecfc366-84a4-390f-b8c7-1adb40197455.png)

最後に言語と Bot パターンを選択する。
Bot 開発にありがちなピザの注文は Form。
今回も Form で作成する。

![bot-service-1-3.png](https://qiita-image-store.s3.amazonaws.com/0/22059/3940ba6c-3165-0368-8ba2-f285e720531b.png)

ということで BotService が作成された。

## Bot開発
BotService を開くと、いかにもコードがいじれそうな画面が開く。

![bot-service-2.png](https://qiita-image-store.s3.amazonaws.com/0/22059/5ef56079-62f1-65a2-0362-e4fb08d17995.png)

Form の場合は `BasicForm.csx` 9割、`MainDialog.csx` を1割見れば大体どうにかなる。

今回は勤怠連絡のためのBotを書いてみた。

```csharp:BasicForm.csx
public enum KintaiOptions
{
	一日休み = 1,
	遅刻,
}

public enum ReasonOptions
{
	交通状況 = 1,
	体調不良,
	その他の問題,
}

[Serializable]
public class BasicForm
{
    public BasicForm()
    {
        /*
        Kintai = KintaiOptions.遅刻;
        Reason = ReasonOptions.交通状況;
        Comment = "特になし";
        */
    }
    
    [Prompt("今日はいかがしましたか？{||}")]
	public KintaiOptions Kintai;

    [Prompt("どうしちゃったの？{||}")]
	public ReasonOptions Reason;

	[Prompt("誰かに何か伝えておくことあるかな？")]
	public String Comment;

    public static IForm<BasicForm> BuildForm()
    {
        OnCompletionAsyncDelegate<BasicForm> processConfirm = async (context, state) =>
        {
            var message = context.MakeMessage();
            switch (state.Reason) {
                case ReasonOptions.交通状況:
                    message.Text = "落ち着いて来てくださいね！";
                    await context.PostAsync(message);
                    break;
                case ReasonOptions.体調不良:
                    message.Text = "いのちをだいじに！お大事にしてください〜";
                    await context.PostAsync(message);
                    break;
            }
        };
        return new FormBuilder<BasicForm>()
            .Message("こんにちは。勤怠Botです。")
            .Field(nameof(Kintai))
            .Field(nameof(Reason))
            .Field(nameof(Comment))
            .AddRemainingFields()
            .Confirm(async (state) => {
                return new PromptAttribute($"「{state.Reason}」が原因で「{state.Kintai}」です。連絡事項は「{state.Comment}」。この内容で伝えていいですか？(y or n)");
            })
            .OnCompletion(processConfirm)
            .Build();
    }

    public static IFormDialog<BasicForm> BuildFormDialog(FormOptions options = FormOptions.PromptInStart)
    {
        // Generated a new FormDialog<T> based on IForm<BasicForm>
        return FormDialog.FromForm(BuildForm, options);
    }
}
```


```csharp:MainDialog.csx
    private async Task FormComplete(IDialogContext context, IAwaitable<BasicForm> result)
    {
        try
        {
            var form = await result;
            if (form == null)
            {
                await context.PostAsync("空の申請がリクエストされました。はじめからやり直してください。");
            }
        }
        catch (OperationCanceledException)
        {
            await context.PostAsync("申請がキャンセルされました。はじめからやり直してください。");
        }

        context.Wait(MessageReceivedAsync);
    }
```

Form の選択肢の変数を初期化しないと警告が表示されるが、初期化するとユーザの入力を待たずに選択されている扱いになってしまうという罠がある。
はじめは未選択、という状態になっているためで、これが列挙型の開始値を`1`にしなければならない理由だと思われる。

## 設定まわり
どのチャットサービスと連携するかは `Channels` で設定する。
残念ながら LINE はない。

![bot-service-6.png](https://qiita-image-store.s3.amazonaws.com/0/22059/57022ec6-7f7c-6977-5047-bf74d345da50.png)

あとは FunctionApp そのものをいじる場合は `Settings` からいじる。

![bot-service-3.png](https://qiita-image-store.s3.amazonaws.com/0/22059/ab76f7df-0ee3-f5b6-ab17-29f150d66c07.png)

![bot-service-4.png](https://qiita-image-store.s3.amazonaws.com/0/22059/5bbaea18-9a1e-c3d2-81db-8e75f5e9e47e.png)

![bot-service-5.png](https://qiita-image-store.s3.amazonaws.com/0/22059/8010b6ff-edf7-a2b6-f8e7-3544bae9c433.png)

開発が進めばあとは公開するだけ。

## ハマりどころ
しばらく開いているとコードエディタで Save しても裏側の FunctionApp が再読込しなくなる。
ブラウザを更新すればこの状態は復活する。
おかしいときは FunctionApp を再起動すれば直る。