5

如何在之前的 BotChat 对话中禁用输入/提交按钮操作 - Microsoft Bot Framework (C#) 中的 AdaptiveCards

4

1 回答 1

3

我想象您想向用户显示一张只使用一次的卡片,例如本例中所示的日历提醒。

机器人主要意味着具有与人类相同的通道访问权限,因此它们无法返回并修改已发送的消息(除非特定通道允许像 Slack 那样进行编辑)。虽然您无法禁用卡片中已经包含在对话历史记录中的按钮,但您可以更改机器人响应该卡片生成的消息的方式。您要做的是跟踪按钮是否已被单击,然后在随后单击该按钮时做出不同的响应。

下面是一些可以以三种方式响应消息的 Dialog 代码的基本示例。如果您键入任何消息并将其发送到机器人,它将显示一张带有按钮的卡片。如果你点击按钮,它会说“你做到了!” 以及您单击的按钮的 ID。如果您再次单击相同的按钮,它将显示“您已经这样做了!” 再次附上身份证。

/// <summary>
/// You'll want a label like this to identify the activity
/// that gets generated in response to your submit button.
/// </summary>
private const string DO_SOMETHING = "DoSomething";

/// <summary>
/// This is passed into context.Wait() in your StartAsync method.
/// </summary>
private async Task MessageReceivedAsync(IDialogContext context,
    IAwaitable<IMessageActivity> result)
{
    var msg = await result;

    if (!string.IsNullOrWhiteSpace(msg.Text))
    {
        // If msg.Text isn't null or white space then that means the user
        // actually typed something, and we're responding to that with a card.
        var reply = context.MakeMessage();
        var attachment = MakeAdaptiveCardAttachment();
        reply.Attachments.Add(attachment);

        await context.PostAsync(reply);
    }
    else
    {
        // If the user didn't type anything then this could be an activity
        // that was generated by your submit button. But we want to make sure
        // it is by checking msg.Value.
        dynamic value = msg.Value;

        try
        {
            // If value doesn't have a type then this will throw a RuntimeBinderException
            if (value != null && value.type == DO_SOMETHING)
            {
                string id = value.id;

                // Check the ID to see if that particular card has been clicked before.
                if (!context.PrivateConversationData.ContainsKey(id))
                {
                    // This is how your bot will keep track of what's been clicked.
                    context.PrivateConversationData.SetValue(id, true);

                    await context.PostAsync("You did it! " + id);
                }
                else
                {
                    await context.PostAsync("You already did that! " + id);
                }
            }
        }
        catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException)
        {
            // Respond to messages that don't have values with a type (or id).
        }
    }

    context.Wait(MessageReceivedAsync);
}

private static Attachment MakeAdaptiveCardAttachment()
{
    var card = new AdaptiveCard();
    // We need to identify this specific card if we want to allow multiple
    // instances of the card to be clicked.
    // A timestamp could work but a GUID will do.
    var cardId = Guid.NewGuid().ToString();

    card.Body.Add(new TextBlock() { Text = cardId });

    card.Actions.Add(new SubmitAction()
    {
        Title = "Do something",
        // The data we put inside this action will persist.
        // I've found setting DataJson to be more reliable than using the Data property.
        // Note that if your WebApiConfig.cs has a CamelCasePropertyNamesContractResolver
        // (which is a default) and you use capitalized (Pascal case) identifiers,
        // they may be converted to camel case and you won't be able to retrieve
        // the data with the same identifiers.
        DataJson = JsonConvert.SerializeObject(new
        {
            // We need a type to differentiate this action from other actions.
            type = DO_SOMETHING,
            // We need an id to differentiate this card from other cards.
            id = cardId,
        }),
    });

    return new Attachment()
    {
        ContentType = AdaptiveCard.ContentType,
        Content = card,
    };
}

这是 Bot Framework Emulator 中的样子。请注意,即使您单击了一张卡片并且无法从该卡片获得第一个响应,您仍然可以从另一张卡片获得第一个响应。

在此处输入图像描述

于 2018-08-09T19:35:20.493 回答