登录卡提示仅一次,之后他们不再提示用户对机器人的任何输入登录卡
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;
}
}
处理此问题的简单方法是重新提示文本,告诉用户单击已发送的按钮。您可以使用 PromptOptions
的 RetryPrompt
属性 来执行此操作。 (请注意,如果机器人发送输入 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,您就可以将其用于提示选项的 Prompt
和 RetryPrompt
属性:
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
将看到空令牌响应,因此它将知道它需要重新启动对话。
我使用 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;
}
}
处理此问题的简单方法是重新提示文本,告诉用户单击已发送的按钮。您可以使用 PromptOptions
的 RetryPrompt
属性 来执行此操作。 (请注意,如果机器人发送输入 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,您就可以将其用于提示选项的 Prompt
和 RetryPrompt
属性:
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
将看到空令牌响应,因此它将知道它需要重新启动对话。