J'ai créé un bot à l'aide du framework v4 en utilisant c#. Il a été authentifié à l'aide d'azur ad. La première fois que j'envoie un message à l'utilisateur, il m'invite à me connecter, mais si je ne me suis pas connecté et la prochaine fois que j'envoie un message au bot, il ne m'invite plus à me connecter à la carte. Je le veux même si l'utilisateur ne se connecte pas dans , la prochaine fois que l'utilisateur envoie une entrée au bot, la carte de signature devrait à nouveau demander L'image dans l'émulateur entrez la description de l'image ici

**Le code utilisé pour l'authentification de l'annonce*

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;
    }
}
0
Amit Kumar 18 févr. 2020 à 13:20

1 réponse

Meilleure réponse

Le moyen le plus simple de gérer cela consiste simplement à réinviter avec un texte indiquant à l'utilisateur de cliquer sur le bouton qui a déjà été envoyé. Vous pouvez le faire avec la propriété RetryPrompt de PromptOptions. (Notez que cela ne fonctionnera pas si le bot envoie une activité de saisie.)

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

Si vous voulez vraiment réessayer avec toute la carte, vous avez deux options et elles sont toutes les deux assez compliquées.

Option 1 : Créez vous-même la carte

La carte réelle que l'invite OAuth utilise est générée dans une méthode privée, il n'y a donc aucun moyen pour vous d'appeler ce code. Vous devrez jeter un œil au code source et copiez-le pour votre propre usage :

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

Vous remarquerez que ce code utilise une autre méthode privée appelée ChannelSupportsOAuthCard, et si le canal ne prend pas en charge les cartes OAuth, il utilise à la place une carte de connexion. Vous n'avez pas mentionné le canal que vous utilisez, j'ai donc inclus les deux cas ici, mais si vous savez que tous les canaux que vous utilisez prennent en charge les cartes OAuth, vous pouvez simplement utiliser le code de la carte OAuth. Alternativement, si vous ne souhaitez pas que l'un de vos canaux utilise des cartes OAuth, vous pouvez simplement utiliser le code de la carte de connexion. Une fois que vous avez créé l'activité contenant la carte, vous pouvez l'utiliser à la fois pour les propriétés Prompt et RetryPrompt de vos options d'invite :

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

Option 2 : redémarrer la boîte de dialogue

Si vous ne voulez vraiment pas vous soucier de créer la carte vous-même, la seule façon d'utiliser le code intégré à cette fin est d'appeler à nouveau BeginDialogAsync. Il y a plusieurs façons d'aborder cela, mais je vais essayer de choisir la voie la plus simple.

Il semble que vous ayez déjà une fonctionnalité "réessayer" dans LoginStepAsync où vous terminez la boîte de dialogue et je suppose que le code que vous avez appelant le MainDialog le recommencera automatiquement au prochain tour. Si vous ne voulez pas attendre le prochain tour, vous pouvez utiliser une petite astuce pour remplacer un dialogue par lui-même :

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

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

Bien sûr, pour accéder à l'étape LoginStepAsync, vous aurez besoin d'un moyen de mettre fin à l'invite OAuth lorsque l'utilisateur tape quelque chose. Vous pouvez le faire en retournant true depuis le validateur de l'invite :

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

Cet exemple fera que le validateur de l'invite retournera toujours true et il mettra donc fin à l'invite au tour suivant, quoi qu'il arrive. Si vous le souhaitez, vous pouvez mettre votre propre logique dans le validateur afin qu'il vérifie l'activité et le résultat de la reconnaissance et décide en fonction de cela. Mais une fois que l'invite se termine et que la cascade passe à l'étape suivante, LoginStepAsync verra une réponse de jeton null et saura donc qu'il doit redémarrer la boîte de dialogue.

0
Kyle Delaney 18 févr. 2020 à 20:51