-
Notifications
You must be signed in to change notification settings - Fork 479
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
81 changed files
with
8,092 additions
and
750 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
66 changes: 66 additions & 0 deletions
66
prototypes/DialogToDialog/AuthenticationBot/AdapterWithErrorHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using System; | ||
using System.Threading.Tasks; | ||
using Microsoft.Bot.Builder; | ||
using Microsoft.Bot.Builder.Integration.AspNet.Core; | ||
using Microsoft.Bot.Connector; | ||
using Microsoft.Bot.Schema; | ||
using Microsoft.Extensions.Configuration; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace AuthenticationBot | ||
{ | ||
public class AdapterWithErrorHandler : BotFrameworkHttpAdapter | ||
{ | ||
public AdapterWithErrorHandler(IConfiguration configuration, ILogger<BotFrameworkHttpAdapter> logger, ConversationState conversationState = null) | ||
: base(configuration, logger) | ||
{ | ||
OnTurnError = async (turnContext, exception) => | ||
{ | ||
// Log any leaked exception from the application. | ||
logger.LogError($"Exception caught : {exception.Message}"); | ||
// Send a catch-all apology to the user. | ||
await turnContext.SendActivityAsync($"Sorry, it looks like something went wrong. \r\n{exception}"); | ||
if (conversationState != null) | ||
{ | ||
try | ||
{ | ||
// Delete the conversationState for the current conversation to prevent the | ||
// bot from getting stuck in a error-loop caused by being in a bad state. | ||
// ConversationState should be thought of as similar to "cookie-state" in a Web pages. | ||
await conversationState.DeleteAsync(turnContext); | ||
} | ||
catch (Exception e) | ||
{ | ||
logger.LogError(e, $"Exception caught on attempting to Delete ConversationState : {e.Message}"); | ||
} | ||
} | ||
// Send a trace activity, which will be displayed in the Bot Framework Emulator | ||
await SendTraceActivityAsync(turnContext, exception); | ||
}; | ||
} | ||
|
||
private static async Task SendTraceActivityAsync(ITurnContext turnContext, Exception exception) | ||
{ | ||
// Only send a trace activity if we're talking to the Bot Framework Emulator | ||
if (turnContext.Activity.ChannelId == Channels.Emulator) | ||
{ | ||
var traceActivity = new Activity(ActivityTypes.Trace) | ||
{ | ||
Label = "TurnError", | ||
Name = "OnTurnError Trace", | ||
Value = exception.Message, | ||
ValueType = "https://www.botframework.com/schemas/error" | ||
}; | ||
|
||
// Send a trace activity | ||
await turnContext.SendActivityAsync(traceActivity); | ||
} | ||
} | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
prototypes/DialogToDialog/AuthenticationBot/AuthenticationBot.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<Project Sdk="Microsoft.NET.Sdk.Web"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netcoreapp2.1</TargetFramework> | ||
<LangVersion>latest</LangVersion> | ||
<UserSecretsId>96d75115-b701-4599-bc9d-256590cdb08d</UserSecretsId> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.AspNetCore.App" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\..\libraries\integration\Microsoft.Bot.Builder.Integration.AspNet.Core\Microsoft.Bot.Builder.Integration.AspNet.Core.csproj" /> | ||
<ProjectReference Include="..\..\..\libraries\Microsoft.Bot.Builder.Dialogs\Microsoft.Bot.Builder.Dialogs.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<Content Update="appsettings.json"> | ||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||
</Content> | ||
</ItemGroup> | ||
</Project> |
53 changes: 53 additions & 0 deletions
53
prototypes/DialogToDialog/AuthenticationBot/Bots/AuthBot.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using System.Collections.Generic; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Bot.Builder; | ||
using Microsoft.Bot.Builder.Dialogs; | ||
using Microsoft.Bot.Schema; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace AuthenticationBot.Bots | ||
{ | ||
public class AuthBot<T> : DialogBot<T> | ||
where T : Dialog | ||
{ | ||
public AuthBot(ConversationState conversationState, UserState userState, T dialog, ILogger<DialogBot<T>> logger) | ||
: base(conversationState, userState, dialog, logger) | ||
{ | ||
} | ||
|
||
public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default) | ||
{ | ||
if (turnContext.Activity.Type == ActivityTypes.Invoke) | ||
{ | ||
await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken); | ||
} | ||
else | ||
{ | ||
await base.OnTurnAsync(turnContext, cancellationToken); | ||
} | ||
} | ||
|
||
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken) | ||
{ | ||
foreach (var member in turnContext.Activity.MembersAdded) | ||
{ | ||
if (member.Id != turnContext.Activity.Recipient.Id) | ||
{ | ||
await turnContext.SendActivityAsync(MessageFactory.Text("Welcome to AuthenticationBot. Type anything to get logged in. Type 'logout' to sign-out."), cancellationToken); | ||
} | ||
} | ||
} | ||
|
||
protected override async Task OnTokenResponseEventAsync(ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken) | ||
{ | ||
Logger.LogInformation("Running dialog with Token Response Event Activity."); | ||
|
||
// Run the Dialog with the new Token Response Event Activity. | ||
await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken); | ||
} | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
prototypes/DialogToDialog/AuthenticationBot/Bots/DialogBot.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Bot.Builder; | ||
using Microsoft.Bot.Builder.Dialogs; | ||
using Microsoft.Bot.Schema; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace AuthenticationBot.Bots | ||
{ | ||
// This IBot implementation can run any type of Dialog. The use of type parameterization is to allows multiple different bots | ||
// to be run at different endpoints within the same project. This can be achieved by defining distinct Controller types | ||
// each with dependency on distinct IBot types, this way ASP Dependency Injection can glue everything together without ambiguity. | ||
// The ConversationState is used by the Dialog system. The UserState isn't, however, it might have been used in a Dialog implementation, | ||
// and the requirement is that all BotState objects are saved at the end of a turn. | ||
public class DialogBot<T> : ActivityHandler | ||
where T : Dialog | ||
{ | ||
private readonly BotState _userState; | ||
|
||
public DialogBot(ConversationState conversationState, UserState userState, T dialog, ILogger<DialogBot<T>> logger) | ||
{ | ||
ConversationState = conversationState; | ||
_userState = userState; | ||
Dialog = dialog; | ||
Logger = logger; | ||
} | ||
|
||
public BotState ConversationState { get; } | ||
|
||
public ILogger Logger { get; } | ||
|
||
public Dialog Dialog { get; } | ||
|
||
public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default) | ||
{ | ||
await base.OnTurnAsync(turnContext, cancellationToken); | ||
|
||
// Save any state changes that might have occured during the turn. | ||
await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken); | ||
await _userState.SaveChangesAsync(turnContext, false, cancellationToken); | ||
} | ||
|
||
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken) | ||
{ | ||
Logger.LogInformation("Running dialog with Message Activity."); | ||
|
||
// Run the Dialog with the new message Activity. | ||
await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken); | ||
} | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
prototypes/DialogToDialog/AuthenticationBot/Controllers/BotController.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.Bot.Builder; | ||
using Microsoft.Bot.Builder.Integration.AspNet.Core; | ||
|
||
namespace AuthenticationBot.Controllers | ||
{ | ||
// This ASP Controller is created to handle a request. Dependency Injection will provide the Adapter and IBot | ||
// implementation at runtime. Multiple different IBot implementations running at different endpoints can be | ||
// achieved by specifying a more specific type for the bot constructor argument. | ||
[Route("api/messages")] | ||
[ApiController] | ||
public class BotController : ControllerBase | ||
{ | ||
private readonly IBotFrameworkHttpAdapter _adapter; | ||
private readonly IBot _bot; | ||
|
||
public BotController(IBotFrameworkHttpAdapter adapter, IBot bot) | ||
{ | ||
_adapter = adapter; | ||
_bot = bot; | ||
} | ||
|
||
[HttpPost] | ||
public async Task PostAsync() | ||
{ | ||
// Delegate the processing of the HTTP POST to the adapter. | ||
// The adapter will invoke the bot. | ||
await _adapter.ProcessAsync(Request, Response, _bot); | ||
} | ||
} | ||
} |
63 changes: 63 additions & 0 deletions
63
prototypes/DialogToDialog/AuthenticationBot/Dialogs/LogoutDialog.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Bot.Builder; | ||
using Microsoft.Bot.Builder.Dialogs; | ||
using Microsoft.Bot.Schema; | ||
|
||
namespace AuthenticationBot.Dialogs | ||
{ | ||
public class LogoutDialog : ComponentDialog | ||
{ | ||
public LogoutDialog(string id, string connectionName) | ||
: base(id) | ||
{ | ||
ConnectionName = connectionName; | ||
} | ||
|
||
protected string ConnectionName { get; } | ||
|
||
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; | ||
} | ||
} | ||
} |
93 changes: 93 additions & 0 deletions
93
prototypes/DialogToDialog/AuthenticationBot/Dialogs/MainDialog.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Bot.Builder; | ||
using Microsoft.Bot.Builder.Dialogs; | ||
using Microsoft.Bot.Schema; | ||
using Microsoft.Extensions.Configuration; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace AuthenticationBot.Dialogs | ||
{ | ||
public class MainDialog : LogoutDialog | ||
{ | ||
private readonly ILogger _logger; | ||
|
||
public MainDialog(IConfiguration configuration, ILogger<MainDialog> logger) | ||
: base(nameof(MainDialog), configuration["ConnectionName"]) | ||
{ | ||
_logger = logger; | ||
|
||
AddDialog(new OAuthPrompt( | ||
nameof(OAuthPrompt), | ||
new OAuthPromptSettings | ||
{ | ||
ConnectionName = 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 WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[] { PromptStepAsync, LoginStepAsync, DisplayTokenPhase1Async, DisplayTokenPhase2Async })); | ||
|
||
// 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) | ||
{ | ||
await stepContext.Context.SendActivityAsync(MessageFactory.Text("You are now logged in."), cancellationToken); | ||
return await stepContext.PromptAsync(nameof(ConfirmPrompt), new PromptOptions { Prompt = MessageFactory.Text("Would you like to view your token?") }, cancellationToken); | ||
} | ||
|
||
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Login was not successful please try again."), cancellationToken); | ||
return await stepContext.EndDialogAsync(cancellationToken: cancellationToken); | ||
} | ||
|
||
private async Task<DialogTurnResult> DisplayTokenPhase1Async(WaterfallStepContext stepContext, CancellationToken cancellationToken) | ||
{ | ||
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Thank you."), cancellationToken); | ||
|
||
var result = (bool)stepContext.Result; | ||
if (result) | ||
{ | ||
// Call the prompt again because we need the token. The reasons for this are: | ||
// 1. If the user is already logged in we do not need to store the token locally in the bot and worry | ||
// about refreshing it. We can always just call the prompt again to get the token. | ||
// 2. We never know how long it will take a user to respond. By the time the | ||
// user responds the token may have expired. The user would then be prompted to login again. | ||
// | ||
// There is no reason to store the token locally in the bot because we can always just call | ||
// the OAuth prompt to get the token or get a new token if needed. | ||
return await stepContext.BeginDialogAsync(nameof(OAuthPrompt), cancellationToken: cancellationToken); | ||
} | ||
|
||
return await stepContext.EndDialogAsync(cancellationToken: cancellationToken); | ||
} | ||
|
||
private async Task<DialogTurnResult> DisplayTokenPhase2Async(WaterfallStepContext stepContext, CancellationToken cancellationToken) | ||
{ | ||
var tokenResponse = (TokenResponse)stepContext.Result; | ||
if (tokenResponse != null) | ||
{ | ||
await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Here is your token {tokenResponse.Token}"), cancellationToken); | ||
} | ||
|
||
return await stepContext.EndDialogAsync(cancellationToken: cancellationToken); | ||
} | ||
} | ||
} |
Oops, something went wrong.