登录卡提示仅一次,之后他们不再提示用户对机器人的任何输入登录卡

Sign in card prompt only one time after that their no more prompt of sign in card on any input from user to bot

我使用 c# 框架 v4 制作了机器人。它已使用 azure ad 进行了身份验证。我第一次向用户发送消息时,它提示我登录提示,但如果我没有登录,下次我向机器人发送消息时,它不会提示我登录卡 again.I 想要,即使用户不登录,下次用户向机器人发送输入时,签名卡应该再次提示 模拟器中的图像

**用于广告认证的代码*

public class MainDialog : ComponentDialog
{
    private readonly IBotServices _botServices;
    protected readonly ILogger _logger;
    private readonly UserState _userState;

    private readonly string _connectionName;

    private readonly IConfiguration _configuration;
    public MainDialog(IConfiguration configuration,ILogger<MainDialog> logger, IBotServices botServices)
        : base(nameof(MainDialog))
    {
        _configuration = configuration;
        _logger = logger;
        _botServices = botServices ?? throw new System.ArgumentNullException(nameof(botServices));
        _connectionName = configuration["ConnectionName"];

        AddDialog(new OAuthPrompt(
          nameof(OAuthPrompt),
          new OAuthPromptSettings
          {
              ConnectionName = configuration["ConnectionName"],
              Text = "Please Sign In",
              Title = "Sign In",
              Timeout = 300000, // User has 5 minutes to login (1000 * 60 * 5)
          }));

        AddDialog(new ConfirmPrompt(nameof(ConfirmPrompt)));

        AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
        AddDialog(new luisandqnamakerDialog(_botServices,_configuration,_logger));
        AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
        {
            PromptStepAsync,
            LoginStepAsync             
        }));

        // The initial child Dialog to run.
        InitialDialogId = nameof(WaterfallDialog);
    }

    private async Task<DialogTurnResult> PromptStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
    {
        return await stepContext.BeginDialogAsync(nameof(OAuthPrompt), null, cancellationToken);
    }

    private async Task<DialogTurnResult> LoginStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
    {

        // Get the token from the previous step. Note that we could also have gotten the
        // token directly from the prompt itself. There is an example of this in the next method.
        var tokenResponse = (TokenResponse)stepContext.Result;
        if (tokenResponse != null)
        {
            if (IsAuthCodeStep(stepContext.Context.Activity.Text))
            {
                await stepContext.Context.SendActivityAsync(MessageFactory.Text("You are now logged in."), cancellationToken);
                return await stepContext.NextAsync();
            }
            else
            {
                 await stepContext.PromptAsync(nameof(luisandqnamakerDialog), new PromptOptions { Prompt = MessageFactory.Text("Would you like to ask your question?") }, cancellationToken);
                return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
            }
        }           

        await stepContext.Context.SendActivityAsync(MessageFactory.Text("Login was not successful please try again."), cancellationToken);

        return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
    }

    private bool IsAuthCodeStep(string code)
    {
        if (string.IsNullOrEmpty(code) || !code.Length.Equals(6)) return false;
        if (!int.TryParse(code, out int result)) return false;
        if (result > 1) return true;                
        return false;
    }


    protected override async Task<DialogTurnResult> OnBeginDialogAsync(DialogContext innerDc, object options, CancellationToken cancellationToken = default(CancellationToken))
    {
        var result = await InterruptAsync(innerDc, cancellationToken);
        if (result != null)
        {
            return result;
        }

        return await base.OnBeginDialogAsync(innerDc, options, cancellationToken);
    }

    protected override async Task<DialogTurnResult> OnContinueDialogAsync(DialogContext innerDc, CancellationToken cancellationToken = default(CancellationToken))
    {
        var result = await InterruptAsync(innerDc, cancellationToken);
        if (result != null)
        {
            return result;
        }

        return await base.OnContinueDialogAsync(innerDc, cancellationToken);
    }

    private async Task<DialogTurnResult> InterruptAsync(DialogContext innerDc, CancellationToken cancellationToken = default(CancellationToken))
    {
        if (innerDc.Context.Activity.Type == ActivityTypes.Message)
        {
            var text = innerDc.Context.Activity.Text.ToLowerInvariant();

            if (text == "logout")
            {
                // The bot adapter encapsulates the authentication processes.
                var botAdapter = (BotFrameworkAdapter)innerDc.Context.Adapter;
                await botAdapter.SignOutUserAsync(innerDc.Context, _connectionName, null, cancellationToken);
                await innerDc.Context.SendActivityAsync(MessageFactory.Text("You have been signed out."), cancellationToken);
                return await innerDc.CancelAllDialogsAsync(cancellationToken);
            }
        }

        return null;
    }
}

处理此问题的简单方法是重新提示文本,告诉用户单击已发送的按钮。您可以使用 PromptOptionsRetryPrompt 属性 来执行此操作。 (请注意,如果机器人发送输入 activity,这将不起作用。)

return await stepContext.BeginDialogAsync(
    nameof(OAuthPrompt),
    new PromptOptions
    {
        RetryPrompt = MessageFactory.Text("Please click the sign-in button.")
    }, cancellationToken);

如果您真的想再次使用整张卡片重新提示,那么您有两个选择,它们都有点复杂。

选项 1:自己创建卡片

OAuth 提示使用的实际卡片是在私有方法中生成的,因此您无法调用该代码。您必须查看 source code 并将其复制供您自己使用:

// Ensure prompt initialized
if (prompt == null)
{
    prompt = Activity.CreateMessageActivity();
}

if (prompt.Attachments == null)
{
    prompt.Attachments = new List<Attachment>();
}

// Append appropriate card if missing
if (!ChannelSupportsOAuthCard(turnContext.Activity.ChannelId))
{
    if (!prompt.Attachments.Any(a => a.Content is SigninCard))
    {
        var link = await adapter.GetOauthSignInLinkAsync(turnContext, _settings.OAuthAppCredentials, _settings.ConnectionName, cancellationToken).ConfigureAwait(false);
        prompt.Attachments.Add(new Attachment
        {
            ContentType = SigninCard.ContentType,
            Content = new SigninCard
            {
                Text = _settings.Text,
                Buttons = new[]
                {
                    new CardAction
                    {
                        Title = _settings.Title,
                        Value = link,
                        Type = ActionTypes.Signin,
                    },
                },
            },
        });
    }
}
else if (!prompt.Attachments.Any(a => a.Content is OAuthCard))
{
    var cardActionType = ActionTypes.Signin;
    string signInLink = null;

    if (turnContext.Activity.IsFromStreamingConnection())
    {
        signInLink = await adapter.GetOauthSignInLinkAsync(turnContext, _settings.OAuthAppCredentials, _settings.ConnectionName, cancellationToken).ConfigureAwait(false);
    }
    else if (turnContext.TurnState.Get<ClaimsIdentity>("BotIdentity") is ClaimsIdentity botIdentity && SkillValidation.IsSkillClaim(botIdentity.Claims))
    {
        // Force magic code for Skills (to be addressed in R8)
        signInLink = await adapter.GetOauthSignInLinkAsync(turnContext, _settings.ConnectionName, cancellationToken).ConfigureAwait(false);
        cardActionType = ActionTypes.OpenUrl;
    }

    prompt.Attachments.Add(new Attachment
    {
        ContentType = OAuthCard.ContentType,
        Content = new OAuthCard
        {
            Text = _settings.Text,
            ConnectionName = _settings.ConnectionName,
            Buttons = new[]
            {
                new CardAction
                {
                    Title = _settings.Title,
                    Text = _settings.Text,
                    Type = cardActionType,
                    Value = signInLink
                }
            }
        }
    });
}

您会注意到此代码使用另一个名为 ChannelSupportsOAuthCard 的私有方法,如果频道不支持 OAuth 卡,则它会改用登录卡。您没有提到您使用的是什么渠道,所以我在这里包括了这两种情况,但是如果您知道您使用的所有渠道都支持 OAuth 卡,那么您可以只使用 OAuth 卡代码。或者,如果您不希望您的任何频道使用 OAuth 卡,那么您可以只使用登录卡代码。一旦您创建了包含卡片的 activity,您就可以将其用于提示选项的 PromptRetryPrompt 属性:

return await stepContext.BeginDialogAsync(
    nameof(OAuthPrompt),
    new PromptOptions
    {
        Prompt = prompt,
        RetryPrompt = prompt
    }, cancellationToken);

选项 2:重新启动对话框

如果您真的不想自己创建卡片,为此目的使用内置代码的唯一方法是再次调用 BeginDialogAsync。有几种方法可以解决这个问题,但我会尝试使用最简单的方法。

看起来您在 LoginStepAsync 中已经有一些 "try again" 功能,您可以在其中结束对话框,我认为您调用 MainDialog 的任何代码都会在下一回合。如果你不想等待下一个回合,那么你可以使用一个小技巧,将对话框替换为自身:

await stepContext.Context.SendActivityAsync(MessageFactory.Text("Login was not successful please try again."), cancellationToken);

return await stepContext.ReplaceDialogAsync(nameof(WaterfallDialog), cancellationToken: cancellationToken);

当然,要进入 LoginStepAsync 步骤,您需要一种方法来在用户键入内容时结束 OAuth 提示。您可以通过从提示的验证器 returning true 来做到这一点:

AddDialog(new OAuthPrompt(
  nameof(OAuthPrompt),
  new OAuthPromptSettings
  {
      ConnectionName = configuration["ConnectionName"],
      Text = "Please Sign In",
      Title = "Sign In",
      Timeout = 300000, // User has 5 minutes to login (1000 * 60 * 5)
  }, (_, _) => Task.FromResult(true)));

此示例将使提示的验证器始终为 return true,因此无论如何都会在下一轮结束提示。如果需要,您可以将自己的逻辑放入验证器中,以便它检查 activity 和识别器结果并据此做出决定。但是一旦提示结束并且瀑布继续下一步,LoginStepAsync 将看到空令牌响应,因此它将知道它需要重新启动对话。