Merge branch 'master' into jeffders/OAuthCardSSOMaster

This commit is contained in:
bothosting.runtime@bf.ai 2020-02-24 13:04:50 -08:00
Родитель c97b4ffb58 5bd7110965
Коммит 096e10c24f
703 изменённых файлов: 10910 добавлений и 3144 удалений

Просмотреть файл

Просмотреть файл

@ -30,7 +30,7 @@
<ModulePath>.*\Microsoft.Bot.Builder.Adapters.Twilio.dll$</ModulePath>
<ModulePath>.*\Microsoft.Bot.Builder.Adapters.Slack.dll$</ModulePath>
<ModulePath>.*\Microsoft.Bot.Builder.Adapters.Facebook.dll$</ModulePath>
<ModulePath>.*\Microsoft.Bot.Expressions.dll$</ModulePath>
<ModulePath>.*\AdaptiveExpressions.dll$</ModulePath>
<ModulePath>.*\Microsoft.Bot.Builder.Dialogs.Adaptive.dll$</ModulePath>
<ModulePath>.*\Microsoft.Bot.Builder.Dialogs.Declarative.dll$</ModulePath>
<ModulePath>.*\Microsoft.Bot.Builder.LanguageGeneration.dll$</ModulePath>

Просмотреть файл

@ -6,7 +6,7 @@
<Configurations>Debug;Release;</Configurations>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
@ -14,7 +14,7 @@
<PackageReference Include="AdaptiveCards" Version="1.2.3" />
<PackageReference Include="Microsoft.Bot.Connector.DirectLine" Version="3.0.2" />
<PackageReference Include="Microsoft.CognitiveServices.Speech" Version="1.7.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.1" />
<PackageReference Include="MSTest.TestAdapter" Version="1.4.0" />
<PackageReference Include="MSTest.TestFramework" Version="1.4.0" />
<PackageReference Include="NAudio" Version="1.9.0" />

Просмотреть файл

@ -0,0 +1,54 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Extensions.Configuration;
namespace Microsoft.BotBuilderSamples.EchoSkillBot.Authentication
{
/// <summary>
/// Sample claims validator that loads an allowed list from configuration if present
/// and checks that requests are coming from allowed parent bots.
/// </summary>
public class AllowedCallersClaimsValidator : ClaimsValidator
{
private const string ConfigKey = "AllowedCallers";
private readonly List<string> _allowedCallers;
public AllowedCallersClaimsValidator(IConfiguration config)
{
if (config == null)
{
throw new ArgumentNullException(nameof(config));
}
// AllowedCallers is the setting in appsettings.json file
// that consists of the list of parent bot ids that are allowed to access the skill
// to add a new parent bot simply go to the AllowedCallers and add
// the parent bot's microsoft app id to the list
var section = config.GetSection(ConfigKey);
var appsList = section.Get<string[]>();
_allowedCallers = appsList != null ? new List<string>(appsList) : null;
}
public override Task ValidateClaimsAsync(IList<Claim> claims)
{
// if _allowedCallers is null we allow all calls
if (_allowedCallers != null && SkillValidation.IsSkillClaim(claims))
{
// Check that the appId claim in the skill request is in the list of skills configured for this bot.
var appId = JwtTokenValidation.GetAppIdFromClaims(claims);
if (!_allowedCallers.Contains(appId))
{
throw new UnauthorizedAccessException($"Received a request from a bot with an app ID of \"{appId}\". To enable requests from this caller, add the app ID to your configuration file.");
}
}
return Task.CompletedTask;
}
}
}

Просмотреть файл

@ -0,0 +1,30 @@
// 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.Schema;
namespace Microsoft.BotBuilderSamples.EchoSkillBot.Bots
{
public class EchoBot : ActivityHandler
{
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
if (turnContext.Activity.Text.Contains("end") || turnContext.Activity.Text.Contains("stop"))
{
// Send End of conversation at the end.
await turnContext.SendActivityAsync(MessageFactory.Text($"ending conversation from the skill..."), cancellationToken);
var endOfConversation = Activity.CreateEndOfConversationActivity();
endOfConversation.Code = EndOfConversationCodes.CompletedSuccessfully;
await turnContext.SendActivityAsync(endOfConversation, cancellationToken);
}
else
{
await turnContext.SendActivityAsync(MessageFactory.Text($"Echo (dotnet core 3.1) : {turnContext.Activity.Text}"), cancellationToken);
await turnContext.SendActivityAsync(MessageFactory.Text("Say \"end\" or \"stop\" and I'll end the conversation and back to the parent."), cancellationToken);
}
}
}
}

Просмотреть файл

@ -0,0 +1,33 @@
// 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 Microsoft.BotBuilderSamples.EchoSkillBot.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()
{
await _adapter.ProcessAsync(Request, Response, _bot);
}
}
}

Просмотреть файл

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>latest</LangVersion>
<AssemblyName>Microsoft.BotBuilderSamples.EchoSkillBot</AssemblyName>
<RootNamespace>Microsoft.BotBuilderSamples.EchoSkillBot</RootNamespace>
<UserSecretsId>a6aa19d1-4134-48c1-8970-8404e694e003</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\libraries\integration\Microsoft.Bot.Builder.Integration.AspNet.Core\Microsoft.Bot.Builder.Integration.AspNet.Core.csproj" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace Microsoft.BotBuilderSamples.EchoSkillBot
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}

Просмотреть файл

@ -0,0 +1,28 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:39793/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"EchoSkillBot31": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

Просмотреть файл

@ -0,0 +1,62 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Builder.TraceExtensions;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Microsoft.BotBuilderSamples.EchoSkillBot
{
public class SkillAdapterWithErrorHandler : BotFrameworkHttpAdapter
{
public SkillAdapterWithErrorHandler(IConfiguration configuration, ICredentialProvider credentialProvider, AuthenticationConfiguration authConfig, ILogger<BotFrameworkHttpAdapter> logger, ConversationState conversationState = null)
: base(configuration, credentialProvider, authConfig, logger: logger)
{
OnTurnError = async (turnContext, exception) =>
{
// Log any leaked exception from the application.
logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}");
// Send a message to the user
var errorMessageText = "The skill encountered an error or bug.";
var errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.IgnoringInput);
await turnContext.SendActivityAsync(errorMessage);
errorMessageText = "To continue to run this bot, please fix the bot source code.";
errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.ExpectingInput);
await turnContext.SendActivityAsync(errorMessage);
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 ex)
{
logger.LogError(ex, $"Exception caught on attempting to Delete ConversationState : {ex}");
}
}
// Send and EndOfConversation activity to the skill caller with the error to end the conversation
// and let the caller decide what to do.
var endOfConversation = Activity.CreateEndOfConversationActivity();
endOfConversation.Code = "SkillError";
endOfConversation.Text = exception.Message;
await turnContext.SendActivityAsync(endOfConversation);
// Send a trace activity, which will be displayed in the Bot Framework Emulator
// Note: we return the entire exception in the value property to help the developer, this should not be done in prod.
await turnContext.TraceActivityAsync("OnTurnError Trace", exception.ToString(), "https://www.botframework.com/schemas/error", "TurnError");
};
}
}
}

Просмотреть файл

@ -0,0 +1,71 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.BotFramework;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.BotBuilderSamples.EchoSkillBot.Authentication;
using Microsoft.BotBuilderSamples.EchoSkillBot.Bots;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Microsoft.BotBuilderSamples.EchoSkillBot
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddNewtonsoftJson();
// Configure credentials
services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();
// Register AuthConfiguration to enable custom claim validation.
services.AddSingleton(sp => new AuthenticationConfiguration { ClaimsValidator = new AllowedCallersClaimsValidator(sp.GetService<IConfiguration>()) });
// Create the Bot Framework Adapter with error handling enabled.
services.AddSingleton<IBotFrameworkHttpAdapter, SkillAdapterWithErrorHandler>();
// Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
services.AddTransient<IBot, EchoBot>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseDefaultFiles();
app.UseStaticFiles();
//app.UseHttpsRedirection(); Enable this to support https
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

Просмотреть файл

@ -0,0 +1,13 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"MicrosoftAppId": "",
"MicrosoftAppPassword": "",
"AllowedCallers": []
}

Просмотреть файл

@ -0,0 +1,420 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>EchoSkillBot</title>
<style>
body {
margin: 0px;
padding: 0px;
font-family: Segoe UI;
}
html,
body {
height: 100%;
}
header {
background-image: url("data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 4638.9 651.6' style='enable-background:new 0 0 4638.9 651.6;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bfill:%2355A0E0;%7D .st1%7Bfill:none;%7D .st2%7Bfill:%230058A8;%7D .st3%7Bfill:%23328BD8;%7D .st4%7Bfill:%23B6DCF1;%7D .st5%7Bopacity:0.2;fill:url(%23SVGID_1_);enable-background:new ;%7D%0A%3C/style%3E%3Crect y='1.1' class='st0' width='4640' height='646.3'/%3E%3Cpath class='st1' d='M3987.8,323.6L4310.3,1.1h-65.6l-460.1,460.1c-17.5,17.5-46.1,17.5-63.6,0L3260.9,1.1H0v646.3h3660.3 L3889,418.7c17.5-17.5,46.1-17.5,63.6,0l228.7,228.7h66.6l-260.2-260.2C3970.3,369.8,3970.3,341.1,3987.8,323.6z'/%3E%3Cpath class='st2' d='M3784.6,461.2L4244.7,1.1h-983.9l460.1,460.1C3738.4,478.7,3767.1,478.7,3784.6,461.2z'/%3E%3Cpath class='st3' d='M4640,1.1h-329.8l-322.5,322.5c-17.5,17.5-17.5,46.1,0,63.6l260.2,260.2H4640L4640,1.1L4640,1.1z'/%3E%3Cpath class='st4' d='M3889,418.8l-228.7,228.7h521.1l-228.7-228.7C3935.2,401.3,3906.5,401.3,3889,418.8z'/%3E%3ClinearGradient id='SVGID_1_' gradientUnits='userSpaceOnUse' x1='3713.7576' y1='438.1175' x2='3911.4084' y2='14.2535' gradientTransform='matrix(1 0 0 -1 0 641.3969)'%3E%3Cstop offset='0' style='stop-color:%23FFFFFF;stop-opacity:0.5'/%3E%3Cstop offset='1' style='stop-color:%23FFFFFF'/%3E%3C/linearGradient%3E%3Cpath class='st5' d='M3952.7,124.5c-17.5-17.5-46.1-17.5-63.6,0l-523,523h1109.6L3952.7,124.5z'/%3E%3C/svg%3E%0A");
background-repeat: no-repeat;
background-size: 100%;
background-position: right;
background-color: #55A0E0;
width: 100%;
font-size: 44px;
height: 120px;
color: white;
padding: 30px 0 40px 0px;
display: inline-block;
}
.header-icon {
background-image: url("data:image/svg+xml;utf8,%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20xmlns%3Axlink%3D%22http%3A//www.w3.org/1999/xlink%22%20x%3D%220px%22%20y%3D%220px%22%0A%09%20viewBox%3D%220%200%20150.2%20125%22%20style%3D%22enable-background%3Anew%200%200%20150.2%20125%3B%22%20xml%3Aspace%3D%22preserve%22%3E%0A%3Cstyle%20type%3D%22text/css%22%3E%0A%09.st0%7Bfill%3Anone%3B%7D%0A%09.st1%7Bfill%3A%23FFFFFF%3B%7D%0A%3C/style%3E%0A%3Crect%20x%3D%220.5%22%20class%3D%22st0%22%20width%3D%22149.7%22%20height%3D%22125%22/%3E%0A%3Cg%3E%0A%09%3Cpath%20class%3D%22st1%22%20d%3D%22M59%2C102.9L21.8%2C66c-3.5-3.5-3.5-9.1%2C0-12.5l37-36.5l2.9%2C3l-37%2C36.4c-1.8%2C1.8-1.8%2C4.7%2C0%2C6.6l37.2%2C37L59%2C102.9z%22%0A%09%09/%3E%0A%3C/g%3E%0A%3Cg%3E%0A%09%3Cpath%20class%3D%22st1%22%20d%3D%22M92.5%2C102.9l-3-3l37.2-37c0.9-0.9%2C1.4-2%2C1.4-3.3c0-1.2-0.5-2.4-1.4-3.3L89.5%2C20l2.9-3l37.2%2C36.4%0A%09%09c1.7%2C1.7%2C2.6%2C3.9%2C2.6%2C6.3s-0.9%2C4.6-2.6%2C6.3L92.5%2C102.9z%22/%3E%0A%3C/g%3E%0A%3Cg%3E%0A%09%3Cpath%20class%3D%22st1%22%20d%3D%22M90.1%2C68.4c-4.5%2C0-8-3.5-8-8.1c0-4.5%2C3.5-8.1%2C8-8.1c4.4%2C0%2C8%2C3.7%2C8%2C8.1C98.1%2C64.7%2C94.4%2C68.4%2C90.1%2C68.4z%0A%09%09%20M90.1%2C56.5c-2.2%2C0-3.8%2C1.7-3.8%2C3.9c0%2C2.2%2C1.7%2C3.9%2C3.8%2C3.9c1.9%2C0%2C3.8-1.6%2C3.8-3.9S91.9%2C56.5%2C90.1%2C56.5z%22/%3E%0A%3C/g%3E%0A%3Cg%3E%0A%09%3Cpath%20class%3D%22st1%22%20d%3D%22M61.4%2C68.4c-4.5%2C0-8-3.5-8-8.1c0-4.5%2C3.5-8.1%2C8-8.1c4.4%2C0%2C8%2C3.7%2C8%2C8.1C69.5%2C64.7%2C65.8%2C68.4%2C61.4%2C68.4z%0A%09%09%20M61.4%2C56.5c-2.2%2C0-3.8%2C1.7-3.8%2C3.9c0%2C2.2%2C1.7%2C3.9%2C3.8%2C3.9c1.9%2C0%2C3.8-1.6%2C3.8-3.9S63.3%2C56.5%2C61.4%2C56.5z%22/%3E%0A%3C/g%3E%0A%3C/svg%3E%0A");
background-repeat: no-repeat;
float: left;
height: 140px;
width: 140px;
display: inline-block;
vertical-align: middle;
}
.header-text {
padding-left: 1%;
color: #FFFFFF;
font-family: "Segoe UI";
font-size: 72px;
font-weight: 300;
letter-spacing: 0.35px;
line-height: 96px;
display: inline-block;
vertical-align: middle;
}
.header-inner-container {
min-width: 480px;
max-width: 1366px;
margin-left: auto;
margin-right: auto;
vertical-align: middle;
}
.header-inner-container::after {
content: "";
clear: both;
display: table;
}
.main-content-area {
padding-left: 30px;
}
.content-title {
color: #000000;
font-family: "Segoe UI";
font-size: 46px;
font-weight: 300;
line-height: 62px;
}
.main-text {
color: #808080;
font-size: 24px;
font-family: "Segoe UI";
font-size: 24px;
font-weight: 200;
line-height: 32px;
}
.main-text-p1 {
padding-top: 48px;
padding-bottom: 28px;
}
.endpoint {
height: 32px;
width: 571px;
color: #808080;
font-family: "Segoe UI";
font-size: 24px;
font-weight: 200;
line-height: 32px;
padding-top: 28px;
}
.how-to-build-section {
padding-top: 20px;
padding-left: 30px;
}
.how-to-build-section>h3 {
font-size: 16px;
font-weight: 600;
letter-spacing: 0.35px;
line-height: 22px;
margin: 0 0 24px 0;
text-transform: uppercase;
}
.step-container {
display: flex;
align-items: stretch;
position: relative;
}
.step-container dl {
border-left: 1px solid #A0A0A0;
display: block;
padding: 0 24px;
margin: 0;
}
.step-container dl>dt::before {
background-color: white;
border: 1px solid #A0A0A0;
border-radius: 100%;
content: '';
left: 47px;
height: 11px;
position: absolute;
width: 11px;
}
.step-container dl>.test-bullet::before {
background-color: blue;
}
.step-container dl>dt {
display: block;
font-size: inherit;
font-weight: bold;
line-height: 20px;
}
.step-container dl>dd {
font-size: inherit;
line-height: 20px;
margin-left: 0;
padding-bottom: 32px;
}
.step-container:last-child dl {
border-left: 1px solid transparent;
}
.ctaLink {
background-color: transparent;
border: 1px solid transparent;
color: #006AB1;
cursor: pointer;
font-weight: 600;
padding: 0;
white-space: normal;
}
.ctaLink:focus {
outline: 1px solid #00bcf2;
}
.ctaLink:hover {
text-decoration: underline;
}
.step-icon {
display: flex;
height: 38px;
margin-right: 15px;
width: 38px;
}
.step-icon>div {
height: 30px;
width: 30px;
background-repeat: no-repeat;
}
.ms-logo-container {
min-width: 580px;
max-width: 980px;
margin-left: auto;
margin-right: auto;
left: 0;
right: 0;
transition: bottom 400ms;
}
.ms-logo {
float: right;
background-image: url("data:image/svg+xml;utf8,%0A%3Csvg%20version%3D%221.1%22%20id%3D%22MS-symbol%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20xmlns%3Axlink%3D%22http%3A//www.w3.org/1999/xlink%22%20x%3D%220px%22%20y%3D%220px%22%0A%09%20viewBox%3D%220%200%20400%20120%22%20style%3D%22enable-background%3Anew%200%200%20400%20120%3B%22%20xml%3Aspace%3D%22preserve%22%3E%0A%3Cstyle%20type%3D%22text/css%22%3E%0A%09.st0%7Bfill%3Anone%3B%7D%0A%09.st1%7Bfill%3A%23737474%3B%7D%0A%09.st2%7Bfill%3A%23D63F26%3B%7D%0A%09.st3%7Bfill%3A%23167D3E%3B%7D%0A%09.st4%7Bfill%3A%232E76BC%3B%7D%0A%09.st5%7Bfill%3A%23FDB813%3B%7D%0A%3C/style%3E%0A%3Crect%20x%3D%220.6%22%20class%3D%22st0%22%20width%3D%22398.7%22%20height%3D%22119%22/%3E%0A%3Cpath%20class%3D%22st1%22%20d%3D%22M171.3%2C38.4v43.2h-7.5V47.7h-0.1l-13.4%2C33.9h-5l-13.7-33.9h-0.1v33.9h-6.9V38.4h10.8l12.4%2C32h0.2l13.1-32H171.3%0A%09z%20M177.6%2C41.7c0-1.2%2C0.4-2.2%2C1.3-3c0.9-0.8%2C1.9-1.2%2C3.1-1.2c1.3%2C0%2C2.4%2C0.4%2C3.2%2C1.3c0.8%2C0.8%2C1.3%2C1.8%2C1.3%2C3c0%2C1.2-0.4%2C2.2-1.3%2C3%0A%09c-0.9%2C0.8-1.9%2C1.2-3.2%2C1.2s-2.3-0.4-3.1-1.2C178%2C43.8%2C177.6%2C42.8%2C177.6%2C41.7z%20M185.7%2C50.6v31h-7.3v-31H185.7z%20M207.8%2C76.3%0A%09c1.1%2C0%2C2.3-0.3%2C3.6-0.8c1.3-0.5%2C2.5-1.2%2C3.6-2v6.8c-1.2%2C0.7-2.5%2C1.2-4%2C1.5c-1.5%2C0.3-3.1%2C0.5-4.9%2C0.5c-4.6%2C0-8.3-1.4-11.1-4.3%0A%09c-2.9-2.9-4.3-6.6-4.3-11c0-5%2C1.5-9.1%2C4.4-12.3c2.9-3.2%2C7-4.8%2C12.4-4.8c1.4%2C0%2C2.7%2C0.2%2C4.1%2C0.5c1.4%2C0.4%2C2.5%2C0.8%2C3.3%2C1.2v7%0A%09c-1.1-0.8-2.3-1.5-3.4-1.9c-1.2-0.5-2.4-0.7-3.6-0.7c-2.9%2C0-5.2%2C0.9-7%2C2.8c-1.8%2C1.9-2.7%2C4.4-2.7%2C7.6c0%2C3.1%2C0.8%2C5.6%2C2.5%2C7.3%0A%09C202.6%2C75.4%2C204.9%2C76.3%2C207.8%2C76.3z%20M235.7%2C50.1c0.6%2C0%2C1.1%2C0%2C1.6%2C0.1s0.9%2C0.2%2C1.2%2C0.3v7.4c-0.4-0.3-0.9-0.5-1.7-0.8%0A%09c-0.7-0.3-1.6-0.4-2.7-0.4c-1.8%2C0-3.3%2C0.8-4.5%2C2.3c-1.2%2C1.5-1.9%2C3.8-1.9%2C7v15.6h-7.3v-31h7.3v4.9h0.1c0.7-1.7%2C1.7-3%2C3-4%0A%09C232.2%2C50.6%2C233.8%2C50.1%2C235.7%2C50.1z%20M238.9%2C66.6c0-5.1%2C1.4-9.2%2C4.3-12.2c2.9-3%2C6.9-4.5%2C12.1-4.5c4.8%2C0%2C8.6%2C1.4%2C11.3%2C4.3%0A%09c2.7%2C2.9%2C4.1%2C6.8%2C4.1%2C11.7c0%2C5-1.4%2C9-4.3%2C12c-2.9%2C3-6.8%2C4.5-11.8%2C4.5c-4.8%2C0-8.6-1.4-11.4-4.2C240.3%2C75.3%2C238.9%2C71.4%2C238.9%2C66.6z%0A%09%20M246.5%2C66.3c0%2C3.2%2C0.7%2C5.7%2C2.2%2C7.4c1.5%2C1.7%2C3.6%2C2.6%2C6.3%2C2.6c2.7%2C0%2C4.7-0.9%2C6.1-2.6c1.4-1.7%2C2.1-4.2%2C2.1-7.6c0-3.3-0.7-5.8-2.2-7.5%0A%09c-1.4-1.7-3.4-2.5-6-2.5c-2.7%2C0-4.7%2C0.9-6.2%2C2.7C247.2%2C60.5%2C246.5%2C63%2C246.5%2C66.3z%20M281.5%2C58.8c0%2C1%2C0.3%2C1.9%2C1%2C2.5%0A%09c0.7%2C0.6%2C2.1%2C1.3%2C4.4%2C2.2c2.9%2C1.2%2C5%2C2.5%2C6.1%2C3.9c1.2%2C1.5%2C1.8%2C3.2%2C1.8%2C5.3c0%2C2.9-1.1%2C5.3-3.4%2C7c-2.2%2C1.8-5.3%2C2.7-9.1%2C2.7%0A%09c-1.3%2C0-2.7-0.2-4.3-0.5c-1.6-0.3-2.9-0.7-4-1.2v-7.2c1.3%2C0.9%2C2.8%2C1.7%2C4.3%2C2.2c1.5%2C0.5%2C2.9%2C0.8%2C4.2%2C0.8c1.6%2C0%2C2.9-0.2%2C3.6-0.7%0A%09c0.8-0.5%2C1.2-1.2%2C1.2-2.3c0-1-0.4-1.9-1.2-2.5c-0.8-0.7-2.4-1.5-4.6-2.4c-2.7-1.1-4.6-2.4-5.7-3.8c-1.1-1.4-1.7-3.2-1.7-5.4%0A%09c0-2.8%2C1.1-5.1%2C3.3-6.9c2.2-1.8%2C5.1-2.7%2C8.6-2.7c1.1%2C0%2C2.3%2C0.1%2C3.6%2C0.4c1.3%2C0.2%2C2.5%2C0.6%2C3.4%2C0.9v6.9c-1-0.6-2.1-1.2-3.4-1.7%0A%09c-1.3-0.5-2.6-0.7-3.8-0.7c-1.4%2C0-2.5%2C0.3-3.2%2C0.8C281.9%2C57.1%2C281.5%2C57.8%2C281.5%2C58.8z%20M297.9%2C66.6c0-5.1%2C1.4-9.2%2C4.3-12.2%0A%09c2.9-3%2C6.9-4.5%2C12.1-4.5c4.8%2C0%2C8.6%2C1.4%2C11.3%2C4.3c2.7%2C2.9%2C4.1%2C6.8%2C4.1%2C11.7c0%2C5-1.4%2C9-4.3%2C12c-2.9%2C3-6.8%2C4.5-11.8%2C4.5%0A%09c-4.8%2C0-8.6-1.4-11.4-4.2C299.4%2C75.3%2C297.9%2C71.4%2C297.9%2C66.6z%20M305.5%2C66.3c0%2C3.2%2C0.7%2C5.7%2C2.2%2C7.4c1.5%2C1.7%2C3.6%2C2.6%2C6.3%2C2.6%0A%09c2.7%2C0%2C4.7-0.9%2C6.1-2.6c1.4-1.7%2C2.1-4.2%2C2.1-7.6c0-3.3-0.7-5.8-2.2-7.5c-1.4-1.7-3.4-2.5-6-2.5c-2.7%2C0-4.7%2C0.9-6.2%2C2.7%0A%09C306.3%2C60.5%2C305.5%2C63%2C305.5%2C66.3z%20M353.9%2C56.6h-10.9v25h-7.4v-25h-5.2v-6h5.2v-4.3c0-3.3%2C1.1-5.9%2C3.2-8c2.1-2.1%2C4.8-3.1%2C8.1-3.1%0A%09c0.9%2C0%2C1.7%2C0%2C2.4%2C0.1c0.7%2C0.1%2C1.3%2C0.2%2C1.8%2C0.4V42c-0.2-0.1-0.7-0.3-1.3-0.5c-0.6-0.2-1.3-0.3-2.1-0.3c-1.5%2C0-2.7%2C0.5-3.5%2C1.4%0A%09s-1.2%2C2.4-1.2%2C4.2v3.7h10.9v-7l7.3-2.2v9.2h7.4v6h-7.4v14.5c0%2C1.9%2C0.3%2C3.3%2C1%2C4c0.7%2C0.8%2C1.8%2C1.2%2C3.3%2C1.2c0.4%2C0%2C0.9-0.1%2C1.5-0.3%0A%09c0.6-0.2%2C1.1-0.4%2C1.6-0.7v6c-0.5%2C0.3-1.2%2C0.5-2.3%2C0.7c-1.1%2C0.2-2.1%2C0.3-3.2%2C0.3c-3.1%2C0-5.4-0.8-6.9-2.5c-1.5-1.6-2.3-4.1-2.3-7.4%0A%09V56.6z%22/%3E%0A%3Cg%3E%0A%09%3Crect%20x%3D%2231%22%20y%3D%2224%22%20class%3D%22st2%22%20width%3D%2234.2%22%20height%3D%2234.2%22/%3E%0A%09%3Crect%20x%3D%2268.8%22%20y%3D%2224%22%20class%3D%22st3%22%20width%3D%2234.2%22%20height%3D%2234.2%22/%3E%0A%09%3Crect%20x%3D%2231%22%20y%3D%2261.8%22%20class%3D%22st4%22%20width%3D%2234.2%22%20height%3D%2234.2%22/%3E%0A%09%3Crect%20x%3D%2268.8%22%20y%3D%2261.8%22%20class%3D%22st5%22%20width%3D%2234.2%22%20height%3D%2234.2%22/%3E%0A%3C/g%3E%0A%3C/svg%3E%0A");
}
.ms-logo-container>div {
min-height: 60px;
width: 150px;
background-repeat: no-repeat;
}
.row {
padding: 90px 0px 0 20px;
min-width: 480px;
max-width: 1366px;
margin-left: auto;
margin-right: auto;
}
.column {
float: left;
width: 45%;
padding-right: 20px;
}
.row:after {
content: "";
display: table;
clear: both;
}
a {
text-decoration: none;
}
.download-the-emulator {
height: 20px;
color: #0063B1;
font-size: 15px;
line-height: 20px;
padding-bottom: 70px;
}
.how-to-iframe {
max-width: 700px !important;
min-width: 650px !important;
height: 700px !important;
}
.remove-frame-height {
height: 10px;
}
@media only screen and (max-width: 1300px) {
.ms-logo {
padding-top: 30px;
}
.header-text {
font-size: 40x;
}
.column {
float: none;
padding-top: 30px;
width: 100%;
}
.ms-logo-container {
padding-top: 30px;
min-width: 480px;
max-width: 650px;
margin-left: auto;
margin-right: auto;
}
.row {
padding: 20px 0px 0 20px;
min-width: 480px;
max-width: 650px;
margin-left: auto;
margin-right: auto;
}
}
@media only screen and (max-width: 1370px) {
header {
background-color: #55A0E0;
background-size: auto 200px;
}
}
@media only screen and (max-width: 1230px) {
header {
background-color: #55A0E0;
background-size: auto 200px;
}
.header-text {
font-size: 44px;
}
.header-icon {
height: 120px;
width: 120px;
}
}
@media only screen and (max-width: 1000px) {
header {
background-color: #55A0E0;
background-image: none;
}
}
@media only screen and (max-width: 632px) {
.header-text {
font-size: 32px;
}
.row {
padding: 10px 0px 0 10px;
max-width: 490px !important;
min-width: 410px !important;
}
.endpoint {
font-size: 25px;
}
.main-text {
font-size: 20px;
}
.step-container dl>dd {
font-size: 14px;
}
.column {
padding-right: 5px;
}
.header-icon {
height: 110px;
width: 110px;
}
.how-to-iframe {
max-width: 480px !important;
min-width: 400px !important;
height: 650px !important;
overflow: hidden;
}
}
.remove-frame-height {
max-height: 10px;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function () {
loadFrame();
});
var loadFrame = function () {
var iframe = document.createElement('iframe');
iframe.setAttribute("id", "iframe");
var offLineHTMLContent = "";
var frameElement = document.getElementById("how-to-iframe");
if (window.navigator.onLine) {
iframe.src = 'https://docs.botframework.com/static/abs/pages/f5.htm';
iframe.setAttribute("scrolling", "no");
iframe.setAttribute("frameborder", "0");
iframe.setAttribute("width", "100%");
iframe.setAttribute("height", "100%");
var frameDiv = document.getElementById("how-to-iframe");
frameDiv.appendChild(iframe);
} else {
frameElement.classList.add("remove-frame-height");
}
};
</script>
</head>
<body>
<header class="header">
<div class="header-inner-container">
<div class="header-icon" style="display: inline-block"></div>
<div class="header-text" style="display: inline-block">EchoSkillBot Bot</div>
</div>
</header>
<div class="row">
<div class="column" class="main-content-area">
<div class="content-title">Your bot is ready!</div>
<div class="main-text main-text-p1">You can test your bot in the Bot Framework Emulator<br />
by connecting to http://localhost:3978/api/messages.</div>
<div class="main-text download-the-emulator"><a class="ctaLink"
href="https://aka.ms/bot-framework-F5-download-emulator" target="_blank">Download the Emulator</a>
</div>
<div class="main-text">Visit <a class="ctaLink" href="https://aka.ms/bot-framework-F5-abs-home"
target="_blank">Azure
Bot Service</a> to register your bot and add it to<br />
various channels. The bot's endpoint URL typically looks
like this:</div>
<div class="endpoint">https://<i>your_bots_hostname</i>/api/messages</div>
</div>
<div class="column how-to-iframe" id="how-to-iframe"></div>
</div>
<div class="ms-logo-container">
<div class="ms-logo"></div>
</div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,25 @@
{
"$schema": "https://schemas.botframework.com/schemas/skills/skill-manifest-2.0.0.json",
"$id": "EchoSkillBot",
"name": "Echo Skill bot",
"version": "1.0",
"description": "This is a sample echo skill",
"publisherName": "Microsoft",
"privacyUrl": "https://echoskillbot.contoso.com/privacy.html",
"copyright": "Copyright (c) Microsoft Corporation. All rights reserved.",
"license": "",
"iconUrl": "https://echoskillbot.contoso.com/icon.png",
"tags": [
"sample",
"echo"
],
"endpoints": [
{
"name": "default",
"protocol": "BotFrameworkV3",
"description": "Default endpoint for the skill",
"endpointUrl": "https://ggechoskillbot.azurewebsites.net/api/messages",
"msAppId": "0be01cfa-478e-4ec2-b2cc-9a4ec02f101b"
}
]
}

Просмотреть файл

@ -0,0 +1,57 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Builder.TraceExtensions;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Schema;
using Microsoft.BotBuilderSamples.DialogRootBot.Middleware;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Microsoft.BotBuilderSamples.DialogRootBot
{
public class AdapterWithErrorHandler : BotFrameworkHttpAdapter
{
public AdapterWithErrorHandler(IConfiguration configuration, ICredentialProvider credentialProvider, AuthenticationConfiguration authConfig, ILogger<AdapterWithErrorHandler> logger, ConversationState conversationState = null)
: base(configuration, credentialProvider, authConfig, logger: logger)
{
OnTurnError = async (turnContext, exception) =>
{
// Log any leaked exception from the application.
logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}");
// Send a message to the user
var errorMessageText = "The bot encountered an error or bug.";
var errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.IgnoringInput);
await turnContext.SendActivityAsync(errorMessage);
errorMessageText = "To continue to run this bot, please fix the bot source code.";
errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.ExpectingInput);
await turnContext.SendActivityAsync(errorMessage);
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 turnContext.TraceActivityAsync("OnTurnError Trace", exception.ToString(), "https://www.botframework.com/schemas/error", "TurnError");
};
Use(new LoggerMiddleware(logger));
}
}
}

Просмотреть файл

@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Bot.Connector.Authentication;
namespace Microsoft.BotBuilderSamples.DialogRootBot.Authentication
{
/// <summary>
/// Sample claims validator that loads an allowed list from configuration if present
/// and checks that responses are coming from configured skills.
/// </summary>
public class AllowedCallersClaimsValidator : ClaimsValidator
{
private readonly List<string> _allowedSkills;
public AllowedCallersClaimsValidator(SkillsConfiguration skillsConfig)
{
if (skillsConfig == null)
{
throw new ArgumentNullException(nameof(skillsConfig));
}
// Load the appIds for the configured skills (we will only allow responses from skills we have configured).
_allowedSkills = (from skill in skillsConfig.Skills.Values select skill.AppId).ToList();
}
public override Task ValidateClaimsAsync(IList<Claim> claims)
{
if (SkillValidation.IsSkillClaim(claims))
{
// Check that the appId claim in the skill request is in the list of skills configured for this bot.
var appId = JwtTokenValidation.GetAppIdFromClaims(claims);
if (!_allowedSkills.Contains(appId))
{
throw new UnauthorizedAccessException($"Received a request from an application with an appID of \"{appId}\". To enable requests from this skill, add the skill to your configuration file.");
}
}
return Task.CompletedTask;
}
}
}

Просмотреть файл

@ -0,0 +1,78 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Schema;
using Newtonsoft.Json;
namespace Microsoft.BotBuilderSamples.DialogRootBot.Bots
{
public class RootBot<T> : ActivityHandler
where T : Dialog
{
private readonly ConversationState _conversationState;
private readonly Dialog _mainDialog;
public RootBot(ConversationState conversationState, T mainDialog)
{
_conversationState = conversationState;
_mainDialog = mainDialog;
}
public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default)
{
if (turnContext.Activity.Type != ActivityTypes.ConversationUpdate)
{
// Run the Dialog with the Activity.
await _mainDialog.RunAsync(turnContext, _conversationState.CreateProperty<DialogState>("DialogState"), cancellationToken);
}
else
{
await base.OnTurnAsync(turnContext, cancellationToken);
}
// Save any state changes that might have occurred during the turn.
await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken);
}
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
foreach (var member in membersAdded)
{
// Greet anyone that was not the target (recipient) of this message.
// To learn more about Adaptive Cards, see https://aka.ms/msbot-adaptivecards for more details.
if (member.Id != turnContext.Activity.Recipient.Id)
{
var welcomeCard = CreateAdaptiveCardAttachment();
var activity = MessageFactory.Attachment(welcomeCard);
await turnContext.SendActivityAsync(activity, cancellationToken);
await _mainDialog.RunAsync(turnContext, _conversationState.CreateProperty<DialogState>("DialogState"), cancellationToken);
}
}
}
// Load attachment from embedded resource.
private Attachment CreateAdaptiveCardAttachment()
{
var cardResourcePath = "Microsoft.BotBuilderSamples.DialogRootBot.Cards.welcomeCard.json";
using (var stream = GetType().Assembly.GetManifestResourceStream(cardResourcePath))
{
using (var reader = new StreamReader(stream))
{
var adaptiveCard = reader.ReadToEnd();
return new Attachment
{
ContentType = "application/vnd.microsoft.card.adaptive",
Content = JsonConvert.DeserializeObject(adaptiveCard)
};
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,24 @@
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.0",
"body": [
{
"type": "TextBlock",
"spacing": "Medium",
"size": "Medium",
"weight": "Bolder",
"text": "Welcome to the Dialog Skill Prototype! (.Net Core 3.1)",
"wrap": true,
"maxLines": 0,
"color": "Accent"
},
{
"type": "TextBlock",
"size": "default",
"text": "This sample allows you to select a skill to invoke and try different SkillDialog scenarios.",
"wrap": true,
"maxLines": 0
}
]
}

Просмотреть файл

@ -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 Microsoft.BotBuilderSamples.DialogRootBot.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(BotFrameworkHttpAdapter 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);
}
}
}

Просмотреть файл

@ -0,0 +1,53 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Builder.Skills;
using Microsoft.Bot.Schema;
namespace Microsoft.BotBuilderSamples.DialogRootBot.Controllers
{
/// <summary>
/// A controller that handles skill replies to the bot.
/// This example uses the <see cref="SkillHandler"/> that is registered as a <see cref="ChannelServiceHandler"/> in startup.cs.
/// </summary>
[ApiController]
[Route("api/skills")]
public class SkillController : ChannelServiceController
{
public SkillController(ChannelServiceHandler handler)
: base(handler)
{
}
public override Task<IActionResult> ReplyToActivityAsync(string conversationId, string activityId, Activity activity)
{
try
{
return base.ReplyToActivityAsync(conversationId, activityId, activity);
}
catch (Exception ex)
{
Console.WriteLine(ex);
throw;
}
}
public override Task<IActionResult> SendToConversationAsync(string conversationId, Activity activity)
{
try
{
return base.SendToConversationAsync(conversationId, activity);
}
catch (Exception ex)
{
Console.WriteLine(ex);
throw;
}
}
}
}

Просмотреть файл

@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>latest</LangVersion>
<AssemblyName>Microsoft.BotBuilderSamples.DialogRootBot</AssemblyName>
<RootNamespace>Microsoft.BotBuilderSamples.DialogRootBot</RootNamespace>
<UserSecretsId>7b2e59c2-bdd5-4f9e-8e80-372a97959d24</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<Content Remove="Cards\welcomeCard.json" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Cards\welcomeCard.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.0" />
</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.Adaptive\Microsoft.Bot.Builder.Dialogs.Adaptive.csproj" />
<ProjectReference Include="..\..\..\..\libraries\Microsoft.Bot.Builder.Skills\Microsoft.Bot.Builder.Skills.csproj" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Newtonsoft.Json;
namespace Microsoft.BotBuilderSamples.DialogRootBot.Dialogs
{
public class BookingDetails
{
[JsonProperty("destination")]
public string Destination { get; set; }
[JsonProperty("origin")]
public string Origin { get; set; }
[JsonProperty("travelDate")]
public string TravelDate { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Newtonsoft.Json;
namespace Microsoft.BotBuilderSamples.DialogRootBot.Dialogs
{
public class Location
{
[JsonProperty("latitude")]
public float? Latitude { get; set; }
[JsonProperty("longitude")]
public float? Longitude { get; set; }
[JsonProperty("postalCode")]
public string PostalCode { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,248 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Choices;
using Microsoft.Bot.Builder.Integration.AspNet.Core.Skills;
using Microsoft.Bot.Builder.Skills;
using Microsoft.Bot.Builder.Skills.Dialogs;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
namespace Microsoft.BotBuilderSamples.DialogRootBot.Dialogs
{
/// <summary>
/// The main dialog for this bot. It uses a <see cref="SkillDialog"/> to call skills.
/// </summary>
public class MainDialog : ComponentDialog
{
private readonly string _selectedSkillKey = $"{typeof(MainDialog).FullName}.SelectedSkillKey";
private readonly SkillsConfiguration _skillsConfig;
// Dependency injection uses this constructor to instantiate MainDialog
public MainDialog(ConversationState conversationState, SkillConversationIdFactoryBase conversationIdFactory, SkillHttpClient skillClient, SkillsConfiguration skillsConfig, IConfiguration configuration)
: base(nameof(MainDialog))
{
var botId = configuration.GetSection(MicrosoftAppCredentials.MicrosoftAppIdKey)?.Value;
if (string.IsNullOrWhiteSpace(botId))
{
throw new ArgumentException($"{MicrosoftAppCredentials.MicrosoftAppIdKey} is not in configuration");
}
_skillsConfig = skillsConfig ?? throw new ArgumentNullException(nameof(skillsConfig));
if (skillClient == null)
{
throw new ArgumentNullException(nameof(skillClient));
}
if (conversationState == null)
{
throw new ArgumentNullException(nameof(conversationState));
}
// ChoicePrompt to render available skills and skill actions
AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
// SkillDialog used to wrap interaction with the selected skill
var skillDialogOptions = new SkillDialogOptions
{
BotId = botId,
ConversationIdFactory = conversationIdFactory,
SkillClient = skillClient,
SkillHostEndpoint = skillsConfig.SkillHostEndpoint
};
AddDialog(new SkillDialog(skillDialogOptions, conversationState));
// Main waterfall dialog for this bot
var waterfallSteps = new WaterfallStep[]
{
SelectSkillStepAsync,
SelectSkillActionStepAsync,
CallSkillActionStepAsync,
FinalStepAsync
};
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), waterfallSteps));
// The initial child Dialog to run.
InitialDialogId = nameof(WaterfallDialog);
}
// Render a prompt to select the skill to call.
private async Task<DialogTurnResult> SelectSkillStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
// Create the PromptOptions from the skill configuration which contain the list of configured skills.
var options = new PromptOptions
{
Prompt = MessageFactory.Text("What skill would you like to call?"),
RetryPrompt = MessageFactory.Text("That was not a valid choice, please select a valid skill."),
Choices = _skillsConfig.Skills.Select(skill => new Choice(skill.Value.Id)).ToList()
};
// Prompt the user to select a skill.
return await stepContext.PromptAsync(nameof(ChoicePrompt), options, cancellationToken);
}
// Render a prompt to select the action for the skill.
private async Task<DialogTurnResult> SelectSkillActionStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
// Get the skill info based on the selected skill.
var selectedSkillId = ((FoundChoice)stepContext.Result).Value;
var selectedSkill = _skillsConfig.Skills.FirstOrDefault(s => s.Value.Id == selectedSkillId).Value;
// Remember the skill selected by the user.
stepContext.Values[_selectedSkillKey] = selectedSkill;
// Create the PromptOptions with the actions supported by the selected skill.
var options = new PromptOptions
{
Prompt = MessageFactory.Text($"What action would you like to call in **{selectedSkill.Id}**?"),
RetryPrompt = MessageFactory.Text("That was not a valid choice, please select a valid action."),
Choices = GetSkillActions(selectedSkill),
Style = ListStyle.SuggestedAction
};
// Prompt the user to select a skill action.
return await stepContext.PromptAsync(nameof(ChoicePrompt), options, cancellationToken);
}
// Starts SkillDialog based on the user's selections
private async Task<DialogTurnResult> CallSkillActionStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var selectedSkill = (BotFrameworkSkill)stepContext.Values[_selectedSkillKey];
Activity skillActivity = null;
switch (selectedSkill.Id)
{
case "EchoSkillBot":
// Echo skill only handles message activities, send a dummy utterance to get it started.
skillActivity = (Activity)Activity.CreateMessageActivity();
skillActivity.Text = "Start echo skill";
break;
case "DialogSkillBot":
skillActivity = GetDialogSkillBotActivity(((FoundChoice)stepContext.Result).Value);
break;
default:
throw new Exception($"Unknown target skill id: {selectedSkill.Id}.");
}
// Create the SkillDialogArgs
var skillDialogArgs = new SkillDialogArgs
{
Skill = selectedSkill,
Activity = skillActivity
};
// We are manually creating the activity to send to the skill, ensure we add the ChannelData and Properties
// from the original activity so the skill gets them.
// Note: this is not necessary if we are just forwarding the current activity from context.
skillDialogArgs.Activity.ChannelData = stepContext.Context.Activity.ChannelData;
skillDialogArgs.Activity.Properties = stepContext.Context.Activity.Properties;
// Start the skillDialog with the arguments.
return await stepContext.BeginDialogAsync(nameof(SkillDialog), skillDialogArgs, cancellationToken);
}
// The SkillDialog has ended, render the results (if any) and restart MainDialog.
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
if (stepContext.Result != null)
{
var message = "Skill invocation complete.";
message += $" Result: {JsonConvert.SerializeObject(stepContext.Result)}";
await stepContext.Context.SendActivityAsync(MessageFactory.Text(message, inputHint: InputHints.IgnoringInput), cancellationToken: cancellationToken);
}
// Restart the main dialog with a different message the second time around
return await stepContext.ReplaceDialogAsync(InitialDialogId, "What else can I do for you?", cancellationToken);
}
// Helper method to create Choice elements for the actions supported by the skill
private IList<Choice> GetSkillActions(BotFrameworkSkill skill)
{
// Note: the bot would probably render this by readying the skill manifest
// we are just using hardcoded skill actions here for simplicity.
var choices = new List<Choice>();
switch (skill.Id)
{
case "EchoSkillBot":
choices.Add(new Choice("Messages"));
break;
case "DialogSkillBot":
choices.Add(new Choice("m:some message for tomorrow"));
choices.Add(new Choice("BookFlight"));
choices.Add(new Choice("OAuthTest"));
choices.Add(new Choice("mv:some message with value"));
choices.Add(new Choice("BookFlightWithValues"));
break;
}
return choices;
}
// Helper method to create the activity to be sent to the DialogSkillBot
private Activity GetDialogSkillBotActivity(string selectedOption)
{
// Note: in a real bot, the dialogArgs will be created dynamically based on the conversation
// and what each action requires, this code hardcodes the values to make things simpler.
// Send a message activity to the skill.
if (selectedOption.StartsWith("m:", StringComparison.CurrentCultureIgnoreCase))
{
var activity = (Activity)Activity.CreateMessageActivity();
activity.Text = selectedOption.Substring(2).Trim();
return activity;
}
// Send a message activity to the skill with some artificial parameters in value
if (selectedOption.StartsWith("mv:", StringComparison.CurrentCultureIgnoreCase))
{
var activity = (Activity)Activity.CreateMessageActivity();
activity.Text = selectedOption.Substring(3).Trim();
activity.Value = new BookingDetails { Destination = "New York" };
return activity;
}
// Send an event activity to the skill with "OAuthTest" in the name.
if (selectedOption.Equals("OAuthTest", StringComparison.CurrentCultureIgnoreCase))
{
var activity = (Activity)Activity.CreateEventActivity();
activity.Name = "OAuthTest";
return activity;
}
// Send an event activity to the skill with "BookFlight" in the name.
if (selectedOption.Equals("BookFlight", StringComparison.CurrentCultureIgnoreCase))
{
var activity = (Activity)Activity.CreateEventActivity();
activity.Name = "BookFlight";
return activity;
}
// Send an event activity to the skill "BookFlight" in the name and some testing values.
if (selectedOption.Equals("BookFlightWithValues", StringComparison.CurrentCultureIgnoreCase))
{
var activity = (Activity)Activity.CreateEventActivity();
activity.Name = "BookFlight";
activity.Value = new BookingDetails
{
Destination = "New York",
Origin = "Seattle"
};
return activity;
}
throw new Exception($"Unable to create dialogArgs for \"{selectedOption}\".");
}
}
}

Просмотреть файл

@ -0,0 +1,55 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Logging;
namespace Microsoft.BotBuilderSamples.DialogRootBot.Middleware
{
/// <summary>
/// A middleware that logs to an ILogger instance filtering ContinueConversation events coming from skill responses.
/// </summary>
public class LoggerMiddleware : IMiddleware
{
private readonly ILogger<BotFrameworkHttpAdapter> _logger;
public LoggerMiddleware(ILogger<BotFrameworkHttpAdapter> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken = default)
{
// Note: skill responses will show as ContinueConversation events, we don't log those,
// we only log incoming messages from users
if (turnContext.Activity.Type != ActivityTypes.Event && turnContext.Activity.Name != "ContinueConversation")
{
var message = $"User said: {turnContext.Activity.Text} Type: \"{turnContext.Activity.Type}\" Name: \"{turnContext.Activity.Name}\"";
_logger.LogInformation(message);
}
// Register outgoing handler.
turnContext.OnSendActivities(OutgoingHandler);
// Continue processing messages.
await next(cancellationToken);
}
private async Task<ResourceResponse[]> OutgoingHandler(ITurnContext turnContext, List<Activity> activities, Func<Task<ResourceResponse[]>> next)
{
foreach (var activity in activities)
{
var message = $"Bot said: {activity.Text} Type: \"{activity.Type}\" Name: \"{activity.Name}\"";
_logger.LogInformation(message);
}
return await next();
}
}
}

Просмотреть файл

@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace Microsoft.BotBuilderSamples.DialogRootBot
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}

Просмотреть файл

@ -0,0 +1,29 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:3978",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "default.htm",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"DialogRootBot31": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

Просмотреть файл

@ -0,0 +1,74 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Skills;
using Microsoft.Bot.Schema;
using Newtonsoft.Json.Linq;
namespace Microsoft.BotBuilderSamples.DialogRootBot
{
/// <summary>
/// A <see cref="SkillConversationIdFactory"/> that uses <see cref="IStorage"/> to store and retrieve <see cref="ConversationReference"/> instances.
/// </summary>
public class SkillConversationIdFactory : SkillConversationIdFactoryBase
{
private readonly IStorage _storage;
public SkillConversationIdFactory(IStorage storage)
{
_storage = storage ?? throw new ArgumentNullException(nameof(storage));
}
public override async Task<string> CreateSkillConversationIdAsync(ConversationReference conversationReference, CancellationToken cancellationToken)
{
if (conversationReference == null)
{
throw new ArgumentNullException(nameof(conversationReference));
}
if (string.IsNullOrWhiteSpace(conversationReference.Conversation.Id))
{
throw new NullReferenceException($"ConversationId in {nameof(conversationReference)} can't be null.");
}
if (string.IsNullOrWhiteSpace(conversationReference.ChannelId))
{
throw new NullReferenceException($"ChannelId in {nameof(conversationReference)} can't be null.");
}
var storageKey = $"{conversationReference.Conversation.Id}-{conversationReference.ChannelId}-skillconvo";
var skillConversationInfo = new Dictionary<string, object> { { storageKey, JObject.FromObject(conversationReference) } };
await _storage.WriteAsync(skillConversationInfo, cancellationToken).ConfigureAwait(false);
return storageKey;
}
public override async Task<ConversationReference> GetConversationReferenceAsync(string skillConversationId, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(skillConversationId))
{
throw new ArgumentNullException(nameof(skillConversationId));
}
var skillConversationInfo = await _storage.ReadAsync(new[] { skillConversationId }, cancellationToken).ConfigureAwait(false);
if (skillConversationInfo.Any())
{
var conversationInfo = ((JObject)skillConversationInfo[skillConversationId]).ToObject<ConversationReference>();
return conversationInfo;
}
return null;
}
public override async Task DeleteConversationReferenceAsync(string skillConversationId, CancellationToken cancellationToken)
{
await _storage.DeleteAsync(new[] { skillConversationId }, cancellationToken).ConfigureAwait(false);
}
}
}

Просмотреть файл

@ -0,0 +1,39 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using Microsoft.Bot.Builder.Skills;
using Microsoft.Extensions.Configuration;
namespace Microsoft.BotBuilderSamples.DialogRootBot
{
/// <summary>
/// A helper class that loads Skills information from configuration.
/// </summary>
public class SkillsConfiguration
{
public SkillsConfiguration(IConfiguration configuration)
{
var section = configuration?.GetSection("BotFrameworkSkills");
var skills = section?.Get<BotFrameworkSkill[]>();
if (skills != null)
{
foreach (var skill in skills)
{
Skills.Add(skill.Id, skill);
}
}
var skillHostEndpoint = configuration?.GetValue<string>(nameof(SkillHostEndpoint));
if (!string.IsNullOrWhiteSpace(skillHostEndpoint))
{
SkillHostEndpoint = new Uri(skillHostEndpoint);
}
}
public Uri SkillHostEndpoint { get; }
public Dictionary<string, BotFrameworkSkill> Skills { get; } = new Dictionary<string, BotFrameworkSkill>();
}
}

Просмотреть файл

@ -0,0 +1,91 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.BotFramework;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Builder.Integration.AspNet.Core.Skills;
using Microsoft.Bot.Builder.Skills;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.BotBuilderSamples.DialogRootBot.Authentication;
using Microsoft.BotBuilderSamples.DialogRootBot.Bots;
using Microsoft.BotBuilderSamples.DialogRootBot.Dialogs;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Microsoft.BotBuilderSamples.DialogRootBot
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddNewtonsoftJson();
// Register credential provider
services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();
// Register the skills configuration class
services.AddSingleton<SkillsConfiguration>();
// Register AuthConfiguration to enable custom claim validation.
services.AddSingleton(sp => new AuthenticationConfiguration { ClaimsValidator = new AllowedCallersClaimsValidator(sp.GetService<SkillsConfiguration>()) });
// Register the Bot Framework Adapter with error handling enabled.
// Note: some classes use the base BotAdapter so we add an extra registration that pulls the same instance.
services.AddSingleton<BotFrameworkHttpAdapter, AdapterWithErrorHandler>();
services.AddSingleton<BotAdapter>(sp => sp.GetService<BotFrameworkHttpAdapter>());
// Register the skills conversation ID factory, the client and the request handler.
services.AddSingleton<SkillConversationIdFactoryBase, SkillConversationIdFactory>();
services.AddHttpClient<SkillHttpClient>();
services.AddSingleton<ChannelServiceHandler, SkillHandler>();
// Register the storage we'll be using for User and Conversation state. (Memory is great for testing purposes.)
services.AddSingleton<IStorage, MemoryStorage>();
// Register Conversation state (used by the Dialog system itself).
services.AddSingleton<ConversationState>();
// Register the MainDialog that will be run by the bot.
services.AddSingleton<MainDialog>();
// Register the bot as a transient. In this case the ASP Controller is expecting an IBot.
services.AddTransient<IBot, RootBot<MainDialog>>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseDefaultFiles();
app.UseStaticFiles();
//app.UseHttpsRedirection(); Enable this to support https
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

Просмотреть файл

@ -0,0 +1,18 @@
{
"MicrosoftAppId": "TODO: Add here the App ID for the bot",
"MicrosoftAppPassword": "TODO: Add here the password for the bot",
"SkillHostEndpoint": "http://localhost:3978/api/skills/",
"BotFrameworkSkills": [
{
"Id": "EchoSkillBot",
"AppId": "TODO: Add here the App ID for the skill",
"SkillEndpoint": "http://localhost:39793/api/messages"
},
{
"Id": "DialogSkillBot",
"AppId": "TODO: Add here the App ID for the skill",
"SkillEndpoint": "http://localhost:39783/api/messages"
}
]
}

Просмотреть файл

@ -0,0 +1,420 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>DialogRootBot</title>
<style>
body {
margin: 0px;
padding: 0px;
font-family: Segoe UI;
}
html,
body {
height: 100%;
}
header {
background-image: url("data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 4638.9 651.6' style='enable-background:new 0 0 4638.9 651.6;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bfill:%2355A0E0;%7D .st1%7Bfill:none;%7D .st2%7Bfill:%230058A8;%7D .st3%7Bfill:%23328BD8;%7D .st4%7Bfill:%23B6DCF1;%7D .st5%7Bopacity:0.2;fill:url(%23SVGID_1_);enable-background:new ;%7D%0A%3C/style%3E%3Crect y='1.1' class='st0' width='4640' height='646.3'/%3E%3Cpath class='st1' d='M3987.8,323.6L4310.3,1.1h-65.6l-460.1,460.1c-17.5,17.5-46.1,17.5-63.6,0L3260.9,1.1H0v646.3h3660.3 L3889,418.7c17.5-17.5,46.1-17.5,63.6,0l228.7,228.7h66.6l-260.2-260.2C3970.3,369.8,3970.3,341.1,3987.8,323.6z'/%3E%3Cpath class='st2' d='M3784.6,461.2L4244.7,1.1h-983.9l460.1,460.1C3738.4,478.7,3767.1,478.7,3784.6,461.2z'/%3E%3Cpath class='st3' d='M4640,1.1h-329.8l-322.5,322.5c-17.5,17.5-17.5,46.1,0,63.6l260.2,260.2H4640L4640,1.1L4640,1.1z'/%3E%3Cpath class='st4' d='M3889,418.8l-228.7,228.7h521.1l-228.7-228.7C3935.2,401.3,3906.5,401.3,3889,418.8z'/%3E%3ClinearGradient id='SVGID_1_' gradientUnits='userSpaceOnUse' x1='3713.7576' y1='438.1175' x2='3911.4084' y2='14.2535' gradientTransform='matrix(1 0 0 -1 0 641.3969)'%3E%3Cstop offset='0' style='stop-color:%23FFFFFF;stop-opacity:0.5'/%3E%3Cstop offset='1' style='stop-color:%23FFFFFF'/%3E%3C/linearGradient%3E%3Cpath class='st5' d='M3952.7,124.5c-17.5-17.5-46.1-17.5-63.6,0l-523,523h1109.6L3952.7,124.5z'/%3E%3C/svg%3E%0A");
background-repeat: no-repeat;
background-size: 100%;
background-position: right;
background-color: #55A0E0;
width: 100%;
font-size: 44px;
height: 120px;
color: white;
padding: 30px 0 40px 0px;
display: inline-block;
}
.header-icon {
background-image: url("data:image/svg+xml;utf8,%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20xmlns%3Axlink%3D%22http%3A//www.w3.org/1999/xlink%22%20x%3D%220px%22%20y%3D%220px%22%0A%09%20viewBox%3D%220%200%20150.2%20125%22%20style%3D%22enable-background%3Anew%200%200%20150.2%20125%3B%22%20xml%3Aspace%3D%22preserve%22%3E%0A%3Cstyle%20type%3D%22text/css%22%3E%0A%09.st0%7Bfill%3Anone%3B%7D%0A%09.st1%7Bfill%3A%23FFFFFF%3B%7D%0A%3C/style%3E%0A%3Crect%20x%3D%220.5%22%20class%3D%22st0%22%20width%3D%22149.7%22%20height%3D%22125%22/%3E%0A%3Cg%3E%0A%09%3Cpath%20class%3D%22st1%22%20d%3D%22M59%2C102.9L21.8%2C66c-3.5-3.5-3.5-9.1%2C0-12.5l37-36.5l2.9%2C3l-37%2C36.4c-1.8%2C1.8-1.8%2C4.7%2C0%2C6.6l37.2%2C37L59%2C102.9z%22%0A%09%09/%3E%0A%3C/g%3E%0A%3Cg%3E%0A%09%3Cpath%20class%3D%22st1%22%20d%3D%22M92.5%2C102.9l-3-3l37.2-37c0.9-0.9%2C1.4-2%2C1.4-3.3c0-1.2-0.5-2.4-1.4-3.3L89.5%2C20l2.9-3l37.2%2C36.4%0A%09%09c1.7%2C1.7%2C2.6%2C3.9%2C2.6%2C6.3s-0.9%2C4.6-2.6%2C6.3L92.5%2C102.9z%22/%3E%0A%3C/g%3E%0A%3Cg%3E%0A%09%3Cpath%20class%3D%22st1%22%20d%3D%22M90.1%2C68.4c-4.5%2C0-8-3.5-8-8.1c0-4.5%2C3.5-8.1%2C8-8.1c4.4%2C0%2C8%2C3.7%2C8%2C8.1C98.1%2C64.7%2C94.4%2C68.4%2C90.1%2C68.4z%0A%09%09%20M90.1%2C56.5c-2.2%2C0-3.8%2C1.7-3.8%2C3.9c0%2C2.2%2C1.7%2C3.9%2C3.8%2C3.9c1.9%2C0%2C3.8-1.6%2C3.8-3.9S91.9%2C56.5%2C90.1%2C56.5z%22/%3E%0A%3C/g%3E%0A%3Cg%3E%0A%09%3Cpath%20class%3D%22st1%22%20d%3D%22M61.4%2C68.4c-4.5%2C0-8-3.5-8-8.1c0-4.5%2C3.5-8.1%2C8-8.1c4.4%2C0%2C8%2C3.7%2C8%2C8.1C69.5%2C64.7%2C65.8%2C68.4%2C61.4%2C68.4z%0A%09%09%20M61.4%2C56.5c-2.2%2C0-3.8%2C1.7-3.8%2C3.9c0%2C2.2%2C1.7%2C3.9%2C3.8%2C3.9c1.9%2C0%2C3.8-1.6%2C3.8-3.9S63.3%2C56.5%2C61.4%2C56.5z%22/%3E%0A%3C/g%3E%0A%3C/svg%3E%0A");
background-repeat: no-repeat;
float: left;
height: 140px;
width: 140px;
display: inline-block;
vertical-align: middle;
}
.header-text {
padding-left: 1%;
color: #FFFFFF;
font-family: "Segoe UI";
font-size: 72px;
font-weight: 300;
letter-spacing: 0.35px;
line-height: 96px;
display: inline-block;
vertical-align: middle;
}
.header-inner-container {
min-width: 480px;
max-width: 1366px;
margin-left: auto;
margin-right: auto;
vertical-align: middle;
}
.header-inner-container::after {
content: "";
clear: both;
display: table;
}
.main-content-area {
padding-left: 30px;
}
.content-title {
color: #000000;
font-family: "Segoe UI";
font-size: 46px;
font-weight: 300;
line-height: 62px;
}
.main-text {
color: #808080;
font-size: 24px;
font-family: "Segoe UI";
font-size: 24px;
font-weight: 200;
line-height: 32px;
}
.main-text-p1 {
padding-top: 48px;
padding-bottom: 28px;
}
.endpoint {
height: 32px;
width: 571px;
color: #808080;
font-family: "Segoe UI";
font-size: 24px;
font-weight: 200;
line-height: 32px;
padding-top: 28px;
}
.how-to-build-section {
padding-top: 20px;
padding-left: 30px;
}
.how-to-build-section>h3 {
font-size: 16px;
font-weight: 600;
letter-spacing: 0.35px;
line-height: 22px;
margin: 0 0 24px 0;
text-transform: uppercase;
}
.step-container {
display: flex;
align-items: stretch;
position: relative;
}
.step-container dl {
border-left: 1px solid #A0A0A0;
display: block;
padding: 0 24px;
margin: 0;
}
.step-container dl>dt::before {
background-color: white;
border: 1px solid #A0A0A0;
border-radius: 100%;
content: '';
left: 47px;
height: 11px;
position: absolute;
width: 11px;
}
.step-container dl>.test-bullet::before {
background-color: blue;
}
.step-container dl>dt {
display: block;
font-size: inherit;
font-weight: bold;
line-height: 20px;
}
.step-container dl>dd {
font-size: inherit;
line-height: 20px;
margin-left: 0;
padding-bottom: 32px;
}
.step-container:last-child dl {
border-left: 1px solid transparent;
}
.ctaLink {
background-color: transparent;
border: 1px solid transparent;
color: #006AB1;
cursor: pointer;
font-weight: 600;
padding: 0;
white-space: normal;
}
.ctaLink:focus {
outline: 1px solid #00bcf2;
}
.ctaLink:hover {
text-decoration: underline;
}
.step-icon {
display: flex;
height: 38px;
margin-right: 15px;
width: 38px;
}
.step-icon>div {
height: 30px;
width: 30px;
background-repeat: no-repeat;
}
.ms-logo-container {
min-width: 580px;
max-width: 980px;
margin-left: auto;
margin-right: auto;
left: 0;
right: 0;
transition: bottom 400ms;
}
.ms-logo {
float: right;
background-image: url("data:image/svg+xml;utf8,%0A%3Csvg%20version%3D%221.1%22%20id%3D%22MS-symbol%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20xmlns%3Axlink%3D%22http%3A//www.w3.org/1999/xlink%22%20x%3D%220px%22%20y%3D%220px%22%0A%09%20viewBox%3D%220%200%20400%20120%22%20style%3D%22enable-background%3Anew%200%200%20400%20120%3B%22%20xml%3Aspace%3D%22preserve%22%3E%0A%3Cstyle%20type%3D%22text/css%22%3E%0A%09.st0%7Bfill%3Anone%3B%7D%0A%09.st1%7Bfill%3A%23737474%3B%7D%0A%09.st2%7Bfill%3A%23D63F26%3B%7D%0A%09.st3%7Bfill%3A%23167D3E%3B%7D%0A%09.st4%7Bfill%3A%232E76BC%3B%7D%0A%09.st5%7Bfill%3A%23FDB813%3B%7D%0A%3C/style%3E%0A%3Crect%20x%3D%220.6%22%20class%3D%22st0%22%20width%3D%22398.7%22%20height%3D%22119%22/%3E%0A%3Cpath%20class%3D%22st1%22%20d%3D%22M171.3%2C38.4v43.2h-7.5V47.7h-0.1l-13.4%2C33.9h-5l-13.7-33.9h-0.1v33.9h-6.9V38.4h10.8l12.4%2C32h0.2l13.1-32H171.3%0A%09z%20M177.6%2C41.7c0-1.2%2C0.4-2.2%2C1.3-3c0.9-0.8%2C1.9-1.2%2C3.1-1.2c1.3%2C0%2C2.4%2C0.4%2C3.2%2C1.3c0.8%2C0.8%2C1.3%2C1.8%2C1.3%2C3c0%2C1.2-0.4%2C2.2-1.3%2C3%0A%09c-0.9%2C0.8-1.9%2C1.2-3.2%2C1.2s-2.3-0.4-3.1-1.2C178%2C43.8%2C177.6%2C42.8%2C177.6%2C41.7z%20M185.7%2C50.6v31h-7.3v-31H185.7z%20M207.8%2C76.3%0A%09c1.1%2C0%2C2.3-0.3%2C3.6-0.8c1.3-0.5%2C2.5-1.2%2C3.6-2v6.8c-1.2%2C0.7-2.5%2C1.2-4%2C1.5c-1.5%2C0.3-3.1%2C0.5-4.9%2C0.5c-4.6%2C0-8.3-1.4-11.1-4.3%0A%09c-2.9-2.9-4.3-6.6-4.3-11c0-5%2C1.5-9.1%2C4.4-12.3c2.9-3.2%2C7-4.8%2C12.4-4.8c1.4%2C0%2C2.7%2C0.2%2C4.1%2C0.5c1.4%2C0.4%2C2.5%2C0.8%2C3.3%2C1.2v7%0A%09c-1.1-0.8-2.3-1.5-3.4-1.9c-1.2-0.5-2.4-0.7-3.6-0.7c-2.9%2C0-5.2%2C0.9-7%2C2.8c-1.8%2C1.9-2.7%2C4.4-2.7%2C7.6c0%2C3.1%2C0.8%2C5.6%2C2.5%2C7.3%0A%09C202.6%2C75.4%2C204.9%2C76.3%2C207.8%2C76.3z%20M235.7%2C50.1c0.6%2C0%2C1.1%2C0%2C1.6%2C0.1s0.9%2C0.2%2C1.2%2C0.3v7.4c-0.4-0.3-0.9-0.5-1.7-0.8%0A%09c-0.7-0.3-1.6-0.4-2.7-0.4c-1.8%2C0-3.3%2C0.8-4.5%2C2.3c-1.2%2C1.5-1.9%2C3.8-1.9%2C7v15.6h-7.3v-31h7.3v4.9h0.1c0.7-1.7%2C1.7-3%2C3-4%0A%09C232.2%2C50.6%2C233.8%2C50.1%2C235.7%2C50.1z%20M238.9%2C66.6c0-5.1%2C1.4-9.2%2C4.3-12.2c2.9-3%2C6.9-4.5%2C12.1-4.5c4.8%2C0%2C8.6%2C1.4%2C11.3%2C4.3%0A%09c2.7%2C2.9%2C4.1%2C6.8%2C4.1%2C11.7c0%2C5-1.4%2C9-4.3%2C12c-2.9%2C3-6.8%2C4.5-11.8%2C4.5c-4.8%2C0-8.6-1.4-11.4-4.2C240.3%2C75.3%2C238.9%2C71.4%2C238.9%2C66.6z%0A%09%20M246.5%2C66.3c0%2C3.2%2C0.7%2C5.7%2C2.2%2C7.4c1.5%2C1.7%2C3.6%2C2.6%2C6.3%2C2.6c2.7%2C0%2C4.7-0.9%2C6.1-2.6c1.4-1.7%2C2.1-4.2%2C2.1-7.6c0-3.3-0.7-5.8-2.2-7.5%0A%09c-1.4-1.7-3.4-2.5-6-2.5c-2.7%2C0-4.7%2C0.9-6.2%2C2.7C247.2%2C60.5%2C246.5%2C63%2C246.5%2C66.3z%20M281.5%2C58.8c0%2C1%2C0.3%2C1.9%2C1%2C2.5%0A%09c0.7%2C0.6%2C2.1%2C1.3%2C4.4%2C2.2c2.9%2C1.2%2C5%2C2.5%2C6.1%2C3.9c1.2%2C1.5%2C1.8%2C3.2%2C1.8%2C5.3c0%2C2.9-1.1%2C5.3-3.4%2C7c-2.2%2C1.8-5.3%2C2.7-9.1%2C2.7%0A%09c-1.3%2C0-2.7-0.2-4.3-0.5c-1.6-0.3-2.9-0.7-4-1.2v-7.2c1.3%2C0.9%2C2.8%2C1.7%2C4.3%2C2.2c1.5%2C0.5%2C2.9%2C0.8%2C4.2%2C0.8c1.6%2C0%2C2.9-0.2%2C3.6-0.7%0A%09c0.8-0.5%2C1.2-1.2%2C1.2-2.3c0-1-0.4-1.9-1.2-2.5c-0.8-0.7-2.4-1.5-4.6-2.4c-2.7-1.1-4.6-2.4-5.7-3.8c-1.1-1.4-1.7-3.2-1.7-5.4%0A%09c0-2.8%2C1.1-5.1%2C3.3-6.9c2.2-1.8%2C5.1-2.7%2C8.6-2.7c1.1%2C0%2C2.3%2C0.1%2C3.6%2C0.4c1.3%2C0.2%2C2.5%2C0.6%2C3.4%2C0.9v6.9c-1-0.6-2.1-1.2-3.4-1.7%0A%09c-1.3-0.5-2.6-0.7-3.8-0.7c-1.4%2C0-2.5%2C0.3-3.2%2C0.8C281.9%2C57.1%2C281.5%2C57.8%2C281.5%2C58.8z%20M297.9%2C66.6c0-5.1%2C1.4-9.2%2C4.3-12.2%0A%09c2.9-3%2C6.9-4.5%2C12.1-4.5c4.8%2C0%2C8.6%2C1.4%2C11.3%2C4.3c2.7%2C2.9%2C4.1%2C6.8%2C4.1%2C11.7c0%2C5-1.4%2C9-4.3%2C12c-2.9%2C3-6.8%2C4.5-11.8%2C4.5%0A%09c-4.8%2C0-8.6-1.4-11.4-4.2C299.4%2C75.3%2C297.9%2C71.4%2C297.9%2C66.6z%20M305.5%2C66.3c0%2C3.2%2C0.7%2C5.7%2C2.2%2C7.4c1.5%2C1.7%2C3.6%2C2.6%2C6.3%2C2.6%0A%09c2.7%2C0%2C4.7-0.9%2C6.1-2.6c1.4-1.7%2C2.1-4.2%2C2.1-7.6c0-3.3-0.7-5.8-2.2-7.5c-1.4-1.7-3.4-2.5-6-2.5c-2.7%2C0-4.7%2C0.9-6.2%2C2.7%0A%09C306.3%2C60.5%2C305.5%2C63%2C305.5%2C66.3z%20M353.9%2C56.6h-10.9v25h-7.4v-25h-5.2v-6h5.2v-4.3c0-3.3%2C1.1-5.9%2C3.2-8c2.1-2.1%2C4.8-3.1%2C8.1-3.1%0A%09c0.9%2C0%2C1.7%2C0%2C2.4%2C0.1c0.7%2C0.1%2C1.3%2C0.2%2C1.8%2C0.4V42c-0.2-0.1-0.7-0.3-1.3-0.5c-0.6-0.2-1.3-0.3-2.1-0.3c-1.5%2C0-2.7%2C0.5-3.5%2C1.4%0A%09s-1.2%2C2.4-1.2%2C4.2v3.7h10.9v-7l7.3-2.2v9.2h7.4v6h-7.4v14.5c0%2C1.9%2C0.3%2C3.3%2C1%2C4c0.7%2C0.8%2C1.8%2C1.2%2C3.3%2C1.2c0.4%2C0%2C0.9-0.1%2C1.5-0.3%0A%09c0.6-0.2%2C1.1-0.4%2C1.6-0.7v6c-0.5%2C0.3-1.2%2C0.5-2.3%2C0.7c-1.1%2C0.2-2.1%2C0.3-3.2%2C0.3c-3.1%2C0-5.4-0.8-6.9-2.5c-1.5-1.6-2.3-4.1-2.3-7.4%0A%09V56.6z%22/%3E%0A%3Cg%3E%0A%09%3Crect%20x%3D%2231%22%20y%3D%2224%22%20class%3D%22st2%22%20width%3D%2234.2%22%20height%3D%2234.2%22/%3E%0A%09%3Crect%20x%3D%2268.8%22%20y%3D%2224%22%20class%3D%22st3%22%20width%3D%2234.2%22%20height%3D%2234.2%22/%3E%0A%09%3Crect%20x%3D%2231%22%20y%3D%2261.8%22%20class%3D%22st4%22%20width%3D%2234.2%22%20height%3D%2234.2%22/%3E%0A%09%3Crect%20x%3D%2268.8%22%20y%3D%2261.8%22%20class%3D%22st5%22%20width%3D%2234.2%22%20height%3D%2234.2%22/%3E%0A%3C/g%3E%0A%3C/svg%3E%0A");
}
.ms-logo-container>div {
min-height: 60px;
width: 150px;
background-repeat: no-repeat;
}
.row {
padding: 90px 0px 0 20px;
min-width: 480px;
max-width: 1366px;
margin-left: auto;
margin-right: auto;
}
.column {
float: left;
width: 45%;
padding-right: 20px;
}
.row:after {
content: "";
display: table;
clear: both;
}
a {
text-decoration: none;
}
.download-the-emulator {
height: 20px;
color: #0063B1;
font-size: 15px;
line-height: 20px;
padding-bottom: 70px;
}
.how-to-iframe {
max-width: 700px !important;
min-width: 650px !important;
height: 700px !important;
}
.remove-frame-height {
height: 10px;
}
@media only screen and (max-width: 1300px) {
.ms-logo {
padding-top: 30px;
}
.header-text {
font-size: 40x;
}
.column {
float: none;
padding-top: 30px;
width: 100%;
}
.ms-logo-container {
padding-top: 30px;
min-width: 480px;
max-width: 650px;
margin-left: auto;
margin-right: auto;
}
.row {
padding: 20px 0px 0 20px;
min-width: 480px;
max-width: 650px;
margin-left: auto;
margin-right: auto;
}
}
@media only screen and (max-width: 1370px) {
header {
background-color: #55A0E0;
background-size: auto 200px;
}
}
@media only screen and (max-width: 1230px) {
header {
background-color: #55A0E0;
background-size: auto 200px;
}
.header-text {
font-size: 44px;
}
.header-icon {
height: 120px;
width: 120px;
}
}
@media only screen and (max-width: 1000px) {
header {
background-color: #55A0E0;
background-image: none;
}
}
@media only screen and (max-width: 632px) {
.header-text {
font-size: 32px;
}
.row {
padding: 10px 0px 0 10px;
max-width: 490px !important;
min-width: 410px !important;
}
.endpoint {
font-size: 25px;
}
.main-text {
font-size: 20px;
}
.step-container dl>dd {
font-size: 14px;
}
.column {
padding-right: 5px;
}
.header-icon {
height: 110px;
width: 110px;
}
.how-to-iframe {
max-width: 480px !important;
min-width: 400px !important;
height: 650px !important;
overflow: hidden;
}
}
.remove-frame-height {
max-height: 10px;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function () {
loadFrame();
});
var loadFrame = function () {
var iframe = document.createElement('iframe');
iframe.setAttribute("id", "iframe");
var offLineHTMLContent = "";
var frameElement = document.getElementById("how-to-iframe");
if (window.navigator.onLine) {
iframe.src = 'https://docs.botframework.com/static/abs/pages/f5.htm';
iframe.setAttribute("scrolling", "no");
iframe.setAttribute("frameborder", "0");
iframe.setAttribute("width", "100%");
iframe.setAttribute("height", "100%");
var frameDiv = document.getElementById("how-to-iframe");
frameDiv.appendChild(iframe);
} else {
frameElement.classList.add("remove-frame-height");
}
};
</script>
</head>
<body>
<header class="header">
<div class="header-inner-container">
<div class="header-icon" style="display: inline-block"></div>
<div class="header-text" style="display: inline-block">DialogRootBot Bot</div>
</div>
</header>
<div class="row">
<div class="column" class="main-content-area">
<div class="content-title">Your bot is ready!</div>
<div class="main-text main-text-p1">You can test your bot in the Bot Framework Emulator<br />
by connecting to http://localhost:3978/api/messages.</div>
<div class="main-text download-the-emulator"><a class="ctaLink"
href="https://aka.ms/bot-framework-F5-download-emulator" target="_blank">Download the Emulator</a>
</div>
<div class="main-text">Visit <a class="ctaLink" href="https://aka.ms/bot-framework-F5-abs-home"
target="_blank">Azure
Bot Service</a> to register your bot and add it to<br />
various channels. The bot's endpoint URL typically looks
like this:</div>
<div class="endpoint">https://<i>your_bots_hostname</i>/api/messages</div>
</div>
<div class="column how-to-iframe" id="how-to-iframe"></div>
</div>
<div class="ms-logo-container">
<div class="ms-logo"></div>
</div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,179 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.TraceExtensions;
using Microsoft.Bot.Schema;
using Microsoft.BotBuilderSamples.DialogSkillBot.Dialogs;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
namespace Microsoft.BotBuilderSamples.DialogSkillBot.Bots
{
/// <summary>
/// A root dialog that can route activities sent to the skill to different dialogs.
/// </summary>
public class ActivityRouterDialog : ComponentDialog
{
private readonly DialogSkillBotRecognizer _luisRecognizer;
public ActivityRouterDialog(DialogSkillBotRecognizer luisRecognizer, IConfiguration configuration)
: base(nameof(ActivityRouterDialog))
{
_luisRecognizer = luisRecognizer;
AddDialog(new BookingDialog());
AddDialog(new OAuthTestDialog(configuration));
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[] { ProcessActivityAsync }));
// The initial child Dialog to run.
InitialDialogId = nameof(WaterfallDialog);
}
private async Task<DialogTurnResult> ProcessActivityAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
// A skill can send trace activities if needed :)
await stepContext.Context.TraceActivityAsync($"{GetType().Name}.ProcessActivityAsync()", label: $"Got ActivityType: {stepContext.Context.Activity.Type}", cancellationToken: cancellationToken);
switch (stepContext.Context.Activity.Type)
{
case ActivityTypes.Message:
return await OnMessageActivityAsync(stepContext, cancellationToken);
case ActivityTypes.Invoke:
return await OnInvokeActivityAsync(stepContext, cancellationToken);
case ActivityTypes.Event:
return await OnEventActivityAsync(stepContext, cancellationToken);
default:
// We didn't get an activity type we can handle.
await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Unrecognized ActivityType: \"{stepContext.Context.Activity.Type}\".", inputHint: InputHints.IgnoringInput), cancellationToken);
return new DialogTurnResult(DialogTurnStatus.Complete);
}
}
// This method performs different tasks based on the event name.
private async Task<DialogTurnResult> OnEventActivityAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var activity = stepContext.Context.Activity;
await stepContext.Context.TraceActivityAsync($"{GetType().Name}.OnEventActivityAsync()", label: $"Name: {activity.Name}. Value: {GetObjectAsJsonString(activity.Value)}", cancellationToken: cancellationToken);
// Resolve what to execute based on the event name.
switch (activity.Name)
{
case "BookFlight":
var bookingDetails = new BookingDetails();
if (activity.Value != null)
{
bookingDetails = JsonConvert.DeserializeObject<BookingDetails>(JsonConvert.SerializeObject(activity.Value));
}
// Start the booking dialog
var bookingDialog = FindDialog(nameof(BookingDialog));
return await stepContext.BeginDialogAsync(bookingDialog.Id, bookingDetails, cancellationToken);
case "OAuthTest":
// Start the OAuthTestDialog
var oAuthDialog = FindDialog(nameof(OAuthTestDialog));
return await stepContext.BeginDialogAsync(oAuthDialog.Id, null, cancellationToken);
default:
// We didn't get an event name we can handle.
await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Unrecognized EventName: \"{activity.Name}\".", inputHint: InputHints.IgnoringInput), cancellationToken);
return new DialogTurnResult(DialogTurnStatus.Complete);
}
}
// This method responds right away using an invokeResponse based on the activity name property.
private async Task<DialogTurnResult> OnInvokeActivityAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var activity = stepContext.Context.Activity;
await stepContext.Context.TraceActivityAsync($"{GetType().Name}.OnInvokeActivityAsync()", label: $"Name: {activity.Name}. Value: {GetObjectAsJsonString(activity.Value)}", cancellationToken: cancellationToken);
// Resolve what to execute based on the invoke name.
switch (activity.Name)
{
case "GetWeather":
var location = new Location();
if (activity.Value != null)
{
location = JsonConvert.DeserializeObject<Location>(JsonConvert.SerializeObject(activity.Value));
}
var lookingIntoItMessage = "Getting your weather forecast...";
await stepContext.Context.SendActivityAsync(MessageFactory.Text($"{lookingIntoItMessage} \n\nValue parameters: {JsonConvert.SerializeObject(location)}", lookingIntoItMessage, inputHint: InputHints.IgnoringInput), cancellationToken);
// Create and return an invoke activity with the weather results.
var invokeResponseActivity = new Activity(type: "invokeResponse")
{
Value = new InvokeResponse
{
Body = new[]
{
"New York, NY, Clear, 56 F",
"Bellevue, WA, Mostly Cloudy, 48 F"
},
Status = (int)HttpStatusCode.OK
}
};
await stepContext.Context.SendActivityAsync(invokeResponseActivity, cancellationToken);
break;
default:
// We didn't get an invoke name we can handle.
await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Unrecognized InvokeName: \"{activity.Name}\".", inputHint: InputHints.IgnoringInput), cancellationToken);
break;
}
return new DialogTurnResult(DialogTurnStatus.Complete);
}
// This method just gets a message activity and runs it through LUIS.
// A developer can chose to start a dialog based on the LUIS results (not implemented here).
private async Task<DialogTurnResult> OnMessageActivityAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var activity = stepContext.Context.Activity;
await stepContext.Context.TraceActivityAsync($"{GetType().Name}.OnMessageActivityAsync()", label: $"Text: \"{activity.Text}\". Value: {GetObjectAsJsonString(activity.Value)}", cancellationToken: cancellationToken);
if (!_luisRecognizer.IsConfigured)
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("NOTE: LUIS is not configured. To enable all capabilities, add 'LuisAppId', 'LuisAPIKey' and 'LuisAPIHostName' to the appsettings.json file.", inputHint: InputHints.IgnoringInput), cancellationToken);
}
else
{
// Call LUIS with the utterance.
var luisResult = await _luisRecognizer.RecognizeAsync(stepContext.Context, cancellationToken);
// Create a message showing the LUIS results.
var sb = new StringBuilder();
sb.AppendLine($"LUIS results for \"{activity.Text}\":");
var (intent, intentScore) = luisResult.Intents.FirstOrDefault(x => x.Value.Equals(luisResult.Intents.Values.Max()));
sb.AppendLine($"Intent: \"{intent}\" Score: {intentScore.Score}");
sb.AppendLine($"Entities found: {luisResult.Entities.Count - 1}");
foreach (var luisResultEntity in luisResult.Entities)
{
if (!luisResultEntity.Key.Equals("$instance"))
{
sb.AppendLine($"* {luisResultEntity.Key}");
}
}
await stepContext.Context.SendActivityAsync(MessageFactory.Text(sb.ToString(), inputHint: InputHints.IgnoringInput), cancellationToken);
}
return new DialogTurnResult(DialogTurnStatus.Complete);
}
private string GetObjectAsJsonString(object value)
{
return value == null ? string.Empty : JsonConvert.SerializeObject(value);
}
}
}

Просмотреть файл

@ -0,0 +1,80 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Schema;
namespace Microsoft.BotBuilderSamples.DialogSkillBot.Bots
{
public class SkillBot<T> : IBot
where T : Dialog
{
private readonly ConversationState _conversationState;
private readonly Dialog _mainDialog;
public SkillBot(ConversationState conversationState, T mainDialog)
{
_conversationState = conversationState;
_mainDialog = mainDialog;
}
public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default)
{
var dialogSet = new DialogSet(_conversationState.CreateProperty<DialogState>("DialogState")) { TelemetryClient = _mainDialog.TelemetryClient };
dialogSet.Add(_mainDialog);
var dialogContext = await dialogSet.CreateContextAsync(turnContext, cancellationToken).ConfigureAwait(false);
if (turnContext.Activity.Type == ActivityTypes.EndOfConversation && dialogContext.Stack.Any())
{
// Handle remote cancellation request if we have something in the stack.
var activeDialogContext = GetActiveDialogContext(dialogContext);
// Send cancellation message to the top dialog in the stack to ensure all the parents are canceled in the right order.
await activeDialogContext.CancelAllDialogsAsync(true, cancellationToken: cancellationToken);
var remoteCancelText = "**SkillBot.** The current mainDialog in the skill was **canceled** by a request **from the host**, do some cleanup here if needed.";
await turnContext.SendActivityAsync(MessageFactory.Text(remoteCancelText, inputHint: InputHints.IgnoringInput), cancellationToken);
}
else
{
// Run the Dialog with the new message Activity and capture the results so we can send end of conversation if needed.
var result = await dialogContext.ContinueDialogAsync(cancellationToken).ConfigureAwait(false);
if (result.Status == DialogTurnStatus.Empty)
{
var startMessageText = $"**SkillBot.** Starting {_mainDialog.Id} (.Net Core 3.1).";
await turnContext.SendActivityAsync(MessageFactory.Text(startMessageText, inputHint: InputHints.IgnoringInput), cancellationToken);
result = await dialogContext.BeginDialogAsync(_mainDialog.Id, null, cancellationToken).ConfigureAwait(false);
}
// Send end of conversation if it is complete
if (result.Status == DialogTurnStatus.Complete || result.Status == DialogTurnStatus.Cancelled)
{
var endMessageText = "**SkillBot.** The mainDialog in the skill has **completed**. Sending EndOfConversation.";
await turnContext.SendActivityAsync(MessageFactory.Text(endMessageText, inputHint: InputHints.IgnoringInput), cancellationToken);
// Send End of conversation at the end.
var activity = new Activity(ActivityTypes.EndOfConversation) { Value = result.Result };
await turnContext.SendActivityAsync(activity, cancellationToken);
}
}
// Save any state changes that might have occured during the turn.
await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken);
}
// Recursively walk up the DC stack to find the active DC.
private DialogContext GetActiveDialogContext(DialogContext dialogContext)
{
var child = dialogContext.Child;
if (child == null)
{
return dialogContext;
}
return GetActiveDialogContext(child);
}
}
}

Просмотреть файл

@ -0,0 +1,339 @@
{
"luis_schema_version": "3.2.0",
"versionId": "0.1",
"name": "FlightBooking",
"desc": "Luis Model for CoreBot",
"culture": "en-us",
"tokenizerVersion": "1.0.0",
"intents": [
{
"name": "BookFlight"
},
{
"name": "Cancel"
},
{
"name": "GetWeather"
},
{
"name": "None"
}
],
"entities": [],
"composites": [
{
"name": "From",
"children": [
"Airport"
],
"roles": []
},
{
"name": "To",
"children": [
"Airport"
],
"roles": []
}
],
"closedLists": [
{
"name": "Airport",
"subLists": [
{
"canonicalForm": "Paris",
"list": [
"paris",
"cdg"
]
},
{
"canonicalForm": "London",
"list": [
"london",
"lhr"
]
},
{
"canonicalForm": "Berlin",
"list": [
"berlin",
"txl"
]
},
{
"canonicalForm": "New York",
"list": [
"new york",
"jfk"
]
},
{
"canonicalForm": "Seattle",
"list": [
"seattle",
"sea"
]
}
],
"roles": []
}
],
"patternAnyEntities": [],
"regex_entities": [],
"prebuiltEntities": [
{
"name": "datetimeV2",
"roles": []
}
],
"model_features": [],
"regex_features": [],
"patterns": [],
"utterances": [
{
"text": "book a flight",
"intent": "BookFlight",
"entities": []
},
{
"text": "book a flight from new york",
"intent": "BookFlight",
"entities": [
{
"entity": "From",
"startPos": 19,
"endPos": 26
}
]
},
{
"text": "book a flight from seattle",
"intent": "BookFlight",
"entities": [
{
"entity": "From",
"startPos": 19,
"endPos": 25
}
]
},
{
"text": "book a hotel in new york",
"intent": "None",
"entities": []
},
{
"text": "book a restaurant",
"intent": "None",
"entities": []
},
{
"text": "book flight from london to paris on feb 14th",
"intent": "BookFlight",
"entities": [
{
"entity": "From",
"startPos": 17,
"endPos": 22
},
{
"entity": "To",
"startPos": 27,
"endPos": 31
}
]
},
{
"text": "book flight to berlin on feb 14th",
"intent": "BookFlight",
"entities": [
{
"entity": "To",
"startPos": 15,
"endPos": 20
}
]
},
{
"text": "book me a flight from london to paris",
"intent": "BookFlight",
"entities": [
{
"entity": "From",
"startPos": 22,
"endPos": 27
},
{
"entity": "To",
"startPos": 32,
"endPos": 36
}
]
},
{
"text": "bye",
"intent": "Cancel",
"entities": []
},
{
"text": "cancel booking",
"intent": "Cancel",
"entities": []
},
{
"text": "exit",
"intent": "Cancel",
"entities": []
},
{
"text": "find an airport near me",
"intent": "None",
"entities": []
},
{
"text": "flight to paris",
"intent": "BookFlight",
"entities": [
{
"entity": "To",
"startPos": 10,
"endPos": 14
}
]
},
{
"text": "flight to paris from london on feb 14th",
"intent": "BookFlight",
"entities": [
{
"entity": "To",
"startPos": 10,
"endPos": 14
},
{
"entity": "From",
"startPos": 21,
"endPos": 26
}
]
},
{
"text": "fly from berlin to paris on may 5th",
"intent": "BookFlight",
"entities": [
{
"entity": "From",
"startPos": 9,
"endPos": 14
},
{
"entity": "To",
"startPos": 19,
"endPos": 23
}
]
},
{
"text": "go to paris",
"intent": "BookFlight",
"entities": [
{
"entity": "To",
"startPos": 6,
"endPos": 10
}
]
},
{
"text": "going from paris to berlin",
"intent": "BookFlight",
"entities": [
{
"entity": "From",
"startPos": 11,
"endPos": 15
},
{
"entity": "To",
"startPos": 20,
"endPos": 25
}
]
},
{
"text": "i'd like to rent a car",
"intent": "None",
"entities": []
},
{
"text": "ignore",
"intent": "Cancel",
"entities": []
},
{
"text": "travel from new york to paris",
"intent": "BookFlight",
"entities": [
{
"entity": "From",
"startPos": 12,
"endPos": 19
},
{
"entity": "To",
"startPos": 24,
"endPos": 28
}
]
},
{
"text": "travel to new york",
"intent": "BookFlight",
"entities": [
{
"entity": "To",
"startPos": 10,
"endPos": 17
}
]
},
{
"text": "travel to paris",
"intent": "BookFlight",
"entities": [
{
"entity": "To",
"startPos": 10,
"endPos": 14
}
]
},
{
"text": "what's the forecast for this friday?",
"intent": "GetWeather",
"entities": []
},
{
"text": "what's the weather like for tomorrow",
"intent": "GetWeather",
"entities": []
},
{
"text": "what's the weather like in new york",
"intent": "GetWeather",
"entities": []
},
{
"text": "what's the weather like?",
"intent": "GetWeather",
"entities": []
},
{
"text": "winter is coming",
"intent": "None",
"entities": []
}
],
"settings": []
}

Просмотреть файл

@ -0,0 +1,34 @@
// 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 Microsoft.BotBuilderSamples.DialogSkillBot.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;
}
[HttpGet]
[HttpPost]
public async Task PostAsync()
{
await _adapter.ProcessAsync(Request, Response, _bot);
}
}
}

Просмотреть файл

@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>latest</LangVersion>
<AssemblyName>Microsoft.BotBuilderSamples.DialogSkillBot</AssemblyName>
<RootNamespace>Microsoft.BotBuilderSamples.DialogSkillBot</RootNamespace>
<UserSecretsId>826727db-61ca-4ace-bda5-40ba6b4cf070</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.0" />
<PackageReference Include="Microsoft.Recognizers.Text.DataTypes.TimexExpression" Version="1.1.5" />
</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.AI.LUIS\Microsoft.Bot.Builder.AI.Luis.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>

Просмотреть файл

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Newtonsoft.Json;
namespace Microsoft.BotBuilderSamples.DialogSkillBot.Dialogs
{
public class BookingDetails
{
[JsonProperty("destination")]
public string Destination { get; set; }
[JsonProperty("origin")]
public string Origin { get; set; }
[JsonProperty("travelDate")]
public string TravelDate { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,105 @@
// 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.Recognizers.Text.DataTypes.TimexExpression;
namespace Microsoft.BotBuilderSamples.DialogSkillBot.Dialogs
{
public class BookingDialog : CancelAndHelpDialog
{
private const string DestinationStepMsgText = "Where would you like to travel to?";
private const string OriginStepMsgText = "Where are you traveling from?";
public BookingDialog()
: base(nameof(BookingDialog))
{
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(new ConfirmPrompt(nameof(ConfirmPrompt)));
AddDialog(new DateResolverDialog());
AddDialog(
new WaterfallDialog(
nameof(WaterfallDialog),
new WaterfallStep[] { DestinationStepAsync, OriginStepAsync, TravelDateStepAsync, ConfirmStepAsync, FinalStepAsync }));
// The initial child Dialog to run.
InitialDialogId = nameof(WaterfallDialog);
}
private static bool IsAmbiguous(string timex)
{
var timexProperty = new TimexProperty(timex);
return !timexProperty.Types.Contains(Constants.TimexTypes.Definite);
}
private async Task<DialogTurnResult> DestinationStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var bookingDetails = (BookingDetails)stepContext.Options;
if (bookingDetails.Destination == null)
{
var promptMessage = MessageFactory.Text(DestinationStepMsgText, DestinationStepMsgText, InputHints.ExpectingInput);
return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = promptMessage }, cancellationToken);
}
return await stepContext.NextAsync(bookingDetails.Destination, cancellationToken);
}
private async Task<DialogTurnResult> OriginStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var bookingDetails = (BookingDetails)stepContext.Options;
bookingDetails.Destination = (string)stepContext.Result;
if (bookingDetails.Origin == null)
{
var promptMessage = MessageFactory.Text(OriginStepMsgText, OriginStepMsgText, InputHints.ExpectingInput);
return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = promptMessage }, cancellationToken);
}
return await stepContext.NextAsync(bookingDetails.Origin, cancellationToken);
}
private async Task<DialogTurnResult> TravelDateStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var bookingDetails = (BookingDetails)stepContext.Options;
bookingDetails.Origin = (string)stepContext.Result;
if (bookingDetails.TravelDate == null || IsAmbiguous(bookingDetails.TravelDate))
{
return await stepContext.BeginDialogAsync(nameof(DateResolverDialog), bookingDetails.TravelDate, cancellationToken);
}
return await stepContext.NextAsync(bookingDetails.TravelDate, cancellationToken);
}
private async Task<DialogTurnResult> ConfirmStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var bookingDetails = (BookingDetails)stepContext.Options;
bookingDetails.TravelDate = (string)stepContext.Result;
var messageText = $"Please confirm, I have you traveling to: {bookingDetails.Destination} from: {bookingDetails.Origin} on: {bookingDetails.TravelDate}. Is this correct?";
var promptMessage = MessageFactory.Text(messageText, messageText, InputHints.ExpectingInput);
return await stepContext.PromptAsync(nameof(ConfirmPrompt), new PromptOptions { Prompt = promptMessage }, cancellationToken);
}
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
if ((bool)stepContext.Result)
{
var bookingDetails = (BookingDetails)stepContext.Options;
return await stepContext.EndDialogAsync(bookingDetails, cancellationToken);
}
return await stepContext.EndDialogAsync(null, cancellationToken);
}
}
}

Просмотреть файл

@ -0,0 +1,58 @@
// 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 Microsoft.BotBuilderSamples.DialogSkillBot.Dialogs
{
public class CancelAndHelpDialog : ComponentDialog
{
private const string HelpMsgText = "Show help here";
private const string CancelMsgText = "Cancelling...";
public CancelAndHelpDialog(string id)
: base(id)
{
}
protected override async Task<DialogTurnResult> OnContinueDialogAsync(DialogContext innerDc, CancellationToken cancellationToken = default)
{
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)
{
if (innerDc.Context.Activity.Type == ActivityTypes.Message)
{
var text = innerDc.Context.Activity.Text.ToLowerInvariant();
switch (text)
{
case "help":
case "?":
var helpMessage = MessageFactory.Text(HelpMsgText, HelpMsgText, InputHints.ExpectingInput);
await innerDc.Context.SendActivityAsync(helpMessage, cancellationToken);
return new DialogTurnResult(DialogTurnStatus.Waiting);
case "cancel":
case "quit":
var cancelMessage = MessageFactory.Text(CancelMsgText, CancelMsgText, InputHints.IgnoringInput);
await innerDc.Context.SendActivityAsync(cancelMessage, cancellationToken);
return await innerDc.CancelAllDialogsAsync(true, cancellationToken: cancellationToken);
}
}
return null;
}
}
}

Просмотреть файл

@ -0,0 +1,88 @@
// 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.Recognizers.Text.DataTypes.TimexExpression;
namespace Microsoft.BotBuilderSamples.DialogSkillBot.Dialogs
{
public class DateResolverDialog : CancelAndHelpDialog
{
private const string PromptMsgText = "When would you like to travel?";
private const string RepromptMsgText = "I'm sorry, to make your booking please enter a full travel date including Day Month and Year.";
public DateResolverDialog(string id = null)
: base(id ?? nameof(DateResolverDialog))
{
AddDialog(new DateTimePrompt(nameof(DateTimePrompt), DateTimePromptValidator));
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[] { InitialStepAsync, FinalStepAsync }));
// The initial child Dialog to run.
InitialDialogId = nameof(WaterfallDialog);
}
private static Task<bool> DateTimePromptValidator(PromptValidatorContext<IList<DateTimeResolution>> promptContext, CancellationToken cancellationToken)
{
if (promptContext.Recognized.Succeeded)
{
// This value will be a TIMEX. And we are only interested in a Date so grab the first result and drop the Time part.
// TIMEX is a format that represents DateTime expressions that include some ambiguity. e.g. missing a Year.
var timex = promptContext.Recognized.Value[0].Timex.Split('T')[0];
// If this is a definite Date including year, month and day we are good otherwise reprompt.
// A better solution might be to let the user know what part is actually missing.
var isDefinite = new TimexProperty(timex).Types.Contains(Constants.TimexTypes.Definite);
return Task.FromResult(isDefinite);
}
return Task.FromResult(false);
}
private async Task<DialogTurnResult> InitialStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var timex = (string)stepContext.Options;
var promptMessage = MessageFactory.Text(PromptMsgText, PromptMsgText, InputHints.ExpectingInput);
var repromptMessage = MessageFactory.Text(RepromptMsgText, RepromptMsgText, InputHints.ExpectingInput);
if (timex == null)
{
// We were not given any date at all so prompt the user.
return await stepContext.PromptAsync(
nameof(DateTimePrompt),
new PromptOptions
{
Prompt = promptMessage,
RetryPrompt = repromptMessage,
}, cancellationToken);
}
// We have a Date we just need to check it is unambiguous.
var timexProperty = new TimexProperty(timex);
if (!timexProperty.Types.Contains(Constants.TimexTypes.Definite))
{
// This is essentially a "reprompt" of the data we were given up front.
return await stepContext.PromptAsync(
nameof(DateTimePrompt),
new PromptOptions
{
Prompt = repromptMessage,
}, cancellationToken);
}
return await stepContext.NextAsync(new List<DateTimeResolution> { new DateTimeResolution { Timex = timex } }, cancellationToken);
}
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var timex = ((List<DateTimeResolution>)stepContext.Result)[0].Timex;
return await stepContext.EndDialogAsync(timex, cancellationToken);
}
}
}

Просмотреть файл

@ -0,0 +1,41 @@
// 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.AI.Luis;
using Microsoft.Extensions.Configuration;
namespace Microsoft.BotBuilderSamples.DialogSkillBot.Dialogs
{
public class DialogSkillBotRecognizer : IRecognizer
{
private readonly LuisRecognizer _recognizer;
public DialogSkillBotRecognizer(IConfiguration configuration)
{
var luisIsConfigured = !string.IsNullOrEmpty(configuration["LuisAppId"]) && !string.IsNullOrEmpty(configuration["LuisAPIKey"]) && !string.IsNullOrEmpty(configuration["LuisAPIHostName"]);
if (luisIsConfigured)
{
var luisApplication = new LuisApplication(
configuration["LuisAppId"],
configuration["LuisAPIKey"],
"https://" + configuration["LuisAPIHostName"]);
var luisOptions = new LuisRecognizerOptionsV2(luisApplication);
_recognizer = new LuisRecognizer(luisOptions);
}
}
// Returns true if luis is configured in the appsettings.json and initialized.
public virtual bool IsConfigured => _recognizer != null;
public virtual async Task<RecognizerResult> RecognizeAsync(ITurnContext turnContext, CancellationToken cancellationToken)
=> await _recognizer.RecognizeAsync(turnContext, cancellationToken);
public virtual async Task<T> RecognizeAsync<T>(ITurnContext turnContext, CancellationToken cancellationToken)
where T : IRecognizerConvert, new()
=> await _recognizer.RecognizeAsync<T>(turnContext, cancellationToken);
}
}

Просмотреть файл

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Newtonsoft.Json;
namespace Microsoft.BotBuilderSamples.DialogSkillBot.Dialogs
{
public class Location
{
[JsonProperty("latitude")]
public float? Latitude { get; set; }
[JsonProperty("longitude")]
public float? Longitude { get; set; }
[JsonProperty("postalCode")]
public string PostalCode { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,69 @@
// 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;
namespace Microsoft.BotBuilderSamples.DialogSkillBot.Dialogs
{
public class OAuthTestDialog : CancelAndHelpDialog
{
private readonly string _connectionName;
public OAuthTestDialog(IConfiguration configuration)
: base(nameof(OAuthTestDialog))
{
_connectionName = configuration["ConnectionName"];
AddDialog(new OAuthPrompt(
nameof(OAuthPrompt),
new OAuthPromptSettings
{
ConnectionName = _connectionName,
Text = $"Please Sign In to connection: '{_connectionName}'",
Title = "Sign In",
Timeout = 300000 // User has 5 minutes to login (1000 * 60 * 5)
}));
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.
var tokenResponse = (TokenResponse)stepContext.Result;
if (tokenResponse != null)
{
// Show the token
var loggedInMessage = "You are now logged in.";
await stepContext.Context.SendActivityAsync(MessageFactory.Text(loggedInMessage, loggedInMessage, InputHints.IgnoringInput), cancellationToken);
var showTokenMessage = "Here is your token:";
await stepContext.Context.SendActivityAsync(MessageFactory.Text($"{showTokenMessage} {tokenResponse.Token}", showTokenMessage, InputHints.IgnoringInput), cancellationToken);
// Sign out
var botAdapter = (BotFrameworkAdapter)stepContext.Context.Adapter;
await botAdapter.SignOutUserAsync(stepContext.Context, _connectionName, null, cancellationToken);
var signOutMessage = "I have signed you out.";
await stepContext.Context.SendActivityAsync(MessageFactory.Text(signOutMessage, signOutMessage, inputHint: InputHints.IgnoringInput), cancellationToken);
return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
}
var tryAgainMessage = "Login was not successful please try again.";
await stepContext.Context.SendActivityAsync(MessageFactory.Text(tryAgainMessage, tryAgainMessage, InputHints.IgnoringInput), cancellationToken);
return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
}
}
}

Просмотреть файл

@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace Microsoft.BotBuilderSamples.DialogSkillBot
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}

Просмотреть файл

@ -0,0 +1,28 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:39783/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"DialogSkillBot31": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "http://localhost:1205/",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

Просмотреть файл

@ -0,0 +1,61 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Builder.TraceExtensions;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Microsoft.BotBuilderSamples.DialogSkillBot
{
public class SkillAdapterWithErrorHandler : BotFrameworkHttpAdapter
{
public SkillAdapterWithErrorHandler(IConfiguration configuration, ILogger<SkillAdapterWithErrorHandler> logger, ConversationState conversationState = null)
: base(configuration, logger)
{
OnTurnError = async (turnContext, exception) =>
{
// Log any leaked exception from the application.
logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}");
// Send a message to the user
var errorMessageText = "The skill encountered an error or bug.";
var errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.IgnoringInput);
await turnContext.SendActivityAsync(errorMessage);
errorMessageText = "To continue to run this bot, please fix the bot source code.";
errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.ExpectingInput);
await turnContext.SendActivityAsync(errorMessage);
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 ex)
{
logger.LogError(ex, $"Exception caught on attempting to Delete ConversationState : {ex}");
}
}
// Send and EndOfConversation activity to the skill caller with the error to end the conversation
// and let the caller decide what to do.
var endOfConversation = Activity.CreateEndOfConversationActivity();
endOfConversation.Code = "SkillError";
endOfConversation.Text = exception.Message;
await turnContext.SendActivityAsync(endOfConversation);
// Send a trace activity, which will be displayed in the Bot Framework Emulator
// Note: we return the entire exception in the value property to help the developer, this should not be done in prod.
await turnContext.TraceActivityAsync("OnTurnError Trace", exception.ToString(), "https://www.botframework.com/schemas/error", "TurnError");
};
}
}
}

Просмотреть файл

@ -0,0 +1,75 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.BotBuilderSamples.DialogSkillBot.Bots;
using Microsoft.BotBuilderSamples.DialogSkillBot.Dialogs;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Microsoft.BotBuilderSamples.DialogSkillBot
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddNewtonsoftJson();
// Create the Bot Framework Adapter with error handling enabled.
services.AddSingleton<IBotFrameworkHttpAdapter, SkillAdapterWithErrorHandler>();
// Create the storage we'll be using for User and Conversation state. (Memory is great for testing purposes.)
services.AddSingleton<IStorage, MemoryStorage>();
// Create the Conversation state. (Used by the Dialog system itself.)
services.AddSingleton<ConversationState>();
// Register LUIS recognizer
services.AddSingleton<DialogSkillBotRecognizer>();
// The Dialog that will be run by the bot.
services.AddSingleton<ActivityRouterDialog>();
// Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
services.AddTransient<IBot, SkillBot<ActivityRouterDialog>>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseDefaultFiles();
app.UseStaticFiles();
//app.UseHttpsRedirection(); Enable this to support https
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

Просмотреть файл

@ -0,0 +1,9 @@
{
"MicrosoftAppId": "",
"MicrosoftAppPassword": "",
"ConnectionName": "",
"LuisAppId": "",
"LuisAPIKey": "",
"LuisAPIHostName": ""
}

Просмотреть файл

@ -0,0 +1,420 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>DialogSkillBot</title>
<style>
body {
margin: 0px;
padding: 0px;
font-family: Segoe UI;
}
html,
body {
height: 100%;
}
header {
background-image: url("data:image/svg+xml,%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 4638.9 651.6' style='enable-background:new 0 0 4638.9 651.6;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bfill:%2355A0E0;%7D .st1%7Bfill:none;%7D .st2%7Bfill:%230058A8;%7D .st3%7Bfill:%23328BD8;%7D .st4%7Bfill:%23B6DCF1;%7D .st5%7Bopacity:0.2;fill:url(%23SVGID_1_);enable-background:new ;%7D%0A%3C/style%3E%3Crect y='1.1' class='st0' width='4640' height='646.3'/%3E%3Cpath class='st1' d='M3987.8,323.6L4310.3,1.1h-65.6l-460.1,460.1c-17.5,17.5-46.1,17.5-63.6,0L3260.9,1.1H0v646.3h3660.3 L3889,418.7c17.5-17.5,46.1-17.5,63.6,0l228.7,228.7h66.6l-260.2-260.2C3970.3,369.8,3970.3,341.1,3987.8,323.6z'/%3E%3Cpath class='st2' d='M3784.6,461.2L4244.7,1.1h-983.9l460.1,460.1C3738.4,478.7,3767.1,478.7,3784.6,461.2z'/%3E%3Cpath class='st3' d='M4640,1.1h-329.8l-322.5,322.5c-17.5,17.5-17.5,46.1,0,63.6l260.2,260.2H4640L4640,1.1L4640,1.1z'/%3E%3Cpath class='st4' d='M3889,418.8l-228.7,228.7h521.1l-228.7-228.7C3935.2,401.3,3906.5,401.3,3889,418.8z'/%3E%3ClinearGradient id='SVGID_1_' gradientUnits='userSpaceOnUse' x1='3713.7576' y1='438.1175' x2='3911.4084' y2='14.2535' gradientTransform='matrix(1 0 0 -1 0 641.3969)'%3E%3Cstop offset='0' style='stop-color:%23FFFFFF;stop-opacity:0.5'/%3E%3Cstop offset='1' style='stop-color:%23FFFFFF'/%3E%3C/linearGradient%3E%3Cpath class='st5' d='M3952.7,124.5c-17.5-17.5-46.1-17.5-63.6,0l-523,523h1109.6L3952.7,124.5z'/%3E%3C/svg%3E%0A");
background-repeat: no-repeat;
background-size: 100%;
background-position: right;
background-color: #55A0E0;
width: 100%;
font-size: 44px;
height: 120px;
color: white;
padding: 30px 0 40px 0px;
display: inline-block;
}
.header-icon {
background-image: url("data:image/svg+xml;utf8,%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20xmlns%3Axlink%3D%22http%3A//www.w3.org/1999/xlink%22%20x%3D%220px%22%20y%3D%220px%22%0A%09%20viewBox%3D%220%200%20150.2%20125%22%20style%3D%22enable-background%3Anew%200%200%20150.2%20125%3B%22%20xml%3Aspace%3D%22preserve%22%3E%0A%3Cstyle%20type%3D%22text/css%22%3E%0A%09.st0%7Bfill%3Anone%3B%7D%0A%09.st1%7Bfill%3A%23FFFFFF%3B%7D%0A%3C/style%3E%0A%3Crect%20x%3D%220.5%22%20class%3D%22st0%22%20width%3D%22149.7%22%20height%3D%22125%22/%3E%0A%3Cg%3E%0A%09%3Cpath%20class%3D%22st1%22%20d%3D%22M59%2C102.9L21.8%2C66c-3.5-3.5-3.5-9.1%2C0-12.5l37-36.5l2.9%2C3l-37%2C36.4c-1.8%2C1.8-1.8%2C4.7%2C0%2C6.6l37.2%2C37L59%2C102.9z%22%0A%09%09/%3E%0A%3C/g%3E%0A%3Cg%3E%0A%09%3Cpath%20class%3D%22st1%22%20d%3D%22M92.5%2C102.9l-3-3l37.2-37c0.9-0.9%2C1.4-2%2C1.4-3.3c0-1.2-0.5-2.4-1.4-3.3L89.5%2C20l2.9-3l37.2%2C36.4%0A%09%09c1.7%2C1.7%2C2.6%2C3.9%2C2.6%2C6.3s-0.9%2C4.6-2.6%2C6.3L92.5%2C102.9z%22/%3E%0A%3C/g%3E%0A%3Cg%3E%0A%09%3Cpath%20class%3D%22st1%22%20d%3D%22M90.1%2C68.4c-4.5%2C0-8-3.5-8-8.1c0-4.5%2C3.5-8.1%2C8-8.1c4.4%2C0%2C8%2C3.7%2C8%2C8.1C98.1%2C64.7%2C94.4%2C68.4%2C90.1%2C68.4z%0A%09%09%20M90.1%2C56.5c-2.2%2C0-3.8%2C1.7-3.8%2C3.9c0%2C2.2%2C1.7%2C3.9%2C3.8%2C3.9c1.9%2C0%2C3.8-1.6%2C3.8-3.9S91.9%2C56.5%2C90.1%2C56.5z%22/%3E%0A%3C/g%3E%0A%3Cg%3E%0A%09%3Cpath%20class%3D%22st1%22%20d%3D%22M61.4%2C68.4c-4.5%2C0-8-3.5-8-8.1c0-4.5%2C3.5-8.1%2C8-8.1c4.4%2C0%2C8%2C3.7%2C8%2C8.1C69.5%2C64.7%2C65.8%2C68.4%2C61.4%2C68.4z%0A%09%09%20M61.4%2C56.5c-2.2%2C0-3.8%2C1.7-3.8%2C3.9c0%2C2.2%2C1.7%2C3.9%2C3.8%2C3.9c1.9%2C0%2C3.8-1.6%2C3.8-3.9S63.3%2C56.5%2C61.4%2C56.5z%22/%3E%0A%3C/g%3E%0A%3C/svg%3E%0A");
background-repeat: no-repeat;
float: left;
height: 140px;
width: 140px;
display: inline-block;
vertical-align: middle;
}
.header-text {
padding-left: 1%;
color: #FFFFFF;
font-family: "Segoe UI";
font-size: 72px;
font-weight: 300;
letter-spacing: 0.35px;
line-height: 96px;
display: inline-block;
vertical-align: middle;
}
.header-inner-container {
min-width: 480px;
max-width: 1366px;
margin-left: auto;
margin-right: auto;
vertical-align: middle;
}
.header-inner-container::after {
content: "";
clear: both;
display: table;
}
.main-content-area {
padding-left: 30px;
}
.content-title {
color: #000000;
font-family: "Segoe UI";
font-size: 46px;
font-weight: 300;
line-height: 62px;
}
.main-text {
color: #808080;
font-size: 24px;
font-family: "Segoe UI";
font-size: 24px;
font-weight: 200;
line-height: 32px;
}
.main-text-p1 {
padding-top: 48px;
padding-bottom: 28px;
}
.endpoint {
height: 32px;
width: 571px;
color: #808080;
font-family: "Segoe UI";
font-size: 24px;
font-weight: 200;
line-height: 32px;
padding-top: 28px;
}
.how-to-build-section {
padding-top: 20px;
padding-left: 30px;
}
.how-to-build-section>h3 {
font-size: 16px;
font-weight: 600;
letter-spacing: 0.35px;
line-height: 22px;
margin: 0 0 24px 0;
text-transform: uppercase;
}
.step-container {
display: flex;
align-items: stretch;
position: relative;
}
.step-container dl {
border-left: 1px solid #A0A0A0;
display: block;
padding: 0 24px;
margin: 0;
}
.step-container dl>dt::before {
background-color: white;
border: 1px solid #A0A0A0;
border-radius: 100%;
content: '';
left: 47px;
height: 11px;
position: absolute;
width: 11px;
}
.step-container dl>.test-bullet::before {
background-color: blue;
}
.step-container dl>dt {
display: block;
font-size: inherit;
font-weight: bold;
line-height: 20px;
}
.step-container dl>dd {
font-size: inherit;
line-height: 20px;
margin-left: 0;
padding-bottom: 32px;
}
.step-container:last-child dl {
border-left: 1px solid transparent;
}
.ctaLink {
background-color: transparent;
border: 1px solid transparent;
color: #006AB1;
cursor: pointer;
font-weight: 600;
padding: 0;
white-space: normal;
}
.ctaLink:focus {
outline: 1px solid #00bcf2;
}
.ctaLink:hover {
text-decoration: underline;
}
.step-icon {
display: flex;
height: 38px;
margin-right: 15px;
width: 38px;
}
.step-icon>div {
height: 30px;
width: 30px;
background-repeat: no-repeat;
}
.ms-logo-container {
min-width: 580px;
max-width: 980px;
margin-left: auto;
margin-right: auto;
left: 0;
right: 0;
transition: bottom 400ms;
}
.ms-logo {
float: right;
background-image: url("data:image/svg+xml;utf8,%0A%3Csvg%20version%3D%221.1%22%20id%3D%22MS-symbol%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20xmlns%3Axlink%3D%22http%3A//www.w3.org/1999/xlink%22%20x%3D%220px%22%20y%3D%220px%22%0A%09%20viewBox%3D%220%200%20400%20120%22%20style%3D%22enable-background%3Anew%200%200%20400%20120%3B%22%20xml%3Aspace%3D%22preserve%22%3E%0A%3Cstyle%20type%3D%22text/css%22%3E%0A%09.st0%7Bfill%3Anone%3B%7D%0A%09.st1%7Bfill%3A%23737474%3B%7D%0A%09.st2%7Bfill%3A%23D63F26%3B%7D%0A%09.st3%7Bfill%3A%23167D3E%3B%7D%0A%09.st4%7Bfill%3A%232E76BC%3B%7D%0A%09.st5%7Bfill%3A%23FDB813%3B%7D%0A%3C/style%3E%0A%3Crect%20x%3D%220.6%22%20class%3D%22st0%22%20width%3D%22398.7%22%20height%3D%22119%22/%3E%0A%3Cpath%20class%3D%22st1%22%20d%3D%22M171.3%2C38.4v43.2h-7.5V47.7h-0.1l-13.4%2C33.9h-5l-13.7-33.9h-0.1v33.9h-6.9V38.4h10.8l12.4%2C32h0.2l13.1-32H171.3%0A%09z%20M177.6%2C41.7c0-1.2%2C0.4-2.2%2C1.3-3c0.9-0.8%2C1.9-1.2%2C3.1-1.2c1.3%2C0%2C2.4%2C0.4%2C3.2%2C1.3c0.8%2C0.8%2C1.3%2C1.8%2C1.3%2C3c0%2C1.2-0.4%2C2.2-1.3%2C3%0A%09c-0.9%2C0.8-1.9%2C1.2-3.2%2C1.2s-2.3-0.4-3.1-1.2C178%2C43.8%2C177.6%2C42.8%2C177.6%2C41.7z%20M185.7%2C50.6v31h-7.3v-31H185.7z%20M207.8%2C76.3%0A%09c1.1%2C0%2C2.3-0.3%2C3.6-0.8c1.3-0.5%2C2.5-1.2%2C3.6-2v6.8c-1.2%2C0.7-2.5%2C1.2-4%2C1.5c-1.5%2C0.3-3.1%2C0.5-4.9%2C0.5c-4.6%2C0-8.3-1.4-11.1-4.3%0A%09c-2.9-2.9-4.3-6.6-4.3-11c0-5%2C1.5-9.1%2C4.4-12.3c2.9-3.2%2C7-4.8%2C12.4-4.8c1.4%2C0%2C2.7%2C0.2%2C4.1%2C0.5c1.4%2C0.4%2C2.5%2C0.8%2C3.3%2C1.2v7%0A%09c-1.1-0.8-2.3-1.5-3.4-1.9c-1.2-0.5-2.4-0.7-3.6-0.7c-2.9%2C0-5.2%2C0.9-7%2C2.8c-1.8%2C1.9-2.7%2C4.4-2.7%2C7.6c0%2C3.1%2C0.8%2C5.6%2C2.5%2C7.3%0A%09C202.6%2C75.4%2C204.9%2C76.3%2C207.8%2C76.3z%20M235.7%2C50.1c0.6%2C0%2C1.1%2C0%2C1.6%2C0.1s0.9%2C0.2%2C1.2%2C0.3v7.4c-0.4-0.3-0.9-0.5-1.7-0.8%0A%09c-0.7-0.3-1.6-0.4-2.7-0.4c-1.8%2C0-3.3%2C0.8-4.5%2C2.3c-1.2%2C1.5-1.9%2C3.8-1.9%2C7v15.6h-7.3v-31h7.3v4.9h0.1c0.7-1.7%2C1.7-3%2C3-4%0A%09C232.2%2C50.6%2C233.8%2C50.1%2C235.7%2C50.1z%20M238.9%2C66.6c0-5.1%2C1.4-9.2%2C4.3-12.2c2.9-3%2C6.9-4.5%2C12.1-4.5c4.8%2C0%2C8.6%2C1.4%2C11.3%2C4.3%0A%09c2.7%2C2.9%2C4.1%2C6.8%2C4.1%2C11.7c0%2C5-1.4%2C9-4.3%2C12c-2.9%2C3-6.8%2C4.5-11.8%2C4.5c-4.8%2C0-8.6-1.4-11.4-4.2C240.3%2C75.3%2C238.9%2C71.4%2C238.9%2C66.6z%0A%09%20M246.5%2C66.3c0%2C3.2%2C0.7%2C5.7%2C2.2%2C7.4c1.5%2C1.7%2C3.6%2C2.6%2C6.3%2C2.6c2.7%2C0%2C4.7-0.9%2C6.1-2.6c1.4-1.7%2C2.1-4.2%2C2.1-7.6c0-3.3-0.7-5.8-2.2-7.5%0A%09c-1.4-1.7-3.4-2.5-6-2.5c-2.7%2C0-4.7%2C0.9-6.2%2C2.7C247.2%2C60.5%2C246.5%2C63%2C246.5%2C66.3z%20M281.5%2C58.8c0%2C1%2C0.3%2C1.9%2C1%2C2.5%0A%09c0.7%2C0.6%2C2.1%2C1.3%2C4.4%2C2.2c2.9%2C1.2%2C5%2C2.5%2C6.1%2C3.9c1.2%2C1.5%2C1.8%2C3.2%2C1.8%2C5.3c0%2C2.9-1.1%2C5.3-3.4%2C7c-2.2%2C1.8-5.3%2C2.7-9.1%2C2.7%0A%09c-1.3%2C0-2.7-0.2-4.3-0.5c-1.6-0.3-2.9-0.7-4-1.2v-7.2c1.3%2C0.9%2C2.8%2C1.7%2C4.3%2C2.2c1.5%2C0.5%2C2.9%2C0.8%2C4.2%2C0.8c1.6%2C0%2C2.9-0.2%2C3.6-0.7%0A%09c0.8-0.5%2C1.2-1.2%2C1.2-2.3c0-1-0.4-1.9-1.2-2.5c-0.8-0.7-2.4-1.5-4.6-2.4c-2.7-1.1-4.6-2.4-5.7-3.8c-1.1-1.4-1.7-3.2-1.7-5.4%0A%09c0-2.8%2C1.1-5.1%2C3.3-6.9c2.2-1.8%2C5.1-2.7%2C8.6-2.7c1.1%2C0%2C2.3%2C0.1%2C3.6%2C0.4c1.3%2C0.2%2C2.5%2C0.6%2C3.4%2C0.9v6.9c-1-0.6-2.1-1.2-3.4-1.7%0A%09c-1.3-0.5-2.6-0.7-3.8-0.7c-1.4%2C0-2.5%2C0.3-3.2%2C0.8C281.9%2C57.1%2C281.5%2C57.8%2C281.5%2C58.8z%20M297.9%2C66.6c0-5.1%2C1.4-9.2%2C4.3-12.2%0A%09c2.9-3%2C6.9-4.5%2C12.1-4.5c4.8%2C0%2C8.6%2C1.4%2C11.3%2C4.3c2.7%2C2.9%2C4.1%2C6.8%2C4.1%2C11.7c0%2C5-1.4%2C9-4.3%2C12c-2.9%2C3-6.8%2C4.5-11.8%2C4.5%0A%09c-4.8%2C0-8.6-1.4-11.4-4.2C299.4%2C75.3%2C297.9%2C71.4%2C297.9%2C66.6z%20M305.5%2C66.3c0%2C3.2%2C0.7%2C5.7%2C2.2%2C7.4c1.5%2C1.7%2C3.6%2C2.6%2C6.3%2C2.6%0A%09c2.7%2C0%2C4.7-0.9%2C6.1-2.6c1.4-1.7%2C2.1-4.2%2C2.1-7.6c0-3.3-0.7-5.8-2.2-7.5c-1.4-1.7-3.4-2.5-6-2.5c-2.7%2C0-4.7%2C0.9-6.2%2C2.7%0A%09C306.3%2C60.5%2C305.5%2C63%2C305.5%2C66.3z%20M353.9%2C56.6h-10.9v25h-7.4v-25h-5.2v-6h5.2v-4.3c0-3.3%2C1.1-5.9%2C3.2-8c2.1-2.1%2C4.8-3.1%2C8.1-3.1%0A%09c0.9%2C0%2C1.7%2C0%2C2.4%2C0.1c0.7%2C0.1%2C1.3%2C0.2%2C1.8%2C0.4V42c-0.2-0.1-0.7-0.3-1.3-0.5c-0.6-0.2-1.3-0.3-2.1-0.3c-1.5%2C0-2.7%2C0.5-3.5%2C1.4%0A%09s-1.2%2C2.4-1.2%2C4.2v3.7h10.9v-7l7.3-2.2v9.2h7.4v6h-7.4v14.5c0%2C1.9%2C0.3%2C3.3%2C1%2C4c0.7%2C0.8%2C1.8%2C1.2%2C3.3%2C1.2c0.4%2C0%2C0.9-0.1%2C1.5-0.3%0A%09c0.6-0.2%2C1.1-0.4%2C1.6-0.7v6c-0.5%2C0.3-1.2%2C0.5-2.3%2C0.7c-1.1%2C0.2-2.1%2C0.3-3.2%2C0.3c-3.1%2C0-5.4-0.8-6.9-2.5c-1.5-1.6-2.3-4.1-2.3-7.4%0A%09V56.6z%22/%3E%0A%3Cg%3E%0A%09%3Crect%20x%3D%2231%22%20y%3D%2224%22%20class%3D%22st2%22%20width%3D%2234.2%22%20height%3D%2234.2%22/%3E%0A%09%3Crect%20x%3D%2268.8%22%20y%3D%2224%22%20class%3D%22st3%22%20width%3D%2234.2%22%20height%3D%2234.2%22/%3E%0A%09%3Crect%20x%3D%2231%22%20y%3D%2261.8%22%20class%3D%22st4%22%20width%3D%2234.2%22%20height%3D%2234.2%22/%3E%0A%09%3Crect%20x%3D%2268.8%22%20y%3D%2261.8%22%20class%3D%22st5%22%20width%3D%2234.2%22%20height%3D%2234.2%22/%3E%0A%3C/g%3E%0A%3C/svg%3E%0A");
}
.ms-logo-container>div {
min-height: 60px;
width: 150px;
background-repeat: no-repeat;
}
.row {
padding: 90px 0px 0 20px;
min-width: 480px;
max-width: 1366px;
margin-left: auto;
margin-right: auto;
}
.column {
float: left;
width: 45%;
padding-right: 20px;
}
.row:after {
content: "";
display: table;
clear: both;
}
a {
text-decoration: none;
}
.download-the-emulator {
height: 20px;
color: #0063B1;
font-size: 15px;
line-height: 20px;
padding-bottom: 70px;
}
.how-to-iframe {
max-width: 700px !important;
min-width: 650px !important;
height: 700px !important;
}
.remove-frame-height {
height: 10px;
}
@media only screen and (max-width: 1300px) {
.ms-logo {
padding-top: 30px;
}
.header-text {
font-size: 40x;
}
.column {
float: none;
padding-top: 30px;
width: 100%;
}
.ms-logo-container {
padding-top: 30px;
min-width: 480px;
max-width: 650px;
margin-left: auto;
margin-right: auto;
}
.row {
padding: 20px 0px 0 20px;
min-width: 480px;
max-width: 650px;
margin-left: auto;
margin-right: auto;
}
}
@media only screen and (max-width: 1370px) {
header {
background-color: #55A0E0;
background-size: auto 200px;
}
}
@media only screen and (max-width: 1230px) {
header {
background-color: #55A0E0;
background-size: auto 200px;
}
.header-text {
font-size: 44px;
}
.header-icon {
height: 120px;
width: 120px;
}
}
@media only screen and (max-width: 1000px) {
header {
background-color: #55A0E0;
background-image: none;
}
}
@media only screen and (max-width: 632px) {
.header-text {
font-size: 32px;
}
.row {
padding: 10px 0px 0 10px;
max-width: 490px !important;
min-width: 410px !important;
}
.endpoint {
font-size: 25px;
}
.main-text {
font-size: 20px;
}
.step-container dl>dd {
font-size: 14px;
}
.column {
padding-right: 5px;
}
.header-icon {
height: 110px;
width: 110px;
}
.how-to-iframe {
max-width: 480px !important;
min-width: 400px !important;
height: 650px !important;
overflow: hidden;
}
}
.remove-frame-height {
max-height: 10px;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function () {
loadFrame();
});
var loadFrame = function () {
var iframe = document.createElement('iframe');
iframe.setAttribute("id", "iframe");
var offLineHTMLContent = "";
var frameElement = document.getElementById("how-to-iframe");
if (window.navigator.onLine) {
iframe.src = 'https://docs.botframework.com/static/abs/pages/f5.htm';
iframe.setAttribute("scrolling", "no");
iframe.setAttribute("frameborder", "0");
iframe.setAttribute("width", "100%");
iframe.setAttribute("height", "100%");
var frameDiv = document.getElementById("how-to-iframe");
frameDiv.appendChild(iframe);
} else {
frameElement.classList.add("remove-frame-height");
}
};
</script>
</head>
<body>
<header class="header">
<div class="header-inner-container">
<div class="header-icon" style="display: inline-block"></div>
<div class="header-text" style="display: inline-block">DialogSkillBot Bot</div>
</div>
</header>
<div class="row">
<div class="column" class="main-content-area">
<div class="content-title">Your bot is ready!</div>
<div class="main-text main-text-p1">You can test your bot in the Bot Framework Emulator<br />
by connecting to http://localhost:3978/api/messages.</div>
<div class="main-text download-the-emulator"><a class="ctaLink"
href="https://aka.ms/bot-framework-F5-download-emulator" target="_blank">Download the Emulator</a>
</div>
<div class="main-text">Visit <a class="ctaLink" href="https://aka.ms/bot-framework-F5-abs-home"
target="_blank">Azure
Bot Service</a> to register your bot and add it to<br />
various channels. The bot's endpoint URL typically looks
like this:</div>
<div class="endpoint">https://<i>your_bots_hostname</i>/api/messages</div>
</div>
<div class="column how-to-iframe" id="how-to-iframe"></div>
</div>
<div class="ms-logo-container">
<div class="ms-logo"></div>
</div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,116 @@
{
"$schema": "https://schemas.botframework.com/schemas/skills/skill-manifest-2.0.0.json",
"$id": "DialogSkillBot",
"name": "Skill bot with dialogs",
"version": "1.0",
"description": "This is a sample skill definition for multiple activity types",
"publisherName": "Microsoft",
"privacyUrl": "https://dialogskillbot.contoso.com/privacy.html",
"copyright": "Copyright (c) Microsoft Corporation. All rights reserved.",
"license": "",
"iconUrl": "https://dialogskillbot.contoso.com/icon.png",
"tags": [
"sample",
"travel",
"weather",
"luis"
],
"endpoints": [
{
"name": "default",
"protocol": "BotFrameworkV3",
"description": "Default endpoint for the skill",
"endpointUrl": "https://ggdialogskillbot.azurewebsites.net/api/messages",
"msAppId": "f3fe8762-e50c-4688-b202-a040f522d916"
}
],
"activities": {
"bookFlight": {
"description": "Books a flight (multi turn)",
"type": "event",
"name": "BookFlight",
"value": {
"$ref": "#/definitions/bookingInfo"
},
"resultValue": {
"$ref": "#/definitions/bookingInfo"
}
},
"getWeather": {
"description": "Retrieves and returns the weather for the user's location (single turn, invoke)",
"type": "invoke",
"name": "GetWeather",
"value": {
"$ref": "#/definitions/location"
},
"resultValue": {
"$ref": "#/definitions/weatherReport"
}
},
"passthroughMessage": {
"type": "message",
"description": "Receives the user's utterance and attempts to resolve it using the skill's LUIS models",
"value": {
"type": "object"
}
}
},
"definitions": {
"localeValue": {
"type": "object",
"properties": {
"locale": {
"type": "string",
"description": "The current user's locale ISO code"
}
}
},
"bookingInfo": {
"type": "object",
"required": [
"origin"
],
"properties": {
"origin": {
"type": "string",
"description": "this is the origin city for the flight"
},
"destination": {
"type": "string",
"description": "this is the destination city for the flight"
},
"travelDate": {
"type": "string",
"description": "The date for the flight in YYYY-MM-DD format"
}
}
},
"weatherReport": {
"type": "array",
"description": "Array of forecasts for the next week.",
"items": [
{
"type": "string"
}
]
},
"location": {
"type": "object",
"description": "Location metadata",
"properties": {
"latitude": {
"type": "number",
"title": "Latitude"
},
"longitude": {
"type": "number",
"title": "Longitude"
},
"postalCode": {
"type": "string",
"title": "Postal code"
}
}
}
}
}

Просмотреть файл

@ -5,6 +5,7 @@
<LangVersion>latest</LangVersion>
<AssemblyName>Microsoft.BotBuilderSamples.EchoSkillBot31</AssemblyName>
<RootNamespace>Microsoft.BotBuilderSamples.EchoSkillBot31</RootNamespace>
<UserSecretsId>d3e58f1c-0841-4154-8a6e-c4dfc5ae3edf</UserSecretsId>
</PropertyGroup>
<ItemGroup>

Просмотреть файл

@ -2,8 +2,10 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>latest</LangVersion>
<AssemblyName>Microsoft.BotBuilderSamples.SimpleRootBot31</AssemblyName>
<RootNamespace>Microsoft.BotBuilderSamples.SimpleRootBot31</RootNamespace>
<UserSecretsId>a52d1fa1-0d90-42d7-bbfc-2d776e8b7804</UserSecretsId>
</PropertyGroup>
<ItemGroup>

Просмотреть файл

@ -80,9 +80,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.TestB
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialogs.Declarative", "libraries\Microsoft.Bot.Builder.Dialogs.Declarative\Microsoft.Bot.Builder.Dialogs.Declarative.csproj", "{1BC05915-044E-4776-8956-B44BBEFF2F84}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Expressions.Tests", "tests\Microsoft.Bot.Expressions.Tests\Microsoft.Bot.Expressions.Tests.csproj", "{AE3FC7F6-B212-4438-B1D7-C121BB4C15ED}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdaptiveExpressions.Tests", "tests\AdaptiveExpressions.Tests\AdaptiveExpressions.Tests.csproj", "{AE3FC7F6-B212-4438-B1D7-C121BB4C15ED}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Expressions", "libraries\Microsoft.Bot.Expressions\Microsoft.Bot.Expressions.csproj", "{8DC1257B-7650-40EB-97A2-C1CBA306DA6A}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdaptiveExpressions", "libraries\AdaptiveExpressions\AdaptiveExpressions.csproj", "{8DC1257B-7650-40EB-97A2-C1CBA306DA6A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialogs.Declarative.Tests", "tests\Microsoft.Bot.Builder.Dialogs.Declarative.Tests\Microsoft.Bot.Builder.Dialogs.Declarative.Tests.csproj", "{D5E70443-4BA2-42ED-992A-010268440B08}"
EndProject
@ -144,8 +144,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.TestB
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.TestBot.NetCore21.Tests", "tests\Microsoft.Bot.Builder.TestBot.NetCore21.Tests\Microsoft.Bot.Builder.TestBot.NetCore21.Tests.csproj", "{2FBA2BB7-73C8-45CD-99B7-D65F817C9051}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Expressions.Properties", "libraries\Microsoft.Bot.Expressions.Properties\Microsoft.Bot.Expressions.Properties.csproj", "{4943C197-0742-480E-B1D3-6738D41582A3}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Skills", "Skills", "{3C16F609-61D8-49C5-A243-9D32B9491D91}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SimpleBotToBot", "SimpleBotToBot", "{C9B9D0E6-1579-4F5E-B23B-9EFD17F50D34}"
@ -154,6 +152,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleRootBot", "Functional
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EchoSkillBot", "FunctionalTests\Skills\SimpleBotToBot\EchoSkillBot\EchoSkillBot.csproj", "{C8F3C6E5-6A21-4F77-ADF7-30119D836A4D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DialogToDialog", "DialogToDialog", "{3E023AB7-FE1F-41B1-9EF4-1550BCE1DC37}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DialogRootBot", "FunctionalTests\Skills\DialogToDialog\DialogRootBot\DialogRootBot.csproj", "{E65DC262-CA77-41F6-8439-02C1917874DD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DialogEchoSkillBot", "FunctionalTests\Skills\DialogToDialog\DialogEchoSkillBot\DialogEchoSkillBot.csproj", "{31D0EA80-192C-4645-911D-F0393535B176}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DialogSkillBot", "FunctionalTests\Skills\DialogToDialog\DialogSkillBot\DialogSkillBot.csproj", "{AB75F219-CFA4-4051-8A11-3EE7A128AF08}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Skills", "libraries\Microsoft.Bot.Builder.Skills\Microsoft.Bot.Builder.Skills.csproj", "{E52019C6-12CF-4502-AFE9-796DA071DDC7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Skills.Tests", "tests\Microsoft.Bot.Builder.Skills.Tests\Microsoft.Bot.Builder.Skills.Tests.csproj", "{91C94485-6B9D-47D8-9228-8F49E2570810}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -586,14 +596,6 @@ Global
{2FBA2BB7-73C8-45CD-99B7-D65F817C9051}.Release|Any CPU.Build.0 = Release|Any CPU
{2FBA2BB7-73C8-45CD-99B7-D65F817C9051}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{2FBA2BB7-73C8-45CD-99B7-D65F817C9051}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{4943C197-0742-480E-B1D3-6738D41582A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4943C197-0742-480E-B1D3-6738D41582A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4943C197-0742-480E-B1D3-6738D41582A3}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{4943C197-0742-480E-B1D3-6738D41582A3}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{4943C197-0742-480E-B1D3-6738D41582A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4943C197-0742-480E-B1D3-6738D41582A3}.Release|Any CPU.Build.0 = Release|Any CPU
{4943C197-0742-480E-B1D3-6738D41582A3}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{4943C197-0742-480E-B1D3-6738D41582A3}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{A0080C33-4628-4259-84D6-2E6DEDBD26B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A0080C33-4628-4259-84D6-2E6DEDBD26B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A0080C33-4628-4259-84D6-2E6DEDBD26B7}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
@ -610,6 +612,46 @@ Global
{C8F3C6E5-6A21-4F77-ADF7-30119D836A4D}.Release|Any CPU.Build.0 = Release|Any CPU
{C8F3C6E5-6A21-4F77-ADF7-30119D836A4D}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{C8F3C6E5-6A21-4F77-ADF7-30119D836A4D}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{E65DC262-CA77-41F6-8439-02C1917874DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E65DC262-CA77-41F6-8439-02C1917874DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E65DC262-CA77-41F6-8439-02C1917874DD}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{E65DC262-CA77-41F6-8439-02C1917874DD}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{E65DC262-CA77-41F6-8439-02C1917874DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E65DC262-CA77-41F6-8439-02C1917874DD}.Release|Any CPU.Build.0 = Release|Any CPU
{E65DC262-CA77-41F6-8439-02C1917874DD}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{E65DC262-CA77-41F6-8439-02C1917874DD}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{31D0EA80-192C-4645-911D-F0393535B176}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{31D0EA80-192C-4645-911D-F0393535B176}.Debug|Any CPU.Build.0 = Debug|Any CPU
{31D0EA80-192C-4645-911D-F0393535B176}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{31D0EA80-192C-4645-911D-F0393535B176}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{31D0EA80-192C-4645-911D-F0393535B176}.Release|Any CPU.ActiveCfg = Release|Any CPU
{31D0EA80-192C-4645-911D-F0393535B176}.Release|Any CPU.Build.0 = Release|Any CPU
{31D0EA80-192C-4645-911D-F0393535B176}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{31D0EA80-192C-4645-911D-F0393535B176}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{AB75F219-CFA4-4051-8A11-3EE7A128AF08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AB75F219-CFA4-4051-8A11-3EE7A128AF08}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AB75F219-CFA4-4051-8A11-3EE7A128AF08}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{AB75F219-CFA4-4051-8A11-3EE7A128AF08}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{AB75F219-CFA4-4051-8A11-3EE7A128AF08}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AB75F219-CFA4-4051-8A11-3EE7A128AF08}.Release|Any CPU.Build.0 = Release|Any CPU
{AB75F219-CFA4-4051-8A11-3EE7A128AF08}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{AB75F219-CFA4-4051-8A11-3EE7A128AF08}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{E52019C6-12CF-4502-AFE9-796DA071DDC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E52019C6-12CF-4502-AFE9-796DA071DDC7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E52019C6-12CF-4502-AFE9-796DA071DDC7}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{E52019C6-12CF-4502-AFE9-796DA071DDC7}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{E52019C6-12CF-4502-AFE9-796DA071DDC7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E52019C6-12CF-4502-AFE9-796DA071DDC7}.Release|Any CPU.Build.0 = Release|Any CPU
{E52019C6-12CF-4502-AFE9-796DA071DDC7}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{E52019C6-12CF-4502-AFE9-796DA071DDC7}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{91C94485-6B9D-47D8-9228-8F49E2570810}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{91C94485-6B9D-47D8-9228-8F49E2570810}.Debug|Any CPU.Build.0 = Debug|Any CPU
{91C94485-6B9D-47D8-9228-8F49E2570810}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{91C94485-6B9D-47D8-9228-8F49E2570810}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{91C94485-6B9D-47D8-9228-8F49E2570810}.Release|Any CPU.ActiveCfg = Release|Any CPU
{91C94485-6B9D-47D8-9228-8F49E2570810}.Release|Any CPU.Build.0 = Release|Any CPU
{91C94485-6B9D-47D8-9228-8F49E2570810}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{91C94485-6B9D-47D8-9228-8F49E2570810}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -672,11 +714,16 @@ Global
{D9242899-AB3F-46BB-BAB4-386CB8EC535C} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{99C466C3-1931-4C0E-AA0A-A8D9D140F56E} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{2FBA2BB7-73C8-45CD-99B7-D65F817C9051} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{4943C197-0742-480E-B1D3-6738D41582A3} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{3C16F609-61D8-49C5-A243-9D32B9491D91} = {8667F820-8ADA-4498-91AE-AE95DEE5227E}
{C9B9D0E6-1579-4F5E-B23B-9EFD17F50D34} = {3C16F609-61D8-49C5-A243-9D32B9491D91}
{A0080C33-4628-4259-84D6-2E6DEDBD26B7} = {C9B9D0E6-1579-4F5E-B23B-9EFD17F50D34}
{C8F3C6E5-6A21-4F77-ADF7-30119D836A4D} = {C9B9D0E6-1579-4F5E-B23B-9EFD17F50D34}
{3E023AB7-FE1F-41B1-9EF4-1550BCE1DC37} = {3C16F609-61D8-49C5-A243-9D32B9491D91}
{E65DC262-CA77-41F6-8439-02C1917874DD} = {3E023AB7-FE1F-41B1-9EF4-1550BCE1DC37}
{31D0EA80-192C-4645-911D-F0393535B176} = {3E023AB7-FE1F-41B1-9EF4-1550BCE1DC37}
{AB75F219-CFA4-4051-8A11-3EE7A128AF08} = {3E023AB7-FE1F-41B1-9EF4-1550BCE1DC37}
{E52019C6-12CF-4502-AFE9-796DA071DDC7} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{91C94485-6B9D-47D8-9228-8F49E2570810} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7173C9F3-A7F9-496E-9078-9156E35D6E16}

Просмотреть файл

@ -80,9 +80,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.TestB
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialogs.Declarative", "libraries\Microsoft.Bot.Builder.Dialogs.Declarative\Microsoft.Bot.Builder.Dialogs.Declarative.csproj", "{1BC05915-044E-4776-8956-B44BBEFF2F84}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Expressions.Tests", "tests\Microsoft.Bot.Expressions.Tests\Microsoft.Bot.Expressions.Tests.csproj", "{AE3FC7F6-B212-4438-B1D7-C121BB4C15ED}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdaptiveExpressions.Tests", "tests\AdaptiveExpressions.Tests\AdaptiveExpressions.Tests.csproj", "{AE3FC7F6-B212-4438-B1D7-C121BB4C15ED}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Expressions", "libraries\Microsoft.Bot.Expressions\Microsoft.Bot.Expressions.csproj", "{8DC1257B-7650-40EB-97A2-C1CBA306DA6A}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdaptiveExpressions", "libraries\AdaptiveExpressions\AdaptiveExpressions.csproj", "{8DC1257B-7650-40EB-97A2-C1CBA306DA6A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialogs.Declarative.Tests", "tests\Microsoft.Bot.Builder.Dialogs.Declarative.Tests\Microsoft.Bot.Builder.Dialogs.Declarative.Tests.csproj", "{D5E70443-4BA2-42ED-992A-010268440B08}"
EndProject
@ -174,6 +174,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.TestB
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.TestBot.NetCore21.Tests", "tests\Microsoft.Bot.Builder.TestBot.NetCore21.Tests\Microsoft.Bot.Builder.TestBot.NetCore21.Tests.csproj", "{2FBA2BB7-73C8-45CD-99B7-D65F817C9051}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Skills", "libraries\Microsoft.Bot.Builder.Skills\Microsoft.Bot.Builder.Skills.csproj", "{5D8CD17C-AFF2-4833-96F4-F026E05AB595}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Skills.Tests", "tests\Microsoft.Bot.Builder.Skills.Tests\Microsoft.Bot.Builder.Skills.Tests.csproj", "{A4450707-89F3-4A34-A2F7-0C350D1EA436}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialogs.Adaptive.Testing", "libraries\Microsoft.Bot.Builder.Dialogs.Adaptive.Testing\Microsoft.Bot.Builder.Dialogs.Adaptive.Testing.csproj", "{D921D320-0450-455F-8DF2-70EDAC5CCE68}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -710,6 +716,30 @@ Global
{2FBA2BB7-73C8-45CD-99B7-D65F817C9051}.Release|Any CPU.Build.0 = Release|Any CPU
{2FBA2BB7-73C8-45CD-99B7-D65F817C9051}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{2FBA2BB7-73C8-45CD-99B7-D65F817C9051}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{5D8CD17C-AFF2-4833-96F4-F026E05AB595}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5D8CD17C-AFF2-4833-96F4-F026E05AB595}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5D8CD17C-AFF2-4833-96F4-F026E05AB595}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{5D8CD17C-AFF2-4833-96F4-F026E05AB595}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{5D8CD17C-AFF2-4833-96F4-F026E05AB595}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5D8CD17C-AFF2-4833-96F4-F026E05AB595}.Release|Any CPU.Build.0 = Release|Any CPU
{5D8CD17C-AFF2-4833-96F4-F026E05AB595}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{5D8CD17C-AFF2-4833-96F4-F026E05AB595}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{A4450707-89F3-4A34-A2F7-0C350D1EA436}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A4450707-89F3-4A34-A2F7-0C350D1EA436}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A4450707-89F3-4A34-A2F7-0C350D1EA436}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{A4450707-89F3-4A34-A2F7-0C350D1EA436}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{A4450707-89F3-4A34-A2F7-0C350D1EA436}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A4450707-89F3-4A34-A2F7-0C350D1EA436}.Release|Any CPU.Build.0 = Release|Any CPU
{A4450707-89F3-4A34-A2F7-0C350D1EA436}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{A4450707-89F3-4A34-A2F7-0C350D1EA436}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{D921D320-0450-455F-8DF2-70EDAC5CCE68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D921D320-0450-455F-8DF2-70EDAC5CCE68}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D921D320-0450-455F-8DF2-70EDAC5CCE68}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{D921D320-0450-455F-8DF2-70EDAC5CCE68}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{D921D320-0450-455F-8DF2-70EDAC5CCE68}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D921D320-0450-455F-8DF2-70EDAC5CCE68}.Release|Any CPU.Build.0 = Release|Any CPU
{D921D320-0450-455F-8DF2-70EDAC5CCE68}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{D921D320-0450-455F-8DF2-70EDAC5CCE68}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -787,6 +817,9 @@ Global
{F98A9787-B175-450F-99FC-EC241EB9D581} = {E8CD434A-306F-41D9-B67D-BFFF3287354D}
{99C466C3-1931-4C0E-AA0A-A8D9D140F56E} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{2FBA2BB7-73C8-45CD-99B7-D65F817C9051} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{5D8CD17C-AFF2-4833-96F4-F026E05AB595} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{A4450707-89F3-4A34-A2F7-0C350D1EA436} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{D921D320-0450-455F-8DF2-70EDAC5CCE68} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7173C9F3-A7F9-496E-9078-9156E35D6E16}

Просмотреть файл

@ -9,16 +9,48 @@ variables:
pool:
vmImage: 'macOS-10.14'
trigger: # ci trigger
branches:
include:
- master
paths:
include:
- '*'
exclude:
- doc/
- specs/
- Changes.md
- LICENSE
- README.md
- UsingMyGet.md
pr: # pr trigger
branches:
include:
- master
paths:
include:
- '*'
exclude:
- doc/
- specs/
- Changes.md
- LICENSE
- README.md
- UsingMyGet.md
steps:
# Note: Template ci-build-steps.yml is not supported in macOS because it calls VSBuild@1 in order to build the Windows-only ASP.NET Desktop assemblies.
- task: UseDotNet@2
displayName: 'Use .Net Core sdk 2.1.x'
inputs:
version: 2.1.x
- task: UseDotNet@2
displayName: 'Use .Net Core sdk 3.1.x'
displayName: 'Use .Net Core sdk 3.1.101'
inputs:
version: 3.1.x
version: 3.1.101
- task: NuGetToolInstaller@0
displayName: 'Use NuGet 4.9.1'

Просмотреть файл

@ -9,19 +9,50 @@ variables:
BotBuilderDll: Microsoft.Bot.Builder.AI.Luis,Microsoft.Bot.Builder.AI.QnA,Microsoft.Bot.Builder.ApplicationInsights,Microsoft.Bot.Builder.Azure,Microsoft.Bot.Builder.Dialogs,Microsoft.Bot.Builder.Integration.ApplicationInsights.Core,Microsoft.Bot.Builder.Integration.AspNet.Core,Microsoft.Bot.Builder.TemplateManager,Microsoft.Bot.Builder.Testing,Microsoft.Bot.Builder,Microsoft.Bot.Configuration,Microsoft.Bot.Connector,Microsoft.Bot.Schema,Microsoft.Bot.Streaming
BuildConfiguration: Debug-Windows
BuildPlatform: any cpu
CoverallsToken: define this in Azure
GitHubCommentApiKey: define this in Azure
# CoverallsTokenEncrypted: define this in Azure
# GitHubCommentApiKey: define this in Azure
IsBuildServer: true # This activates package versioning in the projects in Microsoft.Bot.Builder.sln.
MSBuildArguments: -p:SignAssembly=false -p:delaySign=false
Parameters.solution: Microsoft.Bot.Builder.sln
PreviewPackageVersion: 4.8.0-preview-$(Build.BuildNumber) # This is consumed by projects in Microsoft.Bot.Builder.sln.
ReleasePackageVersion: 4.8.0-preview-$(Build.BuildNumber) # This is consumed by projects in Microsoft.Bot.Builder.sln.
pool:
name: Hosted Windows 2019 with VS2019
demands:
- msbuild
- visualstudio
trigger: # ci trigger
branches:
include:
- master
paths:
include:
- '*'
exclude:
- doc/
- specs/
- Changes.md
- LICENSE
- README.md
- UsingMyGet.md
pr: # pr trigger
branches:
include:
- master
paths:
include:
- '*'
exclude:
- doc/
- specs/
- Changes.md
- LICENSE
- README.md
- UsingMyGet.md
# The following 2 stages run multi-configuration, multi-agent parallel jobs.
# Debug-Windows/Release-Windows => Builds everything in Debug/Release + the ASP.NET Desktop.
# Debug/Release => would build all .NET Standard libs and test them.
@ -39,6 +70,7 @@ stages:
- job: Release_Windows_Configuration
variables:
BuildConfiguration: Release-Windows
NoCoverageUpload: true
steps:
- template: ci-build-steps.yml
- template: ci-test-steps.yml

Просмотреть файл

@ -5,13 +5,26 @@
# "name" here defines the build number format. Build number is accessed via $(Build.BuildNumber)
name: $(Build.BuildId)
trigger: none
pr: none
pool:
vmImage: 'windows-2019'
trigger: # ci trigger
branches:
include:
- master
paths:
include:
- '*'
exclude:
- doc/
- specs/
- Changes.md
- LICENSE
- README.md
- UsingMyGet.md
pr: none # no pr trigger
variables:
ReleasePackageVersion: 4.8.0-preview-$(Build.BuildNumber)
MSBuildArguments: -p:SignAssembly=false -p:delaySign=false
@ -25,12 +38,13 @@ variables:
# AzureDeploymentPassword: define this in Azure
# BotGroup: define this in Azure
# BotName: define this in Azure
# DeleteResourceGroup: define this in Azure. Normally set to true.
steps:
- template: ci-build-steps.yml
- task: DotNetCoreCLI@2
displayName: 'Dotnet Publish TestBot'
displayName: 'Dotnet publish test bot'
inputs:
command: publish
publishWebProjects: false
@ -40,7 +54,7 @@ steps:
modifyOutputPath: false
- task: AzureResourceGroupDeployment@2
displayName: 'Azure Deployment:Create Or Update Resource Group action on $(BotGroup)'
displayName: 'Azure deployment: Create or update resource group $(BotGroup)'
inputs:
azureSubscription: $(AzureSubscription)
resourceGroupName: '$(BotGroup)'
@ -65,14 +79,19 @@ steps:
git remote add azure https://$(AzureDeploymentUser):$(AzureDeploymentPassword)@$(BotName).scm.azurewebsites.net:443/$(BotName).git
git push azure master
workingDirectory: '$(System.DefaultWorkingDirectory)\tests\Microsoft.Bot.Builder.TestBot'
displayName: 'Git Bot Deployment'
displayName: 'Git bot deployment'
- powershell: |
$json = Get-Content '$(System.DefaultWorkingDirectory)\DirectLineCreate.json' | Out-String | ConvertFrom-Json
$key = $json.properties.properties.sites.key
echo "##vso[task.setvariable variable=DIRECTLINE;]$key"
echo "##vso[task.setvariable variable=BOTID;]$(BotName)"
displayName: 'Get Bot Keys'
displayName: 'Get bot keys'
- powershell: |
echo '##vso[task.setvariable variable=TESTAPPID]$(AppId)'
echo '##vso[task.setvariable variable=TESTPASSWORD]$(AppSecret)'
displayName: 'Set Environment Variables'
- task: DotNetCoreCLI@2
displayName: 'Run Functional tests'
@ -82,10 +101,10 @@ steps:
arguments: '-v n --configuration $(BuildConfiguration) --no-build --no-restore --filter "TestCategory=FunctionalTests&TestCategory!=Adapters" --collect:"Code Coverage" --settings $(System.DefaultWorkingDirectory)\CodeCoverage.runsettings '
workingDirectory: '$(System.DefaultWorkingDirectory)\'
#- task: AzureCLI@1
# displayName: 'Delete Resources'
# inputs:
# azureSubscription: $(AzureSubscription)
# scriptLocation: inlineScript
# inlineScript: 'call az group delete -n "$(BotGroup)" --yes'
# condition: always()
- task: AzureCLI@1
displayName: 'Delete test resource group'
inputs:
azureSubscription: $(AzureSubscription)
scriptLocation: inlineScript
inlineScript: 'call az group delete -n "$(BotGroup)" --yes'
condition: and(always(), ne(variables['DeleteResourceGroup'], 'false'))

Просмотреть файл

@ -5,13 +5,26 @@
# "name" here defines the build number format. Build number is accessed via $(Build.BuildNumber)
name: $(Build.BuildId)
trigger: none
pr: none
pool:
vmImage: 'windows-2019'
trigger: # ci trigger
branches:
include:
- master
paths:
include:
- '*'
exclude:
- doc/
- specs/
- Changes.md
- LICENSE
- README.md
- UsingMyGet.md
pr: none # no pr trigger
variables:
ReleasePackageVersion: 4.8.0-preview-$(Build.BuildNumber)
MSBuildArguments: -p:SignAssembly=false -p:delaySign=false
@ -23,8 +36,6 @@ variables:
# AzureSubscription: define this in Azure
# BotGroup: define this in Azure
# BotName: define this in Azure
# LUISAPPIDSECRET: define this in Azure
# LUISSUBSCRIPTIONKEYSECRET: define this in Azure
steps:
@ -59,8 +70,6 @@ steps:
- powershell: |
echo '##vso[task.setvariable variable=TESTAPPID]$(AppId)'
echo '##vso[task.setvariable variable=TESTPASSWORD]$(AppSecret)'
echo '##vso[task.setvariable variable=LUISAPPID]$(LUISAPPIDSECRET)'
echo '##vso[task.setvariable variable=LUISSUBSCRIPTIONKEY]$(LUISSUBSCRIPTIONKEYSECRET)'
displayName: 'Set Environment Variables'
- task: DotNetCoreCLI@2
@ -81,4 +90,4 @@ steps:
azureSubscription: $(AzureSubscription)
scriptLocation: inlineScript
inlineScript: 'call az group delete -n "$(BotGroup)" --yes'
condition: always()
condition: and(always(), ne(variables['DeleteResourceGroup'], 'false'))

Просмотреть файл

@ -11,8 +11,8 @@ variables:
IsBuildServer: true # Consumed by projects in Microsoft.Bot.Builder.sln.
MSBuildArguments: -p:SignAssembly=false -p:delaySign=false
Parameters.solution: Microsoft.Bot.Builder.sln
PreviewPackageVersion: 4.8.0-preview-$(Build.BuildNumber) # Consumed by projects in Microsoft.Bot.Builder.sln. Define this in Azure to be settable at queue time.
ReleasePackageVersion: 4.8.0-preview-$(Build.BuildNumber) # Consumed by projects in Microsoft.Bot.Builder.sln. Define this in Azure to be settable at queue time.
# PreviewPackageVersion: 4.8.0-preview-$(Build.BuildNumber) # Consumed by projects in Microsoft.Bot.Builder.sln. Define this in Azure to be settable at queue time.
# ReleasePackageVersion: 4.8.0-preview-$(Build.BuildNumber) # Consumed by projects in Microsoft.Bot.Builder.sln. Define this in Azure to be settable at queue time.
pool:
name: Hosted Windows 2019 with VS2019
demands:

Просмотреть файл

@ -9,16 +9,33 @@ variables:
TestConfiguration: Release
BuildPlatform: any cpu
IsBuildServer: true # Consumed by projects in Microsoft.Bot.Builder.sln.
MSBuildArguments: -p:IncludeSymbols=true -p:SourceLinkCreate=true -p:SymbolPackageFormat=snupkg
MSBuildArguments: -p:PublishRepositoryUrl=true -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg
Parameters.solution: Microsoft.Bot.Builder.sln
PreviewPackageVersion: 4.8.0-preview-$(Build.BuildNumber) # Consumed by projects in Microsoft.Bot.Builder.sln. Define this in Azure to be settable at queue time.
ReleasePackageVersion: 4.8.0-preview-$(Build.BuildNumber) # Consumed by projects in Microsoft.Bot.Builder.sln. Define this in Azure to be settable at queue time.
# PreviewPackageVersion: 4.8.0-preview-$(Build.BuildNumber) # Consumed by projects in Microsoft.Bot.Builder.sln. Define this in Azure to be settable at queue time.
# ReleasePackageVersion: 4.8.0-preview-$(Build.BuildNumber) # Consumed by projects in Microsoft.Bot.Builder.sln. Define this in Azure to be settable at queue time.
pool:
name: Hosted Windows 2019 with VS2019
demands:
- msbuild
- visualstudio
trigger: # ci trigger
branches:
include:
- master
paths:
include:
- '*'
exclude:
- doc/
- specs/
- Changes.md
- LICENSE
- README.md
- UsingMyGet.md
pr: none # no pr trigger
jobs:
- job: Build_and_Sign
steps:

Просмотреть файл

@ -17,7 +17,7 @@ variables:
BuildConfiguration: 'Debug'
Parameters.solution: Microsoft.Bot.Builder.sln
MSBuildArguments:
AzureSubscription: define this in Azure
# AzureSubscription: define this in Azure
steps:
- template: ci-build-steps.yml

Просмотреть файл

@ -1,46 +0,0 @@
trigger:
- master
pr:
- master
pool:
vmImage: 'windows-2019'
variables:
BuildPlatform: 'Any CPU'
BuildConfiguration: 'debug'
ProjectName: 'Microsoft.Bot.Builder.Adapters.Twilio.Tests'
Build.SourcesDirectory: ''
OutputFile: 'result.xml'
steps:
- task: UseDotNet@2
displayName: 'Use .Net Core sdk 2.1.x'
inputs:
version: 2.1.x
- task: UseDotNet@2
displayName: 'Use .Net Core sdk 3.1.x'
inputs:
version: 3.1.x
- task: NuGetToolInstaller@0
displayName: 'Use NuGet 4.9.1'
inputs:
versionSpec: 4.9.1
- task: NuGetCommand@2
displayName: 'NuGet restore'
inputs:
restoreSolution: '$(System.DefaultWorkingDirectory)\Microsoft.Bot.Builder.sln'
- task: VSBuild@1
displayName: 'Build solution Microsoft.Bot.Builder.sln'
inputs:
solution: Microsoft.Bot.Builder.sln
vsVersion: 16.0
platform: '$(BuildPlatform)'
configuration: '$(BuildConfiguration)'
- template: code-coverage-w-coverlet-steps.yml

Просмотреть файл

@ -15,7 +15,7 @@ steps:
# Variables ReleasePackageVersion and PreviewPackageVersion are consumed by projects in Microsoft.Bot.Builder.sln.
# For the signed build, they should be settable at queue time. To set that up, define the variables in Azure on the Variables tab.
- task: colinsalmcorner.colinsalmcorner-buildtasks.tag-build-task.tagBuildOrRelease@0
displayName: 'Tag Build with PackageVersion'
displayName: 'Tag build with package version'
inputs:
tags: 'Version=$(ReleasePackageVersion)'
continueOnError: true

Просмотреть файл

@ -1,3 +1,6 @@
#variables:
# CoverallsTokenEncrypted: define this in Azure
steps:
- powershell: |
Remove-Item CodeCoverage -Force -Recurse -ErrorAction Ignore
@ -41,6 +44,10 @@ steps:
Get-ChildItem -Path "D:\a\_temp" -Include "*.coverage" -Recurse | Copy-Item -Destination CodeCoverage
displayName: 'Copy .coverage Files to CodeCoverage folder'
- powershell: 'echo ''##vso[task.setvariable variable=CoverallsToken]$(CoverallsTokenEncrypted)'''
displayName: 'Decrypt CoverallsTokenEncrypted if it exists'
condition: and(succeeded(), ne(variables['CoverallsTokenEncrypted'], ''), ne(variables['NoCoverageUpload'], 'true'))
- task: PowerShell@2
displayName: 'Upload Coverage Files to Coveralls.io https://coveralls.io/github/Microsoft/botbuilder-dotnet'
inputs:
@ -48,21 +55,15 @@ steps:
filePath: '$(Build.SourcesDirectory)\build\PublishToCoveralls.ps1'
arguments: '-pathToCoverageFiles "$(Build.SourcesDirectory)\CodeCoverage" -serviceName "master CI-PR"'
continueOnError: true
condition: and(succeeded(), ne(variables['CoverallsToken'], ''), ne(variables['NoCoverageUpload'], 'true'))
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact: Coverage files'
displayName: 'Publish build artifact CodeCoverage'
inputs:
PathtoPublish: CodeCoverage
ArtifactName: CodeCoverage
enabled: false
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact: build folder'
inputs:
PathtoPublish: build
ArtifactName: build
enabled: false
- powershell: |
New-Item -ItemType directory -Path "outputLibraries\" -Force

Просмотреть файл

@ -1,17 +0,0 @@
steps:
- powershell: |
dotnet tool install --global coverlet.console
displayName: 'Install Coverlet'
- powershell: |
coverlet Adapters\$(ProjectName)\bin\$(BuildConfiguration)\netcoreapp3.0\$(ProjectName).dll --target "dotnet" --targetargs "test Adapters\$(ProjectName)\$(ProjectName).csproj -v n --configuration debug --no-build --no-restore --filter 'TestCategory!=IgnoreInAutomatedBuild&TestCategory!=FunctionalTests'" --format cobertura --output $(Build.SourcesDirectory)\$(OutputFile) --exclude-by-file "Adapters\$env:ProjectName\TwilioClientWrapper.cs" --exclude-by-file "Adapters\$env:ProjectName\TwilioMessage.cs"
workingDirectory: tests
displayName: 'Coverlet - Run test'
- task: PublishCodeCoverageResults@1
displayName: 'Publish code coverage '
inputs:
codeCoverageTool: Cobertura
summaryFileLocation: '$(Build.SourcesDirectory)\$(OutputFile)'
reportDirectory: '$(Build.SourcesDirectory)'

Просмотреть файл

@ -19,7 +19,7 @@
- - [ ] New pre-built functions ask
- - [X] [4/11] forEach(collection, iterator, expression)
- - [ ] [Post //BUILD] match(regExp)
- - [X] [4/11] lgTemplate(templateName, arg1, arg2,...) - drop support for @{[]}
- - [X] [4/11] lgTemplate(templateName, arg1, arg2,...) - drop support for ${[]}
- - [ ] property(scope, expression) evaluates to scope.<expressionResult>
- - [ ] [4/11] Drop support for object manipulation functions (json, addProperty, setProperty, removeProperty) from expression library - from doc. Vishwac.
- - [ ] [4/11] Document exists function - Vishwac.

245
doc/Build CI/readme.md Normal file
Просмотреть файл

@ -0,0 +1,245 @@
# BotBuilder SDK Build Process Documentation
To verify the consistency and quality of the code, we configured a Build CI process for each repository that belongs to the BotBuilder SDK. The main idea behind this practice is to test the code frequently as the development process proceeds, so that possible issues are identified and rectified early.
The next table shows a comparison of the Build CI process between the BotBuilder SDK repositories.
<table>
<tr>
<th align="center" colspan="2">CI Pipeline</th>
<th align="center">BotBuilder-DotNet</th>
<th align="center">BotBuilder-JS</th>
<th align="center">BotBuilder-Java</th>
<th align="center">BotBuilder-Python</th>
</tr>
<tr align="center">
<td colspan="2">License</td>
<td>✔️</td>
<td>✔️</td>
<td>✔️</td>
<td>✔️</td>
</tr>
<tr align="center">
<td colspan="2">Coverage</td>
<td>✔️</td>
<td>✔️</td>
<td>✔️</td>
<td>✔️</td>
</tr>
<tr align="center">
<td rowspan="2">Build CI</td>
<td>Windows</td>
<td>✔️</td>
<td>✔️</td>
<td>✔️</td>
<td></td>
</tr>
<tr align="center">
<td>Mac/Linux</td>
<td>✔️</td>
<td></td>
<td></td>
<td>✔️</td>
</tr>
<tr align="center">
<td rowspan="2">Nightly Functional Test</td>
<td>Windows</td>
<td>✔️</td>
<td>✔️</td>
<td></td>
<td></td>
</tr>
<tr align="center">
<td>Linux</td>
<td>✔️</td>
<td>✔️</td>
<td></td>
<td></td>
</tr>
<tr align="center">
<td rowspan="2">Browser Compatibility</td>
<td>Chrome</td>
<td></td>
<td>✔️</td>
<td></td>
<td></td>
</tr>
<tr align="center">
<td>Firefox</td>
<td></td>
<td>✔️</td>
<td></td>
<td></td>
</tr>
<tr align="center">
<td rowspan="4">Adapters</td>
<td>Facebook</td>
<td>✔️</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr align="center">
<td>Slack</td>
<td>✔️</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr align="center">
<td>Webex</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr align="center">
<td>Twilio</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr align="center">
<td rowspan="2">Misc</td>
<td>Functional Test Bot Image</td>
<td></td>
<td></td>
<td></td>
<td>✔️</td>
</tr>
<tr align="center">
<td>Functional Test Bot Container</td>
<td></td>
<td></td>
<td></td>
<td>✔️</td>
</tr>
</table>
## **Pipelines details**
### **Build CI**
This pipeline aims to verify the build and tests configured of the repository. It consists of building and running the tests using a specific OS, which can be Windows or Linux.
Each repository has its custom configuration depending on the language used to build.
**DotNet**
- NetCore 2.1.x and 3.1.x
- Binary compatibility tool
> Running on Windows and Mac OS.
**Javascript**
- Node 13
- NPM
> Running on windows only
**Java**
- Java 1.8
- Maven
> Running on Azure and Travis CI
**Python**
Python 3.7.6
> Running on Linux only
**Configuration**
This pipeline will run every pull request pointing to the master branch.
**Possible build failure causes:**
- Error in the compile process
- Error installing project dependencies
- Unit Tests failing result
### **Nightly Functional Test**
This pipeline aims to verify the interaction of a bot deployed in Azure using a specific OS, which can be Windows or Linux. It consists of building, deploying and running some tests against the bot Azure website endpoint.
**Configuration**
This pipeline will run every night
**Possible build failure causes:**
- Error in the compile process
- Error installing project dependencies
- Deployment process not completed
- Environment specific errors
- Bot functionality errors
### **Browser Compatibility**
This pipeline aims to verify the browser compatibility of the BotBuilder-JS repository. It consists of building and deploying a browser-echo bot. Then, it runs some UI against the bot Azure website endpoint.
**Configuration**
This pipeline will run every pull request pointing to the master branch.
**Possible build failure causes:**
- Error in the compile process
- Error installing project dependencies
- Deployment process not completed
- Environment specific errors
- Bot functionality errors
- Webpack configuration issues
- Browser compatibility comprised
### **Adapters**
This set of pipelines aims to verify the correct integration of the adapter and the bot.
It consists of building and deploying an echo bot configured with the selected adapter. Then, it runs some tests against the communication app of the adapter.
Each adapter has its bot and test suite.
**Configuration**
This pipelines will run every night
**Possible build failure causes:**
- Error in the compile process
- Error installing project dependencies
- Deployment process not completed
- Environment specific errors
- Bot functionality errors
- Webpack configuration issues
- Browser compatibility comprised
### **Miscellaneous**
This category contains the extra pipelines configured for specific repositories.
**BotBuilder-Python**
This pipeline aims to verify the correct deployment of a bot created using BotBuilder-Python. It consists of deploying an echo bot using the bot image and container approaches.
**Configuration**
This pipeline will run every pull request pointing to the master branch.
**Possible build failure causes:**
- Error installing project dependencies
- Deployment process not completed
- Environment specific errors
- Bot functionality errors
- Unit Tests failing result

Просмотреть файл

@ -36,7 +36,7 @@ See [here](./api-reference.md) for API reference.
|{} |Used to denote a variable in template expansion. E.g. {myVariable} |N/A |
|() |Enforces precedence order and groups sub expressions into larger expressions. E.g. (A+B)*C |N/A |
|. |Property selector. E.g. myObject.Property1 |N/A |
|@{} |Used to denote parts of a multi-line value that requires evaluation |N/A |
|${} |Used to denote parts of a multi-line value that requires evaluation |N/A |
|\ |Escape character for templates, expressions. |N/A |
|@entityName|Short hand notation that expands to turn.entities.entityName |N/A |
|$propertyName|Short hand notation that expands to dialog.result.property |N/A |

Просмотреть файл

@ -134,16 +134,16 @@ Here is an example -
```
```
Multi-line variation can request template expansion and entity substitution by enclosing the requested operation in @{}.
Multi-line variation can request template expansion and entity substitution by enclosing the requested operation in ${}.
Here is an example -
```markdown
# MultiLineExample
- ```
Here is what I have for the order
- Title: @{reservation.title}
- Location: @{reservation.location}
- Date/ time: @{reservation.dateTimeReadBack}
- Title: ${reservation.title}
- Location: ${reservation.location}
- Date/ time: ${reservation.dateTimeReadBack}
```
```
@ -173,16 +173,16 @@ Here is an example of complex object that your bot's code will parse out and ren
# ImageGalleryTemplate
- ```
{
"titleText": "@{[TitleText]}",
"subTitle": "@{[SubText]}",
"titleText": "${[TitleText]}",
"subTitle": "${[SubText]}",
"images": [
{
"type": "Image",
"url": "@{[CardImages]}"
"url": "${[CardImages]}"
},
{
"type": "Image",
"url": "@{[CardImages]}"
"url": "${[CardImages]}"
}
]
}

Просмотреть файл

@ -85,11 +85,11 @@ Here is an example of a card definition.
# HeroCardTemplate(buttonsCollection)
- ```
[Herocard
title=@{[TitleText]}
subtitle=@{[SubText]}
text=@{[DescriptionText]}
images=@{[CardImages]}
buttons=@{join(buttonsCollection, '|')]
title=${[TitleText]}
subtitle=${[SubText]}
text=${[DescriptionText]}
images=${[CardImages]}
buttons=${join(buttonsCollection, '|')]
```
# TitleText

Просмотреть файл

@ -39,23 +39,23 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
/// </summary>
/// <param name="configuration">An <see cref="IConfiguration"/> instance.</param>
/// <remarks>
/// The configuration keys are:
/// VerifyToken: The token to respond to the initial verification request.
/// AppSecret: The secret used to validate incoming webhooks.
/// AccessToken: An access token for the bot.
/// The adapter uses these configuration keys:
/// - `VerifyToken`, the token to respond to the initial verification request.
/// - `AppSecret`, the secret used to validate incoming webhooks.
/// - `AccessToken`, an access token for the bot.
/// </remarks>
/// <param name="logger">The ILogger implementation this adapter should use.</param>
/// <param name="logger">The logger this adapter should use.</param>
public FacebookAdapter(IConfiguration configuration, ILogger logger = null)
: this(new FacebookClientWrapper(new FacebookAdapterOptions(configuration["FacebookVerifyToken"], configuration["FacebookAppSecret"], configuration["FacebookAccessToken"])), logger)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="FacebookAdapter"/> class.
/// Creates a Facebook adapter.
/// Initializes a new instance of the <see cref="FacebookAdapter"/> class using an existing Facebook client.
/// </summary>
/// <param name="facebookClient">A Facebook API interface.</param>
/// <param name="logger">The ILogger implementation this adapter should use.</param>
/// <param name="logger">The logger this adapter should use.</param>
/// <exception cref="ArgumentNullException"><paramref name="facebookClient"/> is null.</exception>
public FacebookAdapter(FacebookClientWrapper facebookClient, ILogger logger = null)
{
_facebookClient = facebookClient ?? throw new ArgumentNullException(nameof(facebookClient));
@ -63,12 +63,16 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
}
/// <summary>
/// Standard BotBuilder adapter method to send a message from the bot to the messaging API.
/// Sends activities to the conversation.
/// </summary>
/// <param name="turnContext">A TurnContext representing the current incoming message and environment.</param>
/// <param name="activities">An array of outgoing activities to be sent back to the messaging API.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
/// <param name="turnContext">The context object for the turn.</param>
/// <param name="activities">The activities to send.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>If the activities are successfully sent, the task result contains
/// an array of <see cref="ResourceResponse"/> objects containing the IDs that
/// the receiving channel assigned to the activities.</remarks>
public override async Task<ResourceResponse[]> SendActivitiesAsync(ITurnContext turnContext, Activity[] activities, CancellationToken cancellationToken)
{
var responses = new List<ResourceResponse>();
@ -96,15 +100,15 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
if (activity.Name.Equals(HandoverConstants.PassThreadControl, StringComparison.Ordinal))
{
var recipient = (string)activity.Value == "inbox" ? HandoverConstants.PageInboxId : (string)activity.Value;
await _facebookClient.PassThreadControlAsync(recipient, activity.Conversation.Id, HandoverConstants.MetadataPassThreadControl).ConfigureAwait(false);
await _facebookClient.PassThreadControlAsync(recipient, activity.Conversation.Id, HandoverConstants.MetadataPassThreadControl, cancellationToken).ConfigureAwait(false);
}
else if (activity.Name.Equals(HandoverConstants.TakeThreadControl, StringComparison.Ordinal))
{
await _facebookClient.TakeThreadControlAsync(activity.Conversation.Id, HandoverConstants.MetadataTakeThreadControl).ConfigureAwait(false);
await _facebookClient.TakeThreadControlAsync(activity.Conversation.Id, HandoverConstants.MetadataTakeThreadControl, cancellationToken).ConfigureAwait(false);
}
else if (activity.Name.Equals(HandoverConstants.RequestThreadControl, StringComparison.Ordinal))
{
await _facebookClient.RequestThreadControlAsync(activity.Conversation.Id, HandoverConstants.MetadataRequestThreadControl).ConfigureAwait(false);
await _facebookClient.RequestThreadControlAsync(activity.Conversation.Id, HandoverConstants.MetadataRequestThreadControl, cancellationToken).ConfigureAwait(false);
}
}
@ -121,36 +125,43 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
}
/// <summary>
/// Standard BotBuilder adapter method to update a previous message with new content.
/// Throws a <see cref="NotImplementedException"/> exception in all cases.
/// </summary>
/// <param name="turnContext">A TurnContext representing the current incoming message and environment.</param>
/// <param name="activity">The updated activity in the form '{id: `id of activity to update`, ...}'.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>A resource response with the Id of the updated activity.</returns>
/// <param name="turnContext">The context object for the turn.</param>
/// <param name="activity">New replacement activity.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
public override Task<ResourceResponse> UpdateActivityAsync(ITurnContext turnContext, Activity activity, CancellationToken cancellationToken)
{
return Task.FromException<ResourceResponse>(new NotImplementedException("Facebook adapter does not support updateActivity."));
}
/// <summary>
/// Standard BotBuilder adapter method to delete a previous message.
/// Throws a <see cref="NotImplementedException"/> exception in all cases.
/// </summary>
/// <param name="turnContext">A TurnContext representing the current incoming message and environment.</param>
/// <param name="reference">An object in the form "{activityId: `id of message to delete`, conversation: { id: `id of channel`}}".</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
/// <param name="turnContext">The context object for the turn.</param>
/// <param name="reference">Conversation reference for the activity to delete.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
public override Task DeleteActivityAsync(ITurnContext turnContext, ConversationReference reference, CancellationToken cancellationToken)
{
return Task.FromException(new NotImplementedException("Facebook adapter does not support deleteActivity."));
}
/// <summary>
/// Standard BotBuilder adapter method for continuing an existing conversation based on a conversation reference.
/// Sends a proactive message to a conversation using a conversation reference.
/// </summary>
/// <param name="reference">A conversation reference to be applied to future messages.</param>
/// <param name="logic">A bot logic function that will perform continuing action in the form `async(context) => { ... }`.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task ContinueConversationAsync(ConversationReference reference, BotCallbackHandler logic)
/// <param name="reference">A reference to the conversation to continue.</param>
/// <param name="logic">The method to call for the resulting bot turn.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>Call this method to proactively send a message to a conversation.</remarks>
/// <exception cref="ArgumentNullException"><paramref name="logic"/> or
/// <paramref name="reference"/> is null.</exception>
public async Task ContinueConversationAsync(ConversationReference reference, BotCallbackHandler logic, CancellationToken cancellationToken)
{
if (reference == null)
{
@ -166,7 +177,7 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
using (var context = new TurnContext(this, request))
{
await RunPipelineAsync(context, logic, default).ConfigureAwait(false);
await RunPipelineAsync(context, logic, cancellationToken).ConfigureAwait(false);
}
}
@ -179,8 +190,6 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>Call this method to proactively send a message to a conversation.
/// Most _channels require a user to initialize a conversation with a bot
/// before the bot can send activities to the user.
/// <para>This method registers the following services for the turn.<list type="bullet">
/// <item><description><see cref="IIdentity"/> (key = "BotIdentity"), a claims claimsIdentity for the bot.
/// </description></item>
@ -198,13 +207,16 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
}
/// <summary>
/// Accept an incoming webhook request and convert it into a TurnContext which can be processed by the bot's logic.
/// Accepts an incoming webhook request, creates a turn context,
/// and runs the middleware pipeline for an incoming TRUSTED activity.
/// </summary>
/// <param name="httpRequest">A request object.</param>
/// <param name="httpResponse">A response object.</param>
/// <param name="bot">A bot logic function.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
/// <param name="httpRequest">Represents the incoming side of an HTTP request.</param>
/// <param name="httpResponse">Represents the outgoing side of an HTTP request.</param>
/// <param name="bot">The code to run at the end of the adapter's middleware pipeline.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <exception cref="AuthenticationException">The webhook receives message with invalid signature.</exception>
public async Task ProcessAsync(HttpRequest httpRequest, HttpResponse httpResponse, IBot bot, CancellationToken cancellationToken = default)
{
if (httpRequest.Query["hub.mode"] == HubModeSubscribe)

Просмотреть файл

@ -26,25 +26,25 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
}
/// <summary>
/// Gets or sets the alternate root url used to construct calls to Facebook's API. Defaults to 'graph.facebook.com' but can be changed (for mocking, proxy, etc).
/// Gets or sets the alternate root URL used to construct calls to Facebook's API. Defaults to "graph.facebook.com" but can be changed (for mocking, proxy, etc).
/// </summary>
/// <value>The API host.</value>
public string FacebookApiHost { get; set; }
/// <summary>
/// Gets or sets the alternate API version used to construct calls to Facebook's API. Defaults to v3.2.
/// Gets or sets the alternate API version used to construct calls to Facebook's API. Defaults to "v3.2".
/// </summary>
/// <value>The API version.</value>
public string FacebookApiVersion { get; set; }
/// <summary>
/// Gets or sets the "verify token" used to initially create and verify the Webhooks subscription settings on Facebook's developer portal.
/// Gets or sets the verify token used to initially create and verify the webhooks subscription settings on Facebook's developer portal.
/// </summary>
/// <value>The verification token.</value>
/// <value>The verify token.</value>
public string FacebookVerifyToken { get; set; }
/// <summary>
/// Gets or sets the "app secret" from the "basic settings" page from your app's configuration in the Facebook developer portal.
/// Gets or sets the app secret from the **Basic Settings** page from your app's configuration in the Facebook developer portal.
/// </summary>
/// <value>The app secret.</value>
public string FacebookAppSecret { get; set; }
@ -57,10 +57,9 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
public string FacebookAccessToken { get; set; }
/// <summary>
/// Gets or sets the function described below.
/// When bound to multiple teams, provide a function that, given a page id, will return the page access token for that page.
/// Throws a <see cref="NotImplementedException"/> exception in all cases.
/// </summary>
/// <param name="pageId">The page Id.</param>
/// <param name="pageId">The page ID.</param>
/// <returns>The access token for the page.</returns>
public virtual Task<string> GetAccessTokenForPageAsync(string pageId)
{

Просмотреть файл

@ -25,20 +25,22 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
/// <summary>
/// Initializes a new instance of the <see cref="FacebookClientWrapper"/> class.
/// </summary>
/// <param name="options">An object containing API credentials, a webhook verification token and other options.</param>
/// <param name="options">An object containing API credentials, a webhook verification token, and other options.</param>
public FacebookClientWrapper(FacebookAdapterOptions options)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
}
/// <summary>
/// Send a REST message to Facebook.
/// Sends a REST message to Facebook.
/// </summary>
/// <param name="path">Path to the API endpoint, for example `/me/messages`.</param>
/// <param name="payload">An object to be sent as parameters to the API call..</param>
/// <param name="method">HTTP method, for example POST, GET, DELETE or PUT.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>A task representing the async operation.</returns>
/// <param name="payload">An object to be sent as parameters to the API call.</param>
/// <param name="method">The HTTP method, for example POST, GET, DELETE or PUT.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <exception cref="ArgumentNullException"><paramref name="path"/> or <paramref name="payload"/> is null.</exception>
public virtual async Task<string> SendMessageAsync(string path, FacebookMessage payload, HttpMethod method = null, CancellationToken cancellationToken = default)
{
if (path == null)
@ -92,9 +94,10 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
/// <summary>
/// Verifies the SHA1 signature of the raw request payload before bodyParser parses it will abort parsing if signature is invalid, and pass a generic error to response.
/// </summary>
/// <param name="request">An Http request object.</param>
/// <param name="request">Represents the incoming side of an HTTP request.</param>
/// <param name="payload">The request body.</param>
/// <returns>The result of the comparison between the signature in the request and hashed body.</returns>
/// <exception cref="ArgumentNullException"><paramref name="request"/> is null.</exception>
public virtual bool VerifySignature(HttpRequest request, string payload)
{
if (request == null)
@ -117,7 +120,7 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
}
/// <summary>
/// Generate the app secret proof used to increase security on calls to the graph API.
/// Generates the app secret proof used to increase security on calls to the Graph API.
/// </summary>
/// <returns>The app secret proof.</returns>
public virtual string GetAppSecretProof()
@ -130,12 +133,14 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
}
/// <summary>
/// Verifies the VerifyToken from the message and if it matches the one configured, it sends back the challenge.
/// Verifies the verify token from the message. If the token matches the one configured, sends back the challenge.
/// </summary>
/// <param name="request">An Http request object.</param>
/// <param name="response">An Http response object.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>A task representing the async operation.</returns>
/// <param name="request">Represents the incoming side of an HTTP request.</param>
/// <param name="response">Represents the outgoing side of an HTTP request.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <exception cref="ArgumentNullException"><paramref name="request"/> or <paramref name="response"/> is null.</exception>
public virtual async Task VerifyWebhookAsync(HttpRequest request, HttpResponse response, CancellationToken cancellationToken)
{
if (request == null)
@ -169,8 +174,11 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
/// </summary>
/// <param name="postType">The REST post type (GET, PUT, POST, etc).</param>
/// <param name="content">The string content to be posted to Facebook.</param>
/// <returns>A bool indicating the success of the operation.</returns>
public virtual async Task<bool> PostToFacebookApiAsync(string postType, string content)
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>`true` if the operation succeeded; otherwise, `false`.</returns>
/// <exception cref="ArgumentNullException"><paramref name="postType"/> or <paramref name="content"/> is null.</exception>
public virtual async Task<bool> PostToFacebookApiAsync(string postType, string content, CancellationToken cancellationToken)
{
if (postType == null)
{
@ -195,7 +203,7 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
using (var client = new HttpClient())
{
var res = await client.SendAsync(requestMessage, CancellationToken.None).ConfigureAwait(false);
var res = await client.SendAsync(requestMessage, cancellationToken).ConfigureAwait(false);
return res.IsSuccessStatusCode;
}
}
@ -204,10 +212,13 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
/// <summary>
/// Sends the request_thread_control webhook event to Facebook.
/// </summary>
/// <param name="userId">The sender user Id.</param>
/// <param name="message">An optional message for the metadata paremter.</param>
/// <returns>A bool value indicating the success of the operation.</returns>
public virtual async Task<bool> RequestThreadControlAsync(string userId, string message)
/// <param name="userId">The sender user ID.</param>
/// <param name="message">An optional message for the metadata parameter.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>`true` if the operation succeeded; otherwise, `false`.</returns>
/// <exception cref="ArgumentNullException"><paramref name="userId"/> is null.</exception>
public virtual async Task<bool> RequestThreadControlAsync(string userId, string message, CancellationToken cancellationToken)
{
if (userId == null)
{
@ -215,16 +226,19 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
}
var content = new { recipient = new { id = userId }, metadata = message };
return await PostToFacebookApiAsync($"/me/{HandoverConstants.RequestThreadControl}", JsonConvert.SerializeObject(content)).ConfigureAwait(false);
return await PostToFacebookApiAsync($"/me/{HandoverConstants.RequestThreadControl}", JsonConvert.SerializeObject(content), cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Sends the take_thread_control webhook event to Facebook.
/// </summary>
/// <param name="userId">The sender user Id.</param>
/// <param name="message">An optional message for the metadata paremter.</param>
/// <returns>A bool value indicating the success of the operation.</returns>
public virtual async Task<bool> TakeThreadControlAsync(string userId, string message)
/// <param name="userId">The sender user ID.</param>
/// <param name="message">An optional message for the metadata parameter.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>`true` if the operation succeeded; otherwise, `false`.</returns>
/// <exception cref="ArgumentNullException"><paramref name="userId"/> is null.</exception>
public virtual async Task<bool> TakeThreadControlAsync(string userId, string message, CancellationToken cancellationToken)
{
if (userId == null)
{
@ -232,17 +246,20 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
}
var content = new { recipient = new { id = userId }, metadata = message };
return await PostToFacebookApiAsync($"/me/{HandoverConstants.TakeThreadControl}", JsonConvert.SerializeObject(content)).ConfigureAwait(false);
return await PostToFacebookApiAsync($"/me/{HandoverConstants.TakeThreadControl}", JsonConvert.SerializeObject(content), cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Sends the pass_thread_control webhook event to Facebook.
/// </summary>
/// <param name="targetAppId">The Id of the target app to pass control to.</param>
/// <param name="userId">The sender user Id.</param>
/// <param name="message">An optional message for the metadata paremter.</param>
/// <returns>A bool value indicating the success of the operation.</returns>
public virtual async Task<bool> PassThreadControlAsync(string targetAppId, string userId, string message)
/// <param name="targetAppId">The ID of the target app to pass control to.</param>
/// <param name="userId">The sender user ID.</param>
/// <param name="message">An optional message for the metadata parameter.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>`true` if the operation succeeded; otherwise, `false`.</returns>
/// <exception cref="ArgumentNullException"><paramref name="targetAppId"/> or <paramref name="userId"/> is null.</exception>
public virtual async Task<bool> PassThreadControlAsync(string targetAppId, string userId, string message, CancellationToken cancellationToken)
{
if (targetAppId == null)
{
@ -255,7 +272,7 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
}
var content = new { recipient = new { id = userId }, target_app_id = targetAppId, metadata = message };
return await PostToFacebookApiAsync($"/me/{HandoverConstants.PassThreadControl}", JsonConvert.SerializeObject(content)).ConfigureAwait(false);
return await PostToFacebookApiAsync($"/me/{HandoverConstants.PassThreadControl}", JsonConvert.SerializeObject(content), cancellationToken).ConfigureAwait(false);
}
}
}

Просмотреть файл

@ -11,23 +11,23 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
public class AttachmentPayload
{
/// <summary>
/// Gets or sets the url of the attachment.
/// Gets or sets the URL of the attachment.
/// </summary>
/// <value>Url of the attachment.</value>
/// <value>The URL of the attachment.</value>
[JsonProperty(PropertyName = "url")]
public Uri Url { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the attachment is reusable or not. Default false.
/// </summary>
/// <value>Indicates the reusable condition.</value>
/// <value>Indicates whether the attachment is reusable.</value>
[JsonProperty(PropertyName = "is_reusable")]
public bool IsReusable { get; set; }
/// <summary>
/// Gets or sets the Id of the attachment (for reusable attachments).
/// Gets or sets the ID of the attachment (for reusable attachments).
/// </summary>
/// <value>The id of the saved attachment.</value>
/// <value>The ID of the saved attachment.</value>
[JsonProperty(PropertyName = "attachment_id")]
public string AttachmentId { get; set; }
@ -60,27 +60,27 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
public List<Element> Elements { get; } = new List<Element>();
/// <summary>
/// Newtonsoft Json method for conditionally serializing IsReusable property.
/// Newtonsoft JSON method for conditionally serializing the <see cref="IsReusable"/> property.
/// </summary>
/// <returns>A boolean with the value.</returns>
/// <returns>`true` to serialize the property; otherwise, `false`.</returns>
public bool ShouldSerializeIsReusable()
{
return IsReusable;
}
/// <summary>
/// Newtonsoft Json method for conditionally serializing Buttons property.
/// Newtonsoft JSON method for conditionally serializing the <see cref="Buttons"/> property.
/// </summary>
/// <returns>A boolean with the value.</returns>
/// <returns>`true` to serialize the property; otherwise, `false`.</returns>
public bool ShouldSerializeButtons()
{
return Buttons.Count > 0;
}
/// <summary>
/// Newtonsoft Json method for conditionally serializing Elements property.
/// Newtonsoft JSON method for conditionally serializing the <see cref="Elements"/> property.
/// </summary>
/// <returns>A boolean with the value.</returns>
/// <returns>`true` to serialize the property; otherwise, `false`.</returns>
public bool ShouldSerializeElements()
{
return Elements.Count > 0;

Просмотреть файл

@ -10,14 +10,15 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
/// <summary>
/// Gets or sets the type of the attachment.
/// </summary>
/// <value>Type of attachment, may be image, audio, video, file or template.</value>
/// <value>The type of attachment.
/// May be "image", "audio", "video", "file", or "template".</value>
[JsonProperty(PropertyName = "type")]
public string Type { get; set; }
/// <summary>
/// Gets or sets the payload of the attachment.
/// </summary>
/// <value>Payload of the attachment.</value>
/// <value>The payload of the attachment.</value>
[JsonProperty(PropertyName = "payload")]
public AttachmentPayload Payload { get; set; }
}

Просмотреть файл

@ -8,9 +8,9 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
public class FacebookBotUser
{
/// <summary>
/// Gets or sets the Id of the bot user.
/// Gets or sets the ID of the bot user.
/// </summary>
/// <value>The Id of the bot user.</value>
/// <value>The ID of the bot user.</value>
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
}

Просмотреть файл

@ -8,27 +8,28 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
public class FacebookEntry
{
/// <summary>
/// Gets or sets the Id.
/// Gets or sets the page ID.
/// </summary>
/// <value>The page ID of the page.</value>
/// <value>The ID of the page.</value>
public string Id { get; set; }
/// <summary>
/// Gets or sets the time of the update.
/// </summary>
/// <value>Time of update (epoch time in milliseconds).</value>
/// <value>The time of update (epoch time in milliseconds).</value>
public long Time { get; set; }
/// <summary>
/// Gets the messaging list.
/// </summary>
/// <value>List containing one messaging object. Note that even though this is an enumerable, it will only contain one object.</value>
/// <value>List containing one messaging object.</value>
/// <remarks>Note that even though this is an enumerable, it will only contain one object.</remarks>
public List<FacebookMessage> Messaging { get; } = new List<FacebookMessage>();
/// <summary>
/// Gets the changes list.
/// </summary>
/// <value>List containing the list of changes.</value>
/// <value>The list of changes.</value>
public List<FacebookMessage> Changes { get; } = new List<FacebookMessage>();
/// <summary>
@ -37,19 +38,31 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
/// <value>List containing the messages sent while in standby mode.</value>
public List<FacebookMessage> Standby { get; } = new List<FacebookMessage>();
/// <summary>
/// Newtonsoft JSON method for conditionally serializing the <see cref="Messaging"/> property.
/// </summary>
/// <returns>`true` to serialize the property; otherwise, `false`.</returns>
public bool ShouldSerializeMessaging()
{
return Messaging.Count > 0;
}
/// <summary>
/// Newtonsoft JSON method for conditionally serializing the <see cref="Standby"/> property.
/// </summary>
/// <returns>`true` to serialize the property; otherwise, `false`.</returns>
public bool ShouldSerializeStandby()
{
return Messaging.Count > 0;
return Standby.Count > 0;
}
/// <summary>
/// Newtonsoft JSON method for conditionally serializing the <see cref="Changes"/> property.
/// </summary>
/// <returns>`true` to serialize the property; otherwise, `false`.</returns>
public bool ShouldSerializeChanges()
{
return Messaging.Count > 0;
return Changes.Count > 0;
}
}
}

Просмотреть файл

@ -8,21 +8,21 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
public class FacebookPostBack
{
/// <summary>
/// Gets or Sets the title of the post back message.
/// Gets or sets the title of the post back message.
/// </summary>
/// <value>The title of the post back message.</value>
[JsonProperty(PropertyName = "title")]
public string Title { get; set; }
/// <summary>
/// Gets or Sets the string to send back to the webhook.
/// Gets or sets the string to send back to the webhook.
/// </summary>
/// <value>The string to post back to the webhook.</value>
[JsonProperty(PropertyName = "payload")]
public string Payload { get; set; }
/// <summary>
/// Gets or Sets the referral of the post back message.
/// Gets or sets the referral of the post back message.
/// </summary>
/// <value>The referral of the post back message.</value>
[JsonProperty(PropertyName = "referral")]

Просмотреть файл

@ -10,23 +10,23 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
{
/// <summary>
/// Gets or sets the content type of the reply. Can be:
/// 'text' Sends a text button
/// 'user_phone_number' Sends a button allowing recipient to send the phone number associated with their account.
/// 'user_email' Sends a button allowing recipient to send the email associated with their account.
/// - "text", which sends a text button.
/// - "user_phone_number", which sends a button allowing the recipient to send the phone number associated with their account.
/// - "user_email", which sends a button allowing the recipient to send the email associated with their account.
/// </summary>
/// <value>The content type.</value>
[JsonProperty(PropertyName = "content_type")]
public string ContentType { get; set; }
/// <summary>
/// Gets or sets the title of the reply. Required if content_type is 'text'.
/// Gets or sets the title of the reply. Required if content_type is "text".
/// </summary>
/// <value>The title of the reply. 20 character limit. </value>
[JsonProperty(PropertyName = "title")]
public string Title { get; set; }
/// <summary>
/// Gets or sets the payload of the reply. May be set to an empty string if image_url is set.
/// Gets or sets the payload of the reply. May be set to an empty string if the <see cref="ImageUrl"/> property is set.
/// </summary>
/// <value>The payload. Can be either a string or a long.</value>
[JsonProperty(PropertyName = "payload")]
@ -34,7 +34,7 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
/// <summary>
/// Gets or sets the optional URL of the image to display on the quick reply button for text quick replies.
/// Required if Title is an empty string.
/// Required if the <see cref="Title"/> property is empty.
/// </summary>
/// <value>The optional URL of the image to display.</value>
[JsonProperty(PropertyName = "image_url")]

Просмотреть файл

@ -8,30 +8,30 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
public class FacebookRecipient
{
/// <summary>
/// Gets or sets the Id.
/// Gets or sets the message recipient ID.
/// </summary>
/// <value>Page Scoped User ID (PSID) of the message recipient.</value>
/// <value>The Page-scoped ID (PSID) of the message recipient.</value>
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
/// <summary>
/// Gets or sets the UserRef.
/// Gets or sets the value for the `user_ref` parameter of the checkbox plugin.
/// </summary>
/// <value>Used for the checkbox plugin.</value>
[JsonProperty(PropertyName = "user_ref")]
public string UserRef { get; set; }
/// <summary>
/// Gets or sets the PostId.
/// Gets or sets the post ID.
/// </summary>
/// <value>Used for Private Replies to reference the visitor post to reply to.</value>
/// <value>Used for private replies to reference the visitor post to reply to.</value>
[JsonProperty(PropertyName = "post_id")]
public string PostId { get; set; }
/// <summary>
/// Gets or sets the CommentId.
/// Gets or sets the comment ID.
/// </summary>
/// <value>Used for Private Replies to reference the post comment to reply to.</value>
/// <value>Used for private replies to reference the visitor comment to reply to.</value>
[JsonProperty(PropertyName = "comment_id")]
public string CommentId { get; set; }
}

Просмотреть файл

@ -8,16 +8,16 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
public class FacebookResponseOk
{
/// <summary>
/// Gets or sets the recipient Id.
/// Gets or sets the recipient ID.
/// </summary>
/// <value>The Id of the recipient.</value>
/// <value>The ID of the recipient.</value>
[JsonProperty(PropertyName = "recipient_id")]
public string RecipientId { get; set; }
/// <summary>
/// Gets or sets the message Id.
/// Gets or sets the message ID.
/// </summary>
/// <value>The message id.</value>
/// <value>The message ID.</value>
[JsonProperty(PropertyName = "message_id")]
public string MessageId { get; set; }
}

Просмотреть файл

@ -8,26 +8,26 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Handover
public class FacebookPassThreadControl : FacebookThreadControl
{
/// <summary>
/// Gets or Sets the app id of the new owner.
/// Gets or sets the app ID of the new owner.
/// </summary>
/// <remarks>
/// 263902037430900 for the page inbox.
/// </remarks>
/// <value>The app id of the new owner.</value>
/// <value>The app ID of the new owner.</value>
[JsonProperty("new_owner_app_id")]
public string NewOwnerAppId { get; set; }
/// <summary>
/// Gets or Sets the app id of the previous owner.
/// Gets or sets the app ID of the previous owner.
/// </summary>
/// <value>The app id of the previous owner.</value>
/// <value>The app ID of the previous owner.</value>
[JsonProperty("previous_owner_app_id")]
public string PreviousOwnerAppId { get; set; }
/// <summary>
/// Gets or Sets the app id of the requested owner.
/// Gets or sets the app ID of the requested owner.
/// </summary>
/// <value>The app id of the requested owner.</value>
/// <value>The app ID of the requested owner.</value>
[JsonProperty("requested_owner_app_id")]
public string RequestedOwnerAppId { get; set; }
}

Просмотреть файл

@ -5,17 +5,18 @@ using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Handover
{
/// <summary>A Facebook thread control message, including appid of requested thread owner and an optional message to send with the request.</summary>
/// <summary>A Facebook thread control message, including app ID of requested thread owner and an optional message
/// to send with the request.</summary>
public class FacebookRequestThreadControl : FacebookThreadControl
{
/// <summary>
/// Gets or Sets the app id of the requested owner.
/// Gets or sets the app ID of the requested owner.
/// </summary>
/// <remarks>
/// 263902037430900 for the page inbox.
/// </remarks>
/// <value>
/// the app id of the requested owner.
/// the app ID of the requested owner.
/// </value>
[JsonProperty("requested_owner_app_id")]
public string RequestedOwnerAppId { get; set; }

Просмотреть файл

@ -8,13 +8,13 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Handover
public class FacebookTakeThreadControl : FacebookThreadControl
{
/// <summary>
/// Gets or Sets the app id of the previous owner.
/// Gets or sets the app ID of the previous owner.
/// </summary>
/// <remarks>
/// 263902037430900 for the page inbox.
/// </remarks>
/// <value>
/// The app id of the previous owner.
/// The app ID of the previous owner.
/// </value>
[JsonProperty("previous_owner_app_id")]
public string PreviousOwnerAppId { get; set; }

Просмотреть файл

@ -7,7 +7,7 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Handover
public class FacebookThreadControl
{
/// <summary>
/// Gets or Sets the message sent from the requester.
/// Gets or sets the message sent from the requester.
/// </summary>
/// <remarks>
/// Example: "All yours!".

Просмотреть файл

@ -21,7 +21,7 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Handover
public const string TakeThreadControl = "take_thread_control";
/// <summary>
/// Constant value for the Inbox Id of any page in Facebook.
/// Constant value for the inbox ID of any page in Facebook.
/// </summary>
public const string PageInboxId = "263902037430900";

Просмотреть файл

@ -16,9 +16,9 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Templates
public string Type { get; set; }
/// <summary>
/// Gets or sets the url of the button.
/// Gets or sets the URL of the button.
/// </summary>
/// <value>Url of the button.</value>
/// <value>The URL of the button.</value>
[JsonProperty(PropertyName = "url")]
public Uri Url { get; set; }

Просмотреть файл

@ -16,9 +16,9 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Templates
public string ActionType { get; set; }
/// <summary>
/// Gets or sets the default url to open.
/// Gets or sets the default URL to open.
/// </summary>
/// <value>Url of the action.</value>
/// <value>The URL of the action.</value>
[JsonProperty(PropertyName = "url")]
public Uri ActionUrl { get; set; }
@ -30,9 +30,9 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Templates
public bool MessengerExtensions { get; set; }
/// <summary>
/// Gets or sets the height ratio for the webview. It can be COMPACT, TALL or FULL.
/// Gets or sets the height ratio for the WebView. It can be "COMPACT", "TALL", or "FULL".
/// </summary>
/// <value>the height ratio for the webview.</value>
/// <value>The height ratio for the WebView.</value>
[JsonProperty(PropertyName = "webview_height_ratio")]
public string WebviewHeightRatio { get; set; }
}

Просмотреть файл

@ -8,7 +8,7 @@ using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Templates
{
/// <summary>
/// Represents an element of a Template Message.
/// Represents an element of a template message.
/// </summary>
public class Element
{
@ -20,9 +20,9 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Templates
public string MediaType { get; set; }
/// <summary>
/// Gets or sets the url of the media element.
/// Gets or sets the URL of the media element.
/// </summary>
/// <value>Url of the media element.</value>
/// <value>The URL of the media element.</value>
[JsonProperty(PropertyName = "url")]
public Uri Url { get; set; }
@ -41,9 +41,9 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Templates
public string Subtitle { get; set; }
/// <summary>
/// Gets or sets the url of the image.
/// Gets or sets the URL of the image.
/// </summary>
/// <value>The url of the image.</value>
/// <value>The URL of the image.</value>
[JsonProperty(PropertyName = "image_url")]
public Uri ImageUrl { get; set; }
@ -62,9 +62,9 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Templates
public List<Button> Buttons { get; } = new List<Button>();
/// <summary>
/// Newtonsoft Json method for conditionally serializing Buttons property.
/// Newtonsoft JSON method for conditionally serializing the <see cref="Buttons"/> property.
/// </summary>
/// <returns>A boolean with the value.</returns>
/// <returns>`true` to serialize the property; otherwise, `false`.</returns>
public bool ShouldSerializeButtons()
{
return Buttons.Count > 0;

Просмотреть файл

@ -18,10 +18,11 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
public static class FacebookHelper
{
/// <summary>
/// Converts an Activity object to a Facebook messenger outbound message ready for the API.
/// Converts a Bot Framework activity to a Facebook messenger outbound message ready for the API.
/// </summary>
/// <param name="activity">The activity to be converted to Facebook message.</param>
/// <returns>The resulting message.</returns>
/// <exception cref="ArgumentNullException"><paramref name="activity"/> is null.</exception>
public static FacebookMessage ActivityToFacebook(Activity activity)
{
if (activity == null)
@ -80,10 +81,12 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
}
/// <summary>
/// Handles each individual message inside a webhook payload (webhook may deliver more than one message at a time).
/// Converts a single Facebook messenger message to a Bot Framework activity.
/// </summary>
/// <param name="message">The message to be processed.</param>
/// <returns>An Activity with the result.</returns>
/// <exception cref="ArgumentNullException"><paramref name="message"/> is null.</exception>
/// <remarks>A webhook call may deliver more than one message at a time.</remarks>
public static Activity ProcessSingleMessage(FacebookMessage message)
{
if (message == null)
@ -152,10 +155,10 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
}
/// <summary>
/// Extracts attachments from the facebook message.
/// Extracts attachments from a Facebook message.
/// </summary>
/// <param name="message">The <see cref="Message"/>used for input.</param>
/// <returns>A List of <see cref="Attachment"/>.</returns>
/// <param name="message">The message to get attachments from.</param>
/// <returns>A List of the attachments contained within the message.</returns>
public static List<Attachment> HandleMessageAttachments(Message message)
{
var attachmentsList = new List<Attachment>();
@ -173,16 +176,19 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
return attachmentsList;
}
/// <summary>
/// Writes the HttpResponse.
/// Writes an HTTP response payload.
/// </summary>
/// <param name="response">The httpResponse.</param>
/// <param name="code">The status code to be written.</param>
/// <param name="response">The HTTP response to write to.</param>
/// <param name="code">The status code to apply.</param>
/// <param name="text">The text to be written.</param>
/// <param name="encoding">The encoding for the text.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <exception cref="ArgumentNullException"><paramref name="response"/>, <paramref name="text"/>,
/// or <paramref name="encoding"/> is null.</exception>
public static async Task WriteAsync(HttpResponse response, HttpStatusCode code, string text, Encoding encoding, CancellationToken cancellationToken)
{
if (response == null)
@ -211,8 +217,8 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
/// <summary>
/// Generates an activity that displays a typing indicator.
/// </summary>
/// <param name="recipientId">The id of the recipient of the message.</param>
/// <returns>An activity with sender_action equal to 'typing_on'.</returns>
/// <param name="recipientId">The ID of the recipient of the message.</param>
/// <returns>An activity with sender_action equal to "typing_on".</returns>
public static Activity GenerateTypingActivity(string recipientId)
{
var activity = new Activity()

Просмотреть файл

@ -8,21 +8,23 @@ using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook
{
/// <summary>
/// Represents the messaging structure. See https://developers.facebook.com/docs/messenger-platform/reference/webhook-events/#messaging.
/// Represents information associated with a Facebook webhook event. For more information, see the Facebook
/// [Webhook Events Reference](https://developers.facebook.com/docs/messenger-platform/reference/webhook-events).
/// </summary>
public class FacebookMessage
{
/// <summary>
/// Initializes a new instance of the <see cref="FacebookMessage"/> class.
/// </summary>
/// <param name="recipientId">Id of the message recipient.</param>
/// <param name="message"><see cref="Message"/> representing the contents of the message.</param>
/// <param name="messagingType">Property identifying the messaging type of the message being sent. See https://developers.facebook.com/docs/messenger-platform/send-messages#messaging_types. </param>
/// <param name="recipientId">Contents of the recipient ID field.</param>
/// <param name="message">Contents of the message field.</param>
/// <param name="messagingType">The type of webhook event. For more information, see the Facebook
/// [List of Webhook Events](https://developers.facebook.com/docs/messenger-platform/reference/webhook-events#event_list).</param>
/// <param name="tag">The optional message tag string. See https://developers.facebook.com/docs/messenger-platform/send-messages/message-tags.</param>
/// <param name="notificationType">The optional notification type: REGULAR (default value), SILENT_PUSH, NO_PUSH.</param>
/// <param name="personaId">The persona Id.</param>
/// <param name="personaId">The persona ID.</param>
/// <param name="senderAction">Message state to display to the user: typing_on, typing_off, mark_seen. Cannot be sent with 'message'. When used, 'recipient' should be the only other property set in the request.</param>
/// <param name="senderId">The sender Id.</param>
/// <param name="senderId">The sender ID.</param>
public FacebookMessage(string recipientId, Message message, string messagingType, string tag = null, string notificationType = null, string personaId = null, string senderAction = null, string senderId = null)
{
Recipient.Id = recipientId;
@ -92,9 +94,9 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
public string SenderAction { get; set; }
/// <summary>
/// Gets or sets the timestamp.
/// Gets or sets the time-stamp.
/// </summary>
/// <value>Timestamp.</value>
/// <value>Time-stamp.</value>
[JsonProperty(PropertyName = "timestamp")]
public long TimeStamp { get; set; }
@ -108,7 +110,7 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
/// <summary>
/// Gets or sets the value of the postback property.
/// </summary>
/// <value>The postback payload. Postbacks occur when a postback button, Get Started button, or persistent menu item is tapped.</value>
/// <value>The postback payload. A postback occurs when a postback button, **Get Started** button, or persistent menu item is tapped.</value>
[JsonProperty(PropertyName = "postback")]
public FacebookPostBack PostBack { get; set; }

Просмотреть файл

@ -54,35 +54,39 @@ namespace Microsoft.Bot.Builder.Adapters.Facebook
/// <summary>
/// Gets or sets a value indicating whether the message was sent from the page itself.
/// </summary>
/// <value>A value indicating whether the message was sent from the page itself.</value>
/// <value>`true` if the message was sent from the page itself; otherwise, `false`.</value>
[JsonProperty(PropertyName = "is_echo")]
public bool IsEcho { get; set; }
/// <summary>
/// Gets or sets the Mid.
/// Gets or sets the message ID.
/// </summary>
/// <value>Message ID.</value>
/// <value>The message ID.</value>
[JsonProperty(PropertyName = "mid")]
public string Mid { get; set; }
/// <summary>
/// Newtonsoft Json method for conditionally serializing the QuickReplies property.
/// Newtonsoft JSON method for conditionally serializing the <see cref="QuickReplies"/> property.
/// </summary>
/// <returns>A boolean with the value.</returns>
/// <returns>`true` to serialize the property; otherwise, `false`.</returns>
public bool ShouldSerializeQuickReplies()
{
return QuickReplies.Count > 0;
}
/// <summary>
/// Newtonsoft Json method for conditionally serializing the IsEcho property.
/// Newtonsoft JSON method for conditionally serializing the <see cref="IsEcho"/> property.
/// </summary>
/// <returns>A boolean with the value.</returns>
/// <returns>`true` to serialize the property; otherwise, `false`.</returns>
public bool ShouldSerializeIsEcho()
{
return IsEcho;
}
/// <summary>
/// Newtonsoft JSON method for conditionally serializing the <see cref="Attachments"/> property.
/// </summary>
/// <returns>`true` to serialize the property; otherwise, `false`.</returns>
public bool ShouldSerializeAttachments()
{
return Attachments.Count > 0;

Просмотреть файл

@ -29,9 +29,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Condition=" '$(IsBuildServer)' == '' " Version="4.8.0-local" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Condition=" '$(IsBuildServer)' != '' " Version="$(ReleasePackageVersion)" />
<PackageReference Include="SourceLink.Create.CommandLine" Version="2.8.3" >
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -31,9 +31,7 @@
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="SlackAPI" Version="1.1.2" />
<PackageReference Include="SourceLink.Create.CommandLine" Version="2.8.3" >
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Condition=" '$(IsBuildServer)' == '' " Version="4.8.0-local" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Condition=" '$(IsBuildServer)' != '' " Version="$(ReleasePackageVersion)" />
</ItemGroup>

Просмотреть файл

@ -36,9 +36,7 @@
<PackageReference Include="Twilio" Version="5.33.1" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Condition=" '$(IsBuildServer)' == '' " Version="4.8.0-local" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Condition=" '$(IsBuildServer)' != '' " Version="$(ReleasePackageVersion)" />
<PackageReference Include="SourceLink.Create.CommandLine" Version="2.8.3" >
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -81,7 +81,7 @@ namespace Microsoft.Bot.Builder.Adapters.Twilio
{
var messageOptions = TwilioHelper.ActivityToTwilio(activity, _twilioClient.Options.TwilioNumber);
var res = await _twilioClient.SendMessage(messageOptions).ConfigureAwait(false);
var res = await _twilioClient.SendMessage(messageOptions, cancellationToken).ConfigureAwait(false);
var response = new ResourceResponse() { Id = res, };

Просмотреть файл

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Twilio;
@ -51,8 +52,9 @@ namespace Microsoft.Bot.Builder.Adapters.Twilio
/// Sends a Twilio SMS message.
/// </summary>
/// <param name="messageOptions">An object containing the parameters for the message to send.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>The SID of the Twilio message sent.</returns>
public virtual async Task<string> SendMessage(CreateMessageOptions messageOptions)
public virtual async Task<string> SendMessage(CreateMessageOptions messageOptions, CancellationToken cancellationToken)
{
var messageResource = await MessageResource.CreateAsync(messageOptions).ConfigureAwait(false);
return messageResource.Sid;

Просмотреть файл

@ -32,9 +32,7 @@
<PackageReference Include="Thrzn41.WebexTeams" Version="1.6.2" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Condition=" '$(IsBuildServer)' == '' " Version="4.8.0-local" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Condition=" '$(IsBuildServer)' != '' " Version="$(ReleasePackageVersion)" />
<PackageReference Include="SourceLink.Create.CommandLine" Version="2.8.3" >
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -10,7 +10,7 @@
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DocumentationFile>bin\$(Configuration)\netstandard2.0\Microsoft.Bot.Expressions.xml</DocumentationFile>
<DocumentationFile>bin\$(Configuration)\netstandard2.0\AdaptiveExpressions.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@ -20,7 +20,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageId>Microsoft.Bot.Expressions</PackageId>
<PackageId>AdaptiveExpressions</PackageId>
<Description>This library implements Microsoft Bot Builder Expression evaluator for .NET</Description>
<Summary>This library implements Microsoft Bot Builder Expression evaluator for .NET</Summary>
</PropertyGroup>
@ -46,7 +46,7 @@
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
<PackageReference Include="Microsoft.Recognizers.Text.DataTypes.TimexExpression" Version="1.2.9" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="SourceLink.Create.CommandLine" Version="2.8.3" >
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" >
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше