かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

Azure Bot Service で Microsoft Teams のボットにボタンで選択肢を出したい

これでハマったのでメモ。 もし、もっと楽な方法があったら教えてください。

やりたかったこと

以下のようなメッセージを生成したかった。

xxxxですか?
[ はい ] [ いいえ ]

質問に対して「はい」か「いいえ」をボタンで出したかった。

最初にやったこと

ConfirmPrompt の出番やろ。ということでサンプルの下のような感じで作ってみました。

BotBuilder-Samples/MultiTurnPromptsBot.cs at master · Microsoft/BotBuilder-Samples · GitHub

BotBuilder-Samples/MultiTurnPromptsBot.cs at master · Microsoft/BotBuilder-Samples · GitHub

エミュレーターで確認すると「はい」と「いいえ」のボタンが出てめでたしめでたし…だったのに Teams に繋いでみると

xxxxですか?
(1) Yes, (2) No

と出て数字をユーザーが手入力するものになってしまいました…なんでや…。

次にやったこと

それなら自分で好きな UI 組み立てて見せる!と思って AdapriveCards を試してみました。

adaptivecards.io

ここらへんで JSON 組み立ててサクッと完了。

actions に Action.Submit を指定しておいたらエミュレーターでは動いたんですが…。Teams に繋ぐとボタンを押しても反応なし。残念。(深追いはしてません)

最終的にやったこと

QnA Maker の ActiveLearning のサンプルボットが質問に対して曖昧な回答が複数あると選択肢を出すように動いていたので該当部分をチェックしてみると HeroCard を使ってました。

BotBuilder-Samples/CardHelper.cs at master · Microsoft/BotBuilder-Samples · GitHub

ということで最終的には以下のような Prompt を作成して ConfirmPrompt と差し替えて対応しました。

public class FeedbackPrompt : Prompt<bool>
{
    public FeedbackPrompt(string dialogId) : base(dialogId)
    {

    }

    protected override async Task OnPromptAsync(ITurnContext turnContext, IDictionary<string, object> state, PromptOptions options, bool isRetry, CancellationToken cancellationToken = default(CancellationToken))
    {
        var reply = turnContext.Activity.CreateReply();
        reply.Attachments = new List<Attachment>
        {
            new HeroCard
            {
                Text = Messages.AskingFeedbackMessage,
                Buttons = new List<CardAction>
                {
                    new CardAction { Title = Messages.Yes, Type = "imBack", Value = Messages.Yes },
                    new CardAction { Title = Messages.No, Type = "imBack", Value = Messages.No },
                },
            }.ToAttachment(),
        };

        await turnContext.SendActivityAsync(reply, cancellationToken);
    }

    protected override Task<PromptRecognizerResult<bool>> OnRecognizeAsync(ITurnContext turnContext, IDictionary<string, object> state, PromptOptions options, CancellationToken cancellationToken = default(CancellationToken))
    {
        switch (Messages.RemoveTag(turnContext.Activity.Text.Trim()))
        {
            case Messages.Yes:
                return Task.FromResult(new PromptRecognizerResult<bool>
                {
                    Succeeded = true,
                    Value = true,
                });
            case Messages.No:
                return Task.FromResult(new PromptRecognizerResult<bool>
                {
                    Succeeded = true,
                    Value = false,
                });
            default:
                return Task.FromResult(new PromptRecognizerResult<bool>
                {
                    Succeeded = false,
                });
        }
    }
}

追記

AdaptiveCards でボタンを押したときにメッセージを正しく返す方法について Microsoft MVP の Atushi Yokohama さんに Facebook で教えてもらいました。

docs.microsoft.com

Action に以下のような JSON を指定するらしい。私は data に文字列を指定してました。

{
  "type": "Action.Submit",
  "title": "Click me for imBack",
  "data": {
    "msteams": {
        "type": "imBack",
        "value": "Text to reply in chat"
    }
  }
}

まとめ

Bot Framework Service の Teams 対応部分が、もうちょっと頑張って各種 Prompt の見た目それっぽくしてくれたらうれしいなぁと思った今日この頃でした。

自分が見逃してるだけで何かいい感じに見せるオプションとかあるのかな?