我想我已经弄明白了,这更多是关于调整现有的不使用对话框的机器人来添加一个。首先,我从示例中添加了一个稍作修改的身份验证对话框:
public class AuthDialog : ComponentDialog
{
public AuthDialog()
: base(nameof(AuthDialog))
{
AddDialog(new OAuthPrompt(
nameof(OAuthPrompt),
new OAuthPromptSettings
{
ConnectionName = "botTeamsAuth",
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 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)
{
var tokenResponse = (TokenResponse)stepContext.Result;
if (tokenResponse?.Token != null)
{
// do something with graph here
GraphServiceClient client = iceTeamsBotState.GetGraphClient(tokenResponse.Token);
var messages = await client.Me.MailFolders["inbox"].Messages.Request(new List<Microsoft.Graph.QueryOption> { new Microsoft.Graph.QueryOption("$search", $"\"subject:test\"") }).GetAsync();
await stepContext.Context.SendActivityAsync($"Found {messages.Count} emails");
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);
}
}
然后,我需要改变我的机器人类:
public class myBot : IBot
到这里
public class myBot<T> : TeamsActivityHandler where T:Dialog
另外,我需要确保清单中的 validDomains 将“token.botframework.com”添加到列表中(我之前错过了这一步)。
最后我在 ConfigureServices 中做了这个改变:
//Add the auth dialog
services.AddSingleton<AuthDialog>();
services.AddBot<myBot<AuthDialog>>(options =>
{
options.CredentialProvider = new SimpleCredentialProvider(botConfig.BotID, botConfig.BotSecret);
options.OnTurnError = async (context, exception) =>
{
_log.Error("Exception caught-OnTurnError: ", exception);
await context.SendActivityAsync("Sorry, it looks like something went wrong.");
};
});
注意将对话框注册为单例,然后将模板参数(可能代表主对话框)添加到机器人。然后,在我的 OnTurnAsync 代码中,我能够处理几个测试输入:
public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
...
else if (turnContext.Activity.Type == ActivityTypes.Message && turnContext.Activity.Text == "logout")
{
var dialogState = _accessors.ConversationState.CreateProperty<DialogState>(nameof(DialogState));
var dialogSet = new DialogSet(dialogState);
dialogSet.Add(new AuthDialog());
DialogContext dc = await dialogSet.CreateContextAsync(turnContext, cancellationToken);
var botAdapter = (BotFrameworkAdapter)dc.Context.Adapter;
await botAdapter.SignOutUserAsync(dc.Context, "botTeamsAuth", null, cancellationToken);
await turnContext.SendActivityAsync($"logged out of graph");
}
else if (turnContext.Activity.Type == ActivityTypes.Message && turnContext.Activity.Text == "login")
{
var dialogState = _accessors.ConversationState.CreateProperty<DialogState>(nameof(DialogState));
var dialogSet = new DialogSet(dialogState);
dialogSet.Add(new AuthDialog());
DialogContext dc = await dialogSet.CreateContextAsync(turnContext, cancellationToken);
var turnResult = await dc.BeginDialogAsync("AuthDialog");
await _accessors.ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
}
else if (turnContext.Activity.Type == ActivityTypes.Invoke && turnContext.Activity.Name == "signin/verifyState")
{
var dialogState = _accessors.ConversationState.CreateProperty<DialogState>(nameof(DialogState));
var dialogSet = new DialogSet(dialogState);
dialogSet.Add(new AuthDialog()); //this is counterintuitive, but it gets around the issue where I get the dialog missing exception
DialogContext dc = await dialogSet.CreateContextAsync(turnContext, cancellationToken);
var turnResult = await dc.ContinueDialogAsync();
await _accessors.ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
}
是的,那里有一些重复的代码,但为了清楚起见,我暂时保持原样。至少对我来说似乎有点违反直觉的是更新 DialogState 和 DialogSet 以检索现有的状态成员,但这似乎是它的工作原理。在此示例中,我只是从用户收件箱中提取几条消息来验证连接,但它确实按预期工作。如果用户已经登录,则立即返回响应,如果用户需要进行身份验证,则发送登录卡并返回令牌响应。
无论如何,我想我现在已经畅通无阻,希望这个解决方案可以帮助处于类似位置的其他人。如果您想使用“魔术代码”身份验证流程,@Hilton 在他的回答中提到的方法也将起作用。不过在那种情况下。你会调用这样的东西来获取登录链接:
var adapter = turnContext.Adapter as BotFrameworkAdapter;
string url = await adapter.GetOauthSignInLinkAsync(turnContext, "botTeamsAuth");
await turnContext.SendActivityAsync($"click here to sign in {url}");
然后从用户那里取回一个数字代码作为消息,您可以使用他的回答中提到的 GetUserTokenAsync() 上交该消息。不过,卡片登录似乎更简洁一些,因为它不需要弹出外部登录窗口,并且 OAuthPrompt 对话框将令牌检索/登录包装为一个步骤。