Подсказка для входа в карту только один раз, после чего их больше не будет запрашивать вход карты при любом входе пользователя в бот - PullRequest
0 голосов
/ 18 февраля 2020

Я сделал бот с использованием framework v4, используя c#. Он был аутентифицирован с использованием azure объявления. В первый раз, когда я отправляю пользователю сообщение, он запрашивает у меня вход в систему, но если я не вошел в систему, а в следующий раз, когда я отправляю сообщение боту, он не запрашивает у меня вход в систему снова. Я хочу это, даже если пользователь не подписывает в следующий раз, когда пользователь отправит ввод в бот, знаковая карта должна снова запросить изображение в эмуляторе enter image description here

** Код, используемый для аутентификации рекламы *

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;
    }
}

1 Ответ

0 голосов
/ 18 февраля 2020

Простой способ справиться с этим - просто перепроверить текст, который говорит пользователю нажать на кнопку, которая уже была отправлена. Вы можете сделать это с помощью свойства RetryPrompt PromptOptions. (Обратите внимание, что это не сработает, если бот отправит печатное действие.)

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

Если вы действительно хотите повторно выполнить повторную компоновку со всей картой, у вас есть два варианта, и оба они довольно сложны.

Вариант 1. Создайте карту самостоятельно

Фактическая карта, используемая в приглашении OAuth, создается в закрытом методе, поэтому вы не можете вызвать этот код. Вам нужно взглянуть на исходный код и скопировать его для собственного использования:

// 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, вы можете просто использовать код карты входа. Создав действие с картой в нем, вы можете использовать его как для свойств Prompt, так и RetryPrompt параметров вашей подсказки:

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

Вариант 2. Перезапустите диалоговое окно

Если вы действительно не хотите беспокоиться о создании карты самостоятельно, единственный способ использовать встроенный код для этой цели - снова вызвать BeginDialogAsync. Есть несколько способов подойти к этому, но я постараюсь go по простейшему маршруту.

Похоже, у вас уже есть некоторые функции "попробуйте еще раз" в LoginStepAsync, где вы заканчиваете диалог, и я предположим, что любой код, который вы вызываете 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, когда пользователь что-то печатает. Вы можете сделать это, вернув 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)));

В этом примере валидатор приглашения всегда будет возвращать true и, таким образом, завершит запрос на следующем ходу, несмотря ни на что. Если вы хотите, вы можете поместить свой собственный лог c в валидатор, чтобы он проверял активность и результат распознавателя и принимал решение на основании этого. Но как только подсказка заканчивается, и водопад переходит к следующему шагу, LoginStepAsync увидит нулевой ответ токена и узнает, что ему нужно перезапустить диалог.

...