Merge pull request #594 from microsoft/southworks/add/dotnet-bots-3.1

[#591] Update dotnet bots to target net 6 - Add .NETCore3.1 bots
This commit is contained in:
tracyboehrer 2022-08-22 15:42:28 -05:00 коммит произвёл GitHub
Родитель 188c000987 ba00c1bdf0
Коммит f061641249
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
26 изменённых файлов: 2154 добавлений и 0 удалений

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

@ -0,0 +1,49 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Schema;
namespace Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBot31.Bots
{
public class EchoBot : ActivityHandler
{
/// <summary>
/// Processes a message activity.
/// </summary>
/// <param name="turnContext">Context for the current turn of conversation.</param>
/// <param name="cancellationToken">CancellationToken propagates notifications that operations should be cancelled.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
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: {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);
}
}
/// <summary>
/// Processes an end of conversation activity.
/// </summary>
/// <param name="turnContext">Context for the current turn of conversation.</param>
/// <param name="cancellationToken">CancellationToken propagates notifications that operations should be cancelled.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
protected override Task OnEndOfConversationActivityAsync(ITurnContext<IEndOfConversationActivity> turnContext, CancellationToken cancellationToken)
{
// This will be called if the host bot is ending the conversation. Sending additional messages should be
// avoided as the conversation may have been deleted.
// Perform cleanup of resources if needed.
return Task.CompletedTask;
}
}
}

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

@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
namespace Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBot31.Controllers
{
/// <summary>
/// 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.
/// </summary>
[Route("api/messages")]
[ApiController]
public class BotController : ControllerBase
{
private readonly IBotFrameworkHttpAdapter _adapter;
private readonly IBot _bot;
/// <summary>
/// Initializes a new instance of the <see cref="BotController"/> class.
/// </summary>
/// <param name="adapter">Adapter for the BotController.</param>
/// <param name="bot">Bot for the BotController.</param>
public BotController(IBotFrameworkHttpAdapter adapter, IBot bot)
{
_adapter = adapter;
_bot = bot;
}
/// <summary>
/// Processes an HttpPost request.
/// </summary>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
[HttpPost]
public async Task PostAsync()
{
await _adapter.ProcessAsync(Request, Response, _bot);
}
}
}

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

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>latest</LangVersion>
<AssemblyName>Microsoft.BotFrameworkFunctionalTests.EchoSkillBot31</AssemblyName>
<RootNamespace>Microsoft.BotFrameworkFunctionalTests.EchoSkillBot31</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.2" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Version="$(BotBuilderVersion)" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

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

@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBot31
{
public class Program
{
/// <summary>
/// The entry point of the application.
/// </summary>
/// <param name="args">The command line args.</param>
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
/// <summary>
/// Creates a new instance of the <see cref="HostBuilder"/> class with pre-configured defaults.
/// </summary>
/// <param name="args">The command line args.</param>
/// <returns>The initialized <see cref="IHostBuilder"/>.</returns>
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:35405/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"EchoSkillBotDotNet": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "http://localhost:35405/",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

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

@ -0,0 +1,58 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Builder.TraceExtensions;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Logging;
namespace Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBot31
{
public class SkillAdapterWithErrorHandler : CloudAdapter
{
/// <summary>
/// Initializes a new instance of the <see cref="SkillAdapterWithErrorHandler"/> class to handle errors.
/// </summary>
/// <param name="botFrameworkAuthentication">The cloud environment for the bot.</param>
/// <param name="logger">An instance of a logger.</param>
public SkillAdapterWithErrorHandler(BotFrameworkAuthentication botFrameworkAuthentication, ILogger<CloudAdapter> logger)
: base(botFrameworkAuthentication, logger)
{
OnTurnError = async (turnContext, exception) =>
{
try
{
// 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 + Environment.NewLine + exception, errorMessageText, InputHints.IgnoringInput);
errorMessage.Value = exception;
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);
// 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");
// 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);
}
catch (Exception ex)
{
logger.LogError(ex, $"Exception caught in SkillAdapterWithErrorHandler : {ex}");
}
};
}
}
}

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

@ -0,0 +1,97 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBot31.Bots;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBot31
{
public class Startup
{
private const string CallersConfigKey = "AllowedCallers";
public Startup(IConfiguration config)
{
Configuration = config;
}
public IConfiguration Configuration { get; }
/// <summary>
/// This method gets called by the runtime. Use this method to add services to the container.
/// </summary>
/// <param name="services">Method to add services to the container.</param>
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers().AddNewtonsoftJson();
// Register AuthConfiguration to enable custom claim validation.
services.AddSingleton(sp =>
{
var allowedCallers = new List<string>(sp.GetService<IConfiguration>().GetSection(CallersConfigKey).Get<string[]>());
var claimsValidator = new AllowedCallersClaimsValidator(allowedCallers);
// If TenantId is specified in config, add the tenant as a valid JWT token issuer for Bot to Skill conversation.
// The token issuer for MSI and single tenant scenarios will be the tenant where the bot is registered.
var validTokenIssuers = new List<string>();
var tenantId = sp.GetService<IConfiguration>().GetSection(MicrosoftAppCredentials.MicrosoftAppTenantIdKey)?.Value;
if (!string.IsNullOrWhiteSpace(tenantId))
{
// For SingleTenant/MSI auth, the JWT tokens will be issued from the bot's home tenant.
// Therefore, these issuers need to be added to the list of valid token issuers for authenticating activity requests.
validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidTokenIssuerUrlTemplateV1, tenantId));
validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidTokenIssuerUrlTemplateV2, tenantId));
validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidGovernmentTokenIssuerUrlTemplateV1, tenantId));
validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidGovernmentTokenIssuerUrlTemplateV2, tenantId));
}
return new AuthenticationConfiguration
{
ClaimsValidator = claimsValidator,
ValidTokenIssuers = validTokenIssuers
};
});
services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>();
// 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>();
}
/// <summary>
/// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
/// </summary>
/// <param name="app">The application request pipeline to be configured.</param>
/// <param name="env">The web hosting environment.</param>
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseDefaultFiles()
.UseStaticFiles()
.UseRouting()
.UseAuthorization()
.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

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

@ -0,0 +1,13 @@
{
"MicrosoftAppType": "",
"MicrosoftAppId": "",
"MicrosoftAppPassword": "",
"MicrosoftAppTenantId": "",
"ChannelService": "",
// This is a comma separate list with the App IDs that will have access to the skill.
// This setting is used in AllowedCallersClaimsValidator.
// Examples:
// [ "*" ] allows all callers.
// [ "AppId1", "AppId2" ] only allows access to parent bots with "AppId1" and "AppId2".
"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>EchoSkillBotDotNet</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">EchoSkillBotDotNet 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:35405/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,23 @@
{
"$schema": "https://schemas.botframework.com/schemas/skills/v2.1/skill-manifest.json",
"$id": "EchoSkillBotDotNet31",
"name": "EchoSkillBotDotNet31",
"version": "1.0",
"description": "This is a skill for echoing what the user sent to the bot (using .netcore 3.1).",
"publisherName": "Microsoft",
"privacyUrl": "https://microsoft.com/privacy",
"copyright": "Copyright (c) Microsoft Corporation. All rights reserved.",
"license": "https://github.com/microsoft/BotFramework-FunctionalTests/blob/main/LICENSE",
"tags": [
"echo"
],
"endpoints": [
{
"name": "default",
"protocol": "BotFrameworkV3",
"description": "Localhost endpoint for the skill (on port 35405)",
"endpointUrl": "http://localhost:35405/api/messages",
"msAppId": "00000000-0000-0000-0000-000000000000"
}
]
}

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

@ -32,6 +32,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CustomActions", "CustomActi
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DownloadFileDialog", "Composer\CustomActions\DownloadFileDialog\DownloadFileDialog.csproj", "{C3796ECA-E952-47EB-B54E-CE0A0B8A7F6C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot31", "SimpleHostBot-3.1\Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot31.csproj", "{C9F302F6-A7B8-4AF6-9394-E4951D8ED53B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBot31", "EchoSkillBot-3.1\Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBot31.csproj", "{4241B947-F2E0-469D-8FCE-DF3A0C0D48EB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -78,6 +82,14 @@ Global
{C3796ECA-E952-47EB-B54E-CE0A0B8A7F6C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C3796ECA-E952-47EB-B54E-CE0A0B8A7F6C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C3796ECA-E952-47EB-B54E-CE0A0B8A7F6C}.Release|Any CPU.Build.0 = Release|Any CPU
{C9F302F6-A7B8-4AF6-9394-E4951D8ED53B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C9F302F6-A7B8-4AF6-9394-E4951D8ED53B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9F302F6-A7B8-4AF6-9394-E4951D8ED53B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C9F302F6-A7B8-4AF6-9394-E4951D8ED53B}.Release|Any CPU.Build.0 = Release|Any CPU
{4241B947-F2E0-469D-8FCE-DF3A0C0D48EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4241B947-F2E0-469D-8FCE-DF3A0C0D48EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4241B947-F2E0-469D-8FCE-DF3A0C0D48EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4241B947-F2E0-469D-8FCE-DF3A0C0D48EB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

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

@ -0,0 +1,155 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot31.Bots;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Builder.Skills;
using Microsoft.Bot.Builder.TraceExtensions;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot31
{
public class AdapterWithErrorHandler : CloudAdapter
{
private readonly BotFrameworkAuthentication _auth;
private readonly ConversationState _conversationState;
private readonly IConfiguration _configuration;
private readonly ILogger _logger;
private readonly SkillsConfiguration _skillsConfig;
/// <summary>
/// Initializes a new instance of the <see cref="AdapterWithErrorHandler"/> class to handle errors.
/// </summary>
/// <param name="auth">The cloud environment for the bot.</param>
/// <param name="configuration">The configuration properties.</param>
/// <param name="logger">An instance of a logger.</param>
/// <param name="conversationState">A state management object for the conversation.</param>
/// <param name="skillsConfig">The skills configuration.</param>
public AdapterWithErrorHandler(BotFrameworkAuthentication auth, IConfiguration configuration, ILogger<CloudAdapter> logger, ConversationState conversationState = null, SkillsConfiguration skillsConfig = null)
: base(auth, logger)
{
_auth = auth ?? throw new ArgumentNullException(nameof(auth));
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
_conversationState = conversationState;
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_skillsConfig = skillsConfig;
OnTurnError = HandleTurnErrorAsync;
}
/// <summary>
/// Handles the error by sending a message to the user and ending the conversation with the skill.
/// </summary>
/// <param name="turnContext">Context for the current turn of conversation.</param>
/// <param name="exception">The handled exception.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
private async Task HandleTurnErrorAsync(ITurnContext turnContext, Exception exception)
{
// Log any leaked exception from the application.
_logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}");
await SendErrorMessageAsync(turnContext, exception, default);
await EndSkillConversationAsync(turnContext, default);
await ClearConversationStateAsync(turnContext, default);
}
/// <summary>
/// Sends an error message to the user and a trace activity to be displayed in the Bot Framework Emulator.
/// </summary>
/// <param name="turnContext">Context for the current turn of conversation.</param>
/// <param name="exception">The exception to be sent in the message.</param>
/// <param name="cancellationToken">CancellationToken propagates notifications that operations should be cancelled.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
private async Task SendErrorMessageAsync(ITurnContext turnContext, Exception exception, CancellationToken cancellationToken)
{
try
{
// Send a message to the user
var errorMessageText = "The bot encountered an error or bug.";
var errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.IgnoringInput);
errorMessage.Value = exception;
await turnContext.SendActivityAsync(errorMessage, cancellationToken);
await turnContext.SendActivityAsync($"Exception: {exception.Message}");
await turnContext.SendActivityAsync(exception.ToString());
errorMessageText = "To continue to run this bot, please fix the bot source code.";
errorMessage = MessageFactory.Text(errorMessageText, errorMessageText, InputHints.ExpectingInput);
await turnContext.SendActivityAsync(errorMessage, cancellationToken);
// 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", cancellationToken);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Exception caught in SendErrorMessageAsync : {ex}");
}
}
/// <summary>
/// Informs to the active skill that the conversation is ended so that it has a chance to clean up.
/// </summary>
/// <param name="turnContext">Context for the current turn of conversation.</param>
/// <param name="cancellationToken">CancellationToken propagates notifications that operations should be cancelled.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
private async Task EndSkillConversationAsync(ITurnContext turnContext, CancellationToken cancellationToken)
{
if (_conversationState == null || _skillsConfig == null)
{
return;
}
try
{
// Note: ActiveSkillPropertyName is set by the HostBot while messages are being
// forwarded to a Skill.
var activeSkill = await _conversationState.CreateProperty<BotFrameworkSkill>(HostBot.ActiveSkillPropertyName).GetAsync(turnContext, () => null, cancellationToken);
if (activeSkill != null)
{
var botId = _configuration.GetSection(MicrosoftAppCredentials.MicrosoftAppIdKey)?.Value;
var endOfConversation = Activity.CreateEndOfConversationActivity();
endOfConversation.Code = "HostSkillError";
endOfConversation.ApplyConversationReference(turnContext.Activity.GetConversationReference(), true);
await _conversationState.SaveChangesAsync(turnContext, true, cancellationToken);
using var client = _auth.CreateBotFrameworkClient();
await client.PostActivityAsync(botId, activeSkill.AppId, activeSkill.SkillEndpoint, _skillsConfig.SkillHostEndpoint, endOfConversation.Conversation.Id, (Activity)endOfConversation, cancellationToken);
}
}
catch (Exception ex)
{
_logger.LogError(ex, $"Exception caught on attempting to send EndOfConversation : {ex}");
}
}
/// <summary>
/// Deletes the conversationState for the current conversation to prevent the bot from getting stuck in an error-loop caused by being in a bad state.
/// ConversationState should be thought of as similar to "cookie-state" in a Web pages.
/// </summary>
/// <param name="turnContext">Context for the current turn of conversation.</param>
/// <param name="cancellationToken">CancellationToken propagates notifications that operations should be cancelled.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
private async Task ClearConversationStateAsync(ITurnContext turnContext, CancellationToken cancellationToken)
{
if (_conversationState != null)
{
try
{
await _conversationState.DeleteAsync(turnContext, cancellationToken);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Exception caught on attempting to Delete ConversationState : {ex}");
}
}
}
}
}

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

@ -0,0 +1,259 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot31.Dialogs;
using Microsoft.Bot.Builder.Skills;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot31.Bots
{
public class HostBot : ActivityHandler
{
public const string DeliveryModePropertyName = "deliveryModeProperty";
public const string ActiveSkillPropertyName = "activeSkillProperty";
private readonly IStatePropertyAccessor<string> _deliveryModeProperty;
private readonly IStatePropertyAccessor<BotFrameworkSkill> _activeSkillProperty;
private readonly IStatePropertyAccessor<DialogState> _dialogStateProperty;
private readonly string _botId;
private readonly BotFrameworkAuthentication _auth;
private readonly ConversationState _conversationState;
private readonly SkillsConfiguration _skillsConfig;
private readonly SkillConversationIdFactoryBase _conversationIdFactory;
private readonly Dialog _dialog;
/// <summary>
/// Initializes a new instance of the <see cref="HostBot"/> class.
/// </summary>
/// <param name="auth">The cloud environment for the bot.</param>
/// <param name="conversationState">A state management object for the conversation.</param>
/// <param name="skillsConfig">The skills configuration.</param>
/// <param name="configuration">The configuration properties.</param>
/// <param name="conversationIdFactory">The conversation id factory.</param>
/// <param name="dialog">The dialog to use.</param>
public HostBot(BotFrameworkAuthentication auth, ConversationState conversationState, SkillsConfiguration skillsConfig, SkillConversationIdFactoryBase conversationIdFactory, IConfiguration configuration, SetupDialog dialog)
{
_auth = auth ?? throw new ArgumentNullException(nameof(auth));
_conversationState = conversationState ?? throw new ArgumentNullException(nameof(conversationState));
_skillsConfig = skillsConfig ?? throw new ArgumentNullException(nameof(skillsConfig));
_conversationIdFactory = conversationIdFactory ?? throw new ArgumentNullException(nameof(conversationIdFactory));
_dialog = dialog ?? throw new ArgumentNullException(nameof(dialog));
_dialogStateProperty = _conversationState.CreateProperty<DialogState>("DialogState");
if (configuration == null)
{
throw new ArgumentNullException(nameof(configuration));
}
_botId = configuration.GetSection(MicrosoftAppCredentials.MicrosoftAppIdKey)?.Value;
// Create state properties to track the delivery mode and active skill.
_deliveryModeProperty = conversationState.CreateProperty<string>(DeliveryModePropertyName);
_activeSkillProperty = conversationState.CreateProperty<BotFrameworkSkill>(ActiveSkillPropertyName);
}
/// <inheritdoc/>
public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default)
{
// Forward all activities except EndOfConversation to the active skill.
if (turnContext.Activity.Type != ActivityTypes.EndOfConversation)
{
// Try to get the active skill
var activeSkill = await _activeSkillProperty.GetAsync(turnContext, () => null, cancellationToken);
if (activeSkill != null)
{
var deliveryMode = await _deliveryModeProperty.GetAsync(turnContext, () => null, cancellationToken);
// Send the activity to the skill
await SendToSkillAsync(turnContext, deliveryMode, activeSkill, cancellationToken);
return;
}
}
await base.OnTurnAsync(turnContext, cancellationToken);
// Save any state changes that might have occurred during the turn.
await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken);
}
/// <summary>
/// Processes a message activity.
/// </summary>
/// <param name="turnContext">Context for the current turn of conversation.</param>
/// <param name="cancellationToken">CancellationToken propagates notifications that operations should be cancelled.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
if (_skillsConfig.Skills.ContainsKey(turnContext.Activity.Text))
{
var deliveryMode = await _deliveryModeProperty.GetAsync(turnContext, () => null, cancellationToken);
var selectedSkill = _skillsConfig.Skills[turnContext.Activity.Text];
var v3Bots = new List<string> { "EchoSkillBotDotNetV3", "EchoSkillBotJSV3" };
if (selectedSkill != null && deliveryMode == DeliveryModes.ExpectReplies && v3Bots.Contains(selectedSkill.Id))
{
var message = MessageFactory.Text("V3 Bots do not support 'expectReplies' delivery mode.");
await turnContext.SendActivityAsync(message, cancellationToken);
// Forget delivery mode and skill invocation.
await _deliveryModeProperty.DeleteAsync(turnContext, cancellationToken);
await _activeSkillProperty.DeleteAsync(turnContext, cancellationToken);
// Restart setup dialog
await _conversationState.DeleteAsync(turnContext, cancellationToken);
}
}
await _dialog.RunAsync(turnContext, _dialogStateProperty, cancellationToken);
}
/// <summary>
/// Processes an end of conversation activity.
/// </summary>
/// <param name="turnContext">Context for the current turn of conversation.</param>
/// <param name="cancellationToken">CancellationToken propagates notifications that operations should be cancelled.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
protected override async Task OnEndOfConversationActivityAsync(ITurnContext<IEndOfConversationActivity> turnContext, CancellationToken cancellationToken)
{
await EndConversation((Activity)turnContext.Activity, turnContext, cancellationToken);
}
/// <summary>
/// Processes a member added event.
/// </summary>
/// <param name="membersAdded">The list of members added to the conversation.</param>
/// <param name="turnContext">Context for the current turn of conversation.</param>
/// <param name="cancellationToken">CancellationToken propagates notifications that operations should be cancelled.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
foreach (var member in membersAdded)
{
if (member.Id != turnContext.Activity.Recipient.Id)
{
await turnContext.SendActivityAsync(MessageFactory.Text("Hello and welcome!"), cancellationToken);
await _dialog.RunAsync(turnContext, _dialogStateProperty, cancellationToken);
}
}
}
/// <summary>
/// Clears storage variables and sends the end of conversation activities.
/// </summary>
/// <param name="activity">End of conversation activity.</param>
/// <param name="turnContext">Context for the current turn of conversation.</param>
/// <param name="cancellationToken">CancellationToken propagates notifications that operations should be cancelled.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
private async Task EndConversation(Activity activity, ITurnContext turnContext, CancellationToken cancellationToken)
{
// Forget delivery mode and skill invocation.
await _deliveryModeProperty.DeleteAsync(turnContext, cancellationToken);
await _activeSkillProperty.DeleteAsync(turnContext, cancellationToken);
// Show status message, text and value returned by the skill
var eocActivityMessage = $"Received {ActivityTypes.EndOfConversation}.\n\nCode: {activity.Code}.";
if (!string.IsNullOrWhiteSpace(activity.Text))
{
eocActivityMessage += $"\n\nText: {activity.Text}";
}
if (activity.Value != null)
{
eocActivityMessage += $"\n\nValue: {JsonConvert.SerializeObject(activity.Value)}";
}
await turnContext.SendActivityAsync(MessageFactory.Text(eocActivityMessage), cancellationToken);
// We are back at the host.
await turnContext.SendActivityAsync(MessageFactory.Text("Back in the host bot."), cancellationToken);
// Restart setup dialog.
await _dialog.RunAsync(turnContext, _dialogStateProperty, cancellationToken);
await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken);
}
/// <summary>
/// Sends an activity to the skill bot.
/// </summary>
/// <param name="turnContext">Context for the current turn of conversation.</param>
/// <param name="deliveryMode">The delivery mode to use when communicating to the skill.</param>
/// <param name="targetSkill">The skill that will receive the activity.</param>
/// <param name="cancellationToken">CancellationToken propagates notifications that operations should be cancelled.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
private async Task SendToSkillAsync(ITurnContext turnContext, string deliveryMode, BotFrameworkSkill targetSkill, CancellationToken cancellationToken)
{
// NOTE: Always SaveChanges() before calling a skill so that any activity generated by the skill
// will have access to current accurate state.
await _conversationState.SaveChangesAsync(turnContext, force: true, cancellationToken: cancellationToken);
// Route the activity to the skill.
using var client = _auth.CreateBotFrameworkClient();
// Create a conversationId to interact with the skill and send the activity
var options = new SkillConversationIdFactoryOptions
{
FromBotOAuthScope = turnContext.TurnState.Get<string>(BotAdapter.OAuthScopeKey),
FromBotId = _botId,
Activity = turnContext.Activity,
BotFrameworkSkill = targetSkill
};
var skillConversationId = await _conversationIdFactory.CreateSkillConversationIdAsync(options, cancellationToken);
if (deliveryMode == DeliveryModes.ExpectReplies)
{
// Clone activity and update its delivery mode.
var activity = JsonConvert.DeserializeObject<Activity>(JsonConvert.SerializeObject(turnContext.Activity));
activity.DeliveryMode = deliveryMode;
// route the activity to the skill
var expectRepliesResponse = await client.PostActivityAsync(_botId, targetSkill.AppId, targetSkill.SkillEndpoint, _skillsConfig.SkillHostEndpoint, skillConversationId, activity, cancellationToken);
// Check response status.
if (!expectRepliesResponse.IsSuccessStatusCode())
{
throw new HttpRequestException($"Error invoking the skill id: \"{targetSkill.Id}\" at \"{targetSkill.SkillEndpoint}\" (status is {expectRepliesResponse.Status}). \r\n {expectRepliesResponse.Body}");
}
// Route response activities back to the channel.
var response = expectRepliesResponse.Body as JObject;
var activities = response["activities"];
var responseActivities = activities.ToObject<IList<Activity>>();
foreach (var responseActivity in responseActivities)
{
if (responseActivity.Type == ActivityTypes.EndOfConversation)
{
await EndConversation(responseActivity, turnContext, cancellationToken);
}
else
{
await turnContext.SendActivityAsync(responseActivity, cancellationToken);
}
}
}
else
{
// Route the activity to the skill.
var response = await client.PostActivityAsync(_botId, targetSkill.AppId, targetSkill.SkillEndpoint, _skillsConfig.SkillHostEndpoint, skillConversationId, turnContext.Activity, cancellationToken);
// Check response status
if (!response.IsSuccessStatusCode())
{
throw new HttpRequestException($"Error invoking the skill id: \"{targetSkill.Id}\" at \"{targetSkill.SkillEndpoint}\" (status is {response.Status}). \r\n {response.Body}");
}
}
}
}
}

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

@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
namespace Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot31.Controllers
{
/// <summary>
/// This ASP Controller is created to handle a request. Dependency Injection will provide the Adapter and IBot implementation at runtime.
/// </summary>
[Route("api/messages")]
[ApiController]
public class BotController : ControllerBase
{
private readonly IBotFrameworkHttpAdapter _adapter;
private readonly IBot _bot;
/// <summary>
/// Initializes a new instance of the <see cref="BotController"/> class.
/// </summary>
/// <param name="adapter">Adapter for the BotController.</param>
/// <param name="bot">Bot for the BotController.</param>
public BotController(IBotFrameworkHttpAdapter adapter, IBot bot)
{
_adapter = adapter;
_bot = bot;
}
/// <summary>
/// Processes an HttpPost request.
/// </summary>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
[HttpPost]
public async Task PostAsync()
{
await _adapter.ProcessAsync(Request, Response, _bot);
}
}
}

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

@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Builder.Skills;
namespace Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot31.Controllers
{
/// <summary>
/// A controller that handles skill replies to the bot.
/// This example uses the <see cref="CloudSkillHandler"/> that is registered as a <see cref="ChannelServiceHandlerBase"/> in startup.cs.
/// </summary>
[ApiController]
[Route("api/skills")]
public class SkillController : ChannelServiceController
{
/// <summary>
/// Initializes a new instance of the <see cref="SkillController"/> class.
/// </summary>
/// <param name="handler">The skill handler registered as ChannelServiceHandlerBase.</param>
public SkillController(ChannelServiceHandlerBase handler)
: base(handler)
{
}
}
}

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

@ -0,0 +1,118 @@
// 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.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Choices;
using Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot31.Bots;
using Microsoft.Bot.Builder.Skills;
using Microsoft.Bot.Schema;
namespace Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot31.Dialogs
{
/// <summary>
/// The setup dialog for this bot.
/// </summary>
public class SetupDialog : ComponentDialog
{
private readonly IStatePropertyAccessor<string> _deliveryModeProperty;
private readonly IStatePropertyAccessor<BotFrameworkSkill> _activeSkillProperty;
private readonly SkillsConfiguration _skillsConfig;
private string _deliveryMode;
public SetupDialog(ConversationState conversationState, SkillsConfiguration skillsConfig)
: base(nameof(SetupDialog))
{
_skillsConfig = skillsConfig ?? throw new ArgumentNullException(nameof(skillsConfig));
_deliveryModeProperty = conversationState.CreateProperty<string>(HostBot.DeliveryModePropertyName);
_activeSkillProperty = conversationState.CreateProperty<BotFrameworkSkill>(HostBot.ActiveSkillPropertyName);
// Define the setup dialog and its related components.
// Add ChoicePrompt to render available skills.
AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
// Add main waterfall dialog for this bot.
var waterfallSteps = new WaterfallStep[]
{
SelectDeliveryModeStepAsync,
SelectSkillStepAsync,
FinalStepAsync
};
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), waterfallSteps));
InitialDialogId = nameof(WaterfallDialog);
}
// Render a prompt to select the delivery mode to use.
private async Task<DialogTurnResult> SelectDeliveryModeStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
// Create the PromptOptions with the delivery modes supported.
const string messageText = "What delivery mode would you like to use?";
const string repromptMessageText = "That was not a valid choice, please select a valid delivery mode.";
var choices = new List<Choice>();
choices.Add(new Choice(DeliveryModes.Normal));
choices.Add(new Choice(DeliveryModes.ExpectReplies));
var options = new PromptOptions
{
Prompt = MessageFactory.Text(messageText, messageText, InputHints.ExpectingInput),
RetryPrompt = MessageFactory.Text(repromptMessageText, repromptMessageText, InputHints.ExpectingInput),
Choices = choices
};
// Prompt the user to select a delivery mode.
return await stepContext.PromptAsync(nameof(ChoicePrompt), options, cancellationToken);
}
// Render a prompt to select the skill to call.
private async Task<DialogTurnResult> SelectSkillStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
// Set delivery mode.
_deliveryMode = ((FoundChoice)stepContext.Result).Value;
await _deliveryModeProperty.SetAsync(stepContext.Context, ((FoundChoice)stepContext.Result).Value, cancellationToken);
// Create the PromptOptions from the skill configuration which contains the list of configured skills.
const string messageText = "What skill would you like to call?";
const string repromptMessageText = "That was not a valid choice, please select a valid skill.";
var options = new PromptOptions
{
Prompt = MessageFactory.Text(messageText, messageText, InputHints.ExpectingInput),
RetryPrompt = MessageFactory.Text(repromptMessageText, repromptMessageText, InputHints.ExpectingInput),
Choices = _skillsConfig.Skills.Select(skill => new Choice(skill.Key)).ToList(),
Style = ListStyle.SuggestedAction
};
// Prompt the user to select a skill.
return await stepContext.PromptAsync(nameof(ChoicePrompt), options, cancellationToken);
}
// The SetupDialog has ended, we go back to the HostBot to connect with the selected skill.
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var selectedSkillKey = ((FoundChoice)stepContext.Result).Value;
var selectedSkill = _skillsConfig.Skills.FirstOrDefault(skill => skill.Key == selectedSkillKey);
var v3Bots = new List<string> { "EchoSkillBotDotNetV3", "EchoSkillBotJSV3" };
if (_deliveryMode == DeliveryModes.ExpectReplies && v3Bots.Contains(selectedSkillKey))
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("V3 Bots do not support 'expectReplies' delivery mode."), cancellationToken);
// Restart setup dialog
return await stepContext.ReplaceDialogAsync(InitialDialogId, null, cancellationToken);
}
// Set active skill
await _activeSkillProperty.SetAsync(stepContext.Context, selectedSkill.Value, cancellationToken);
var message = MessageFactory.Text("Type anything to send to the skill.", "Type anything to send to the skill.", InputHints.ExpectingInput);
await stepContext.Context.SendActivityAsync(message, cancellationToken);
return await stepContext.EndDialogAsync(stepContext.Values, cancellationToken);
}
}
}

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

@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>latest</LangVersion>
<AssemblyName>Microsoft.BotFrameworkFunctionalTests.SimpleHostBot31</AssemblyName>
<RootNamespace>Microsoft.BotFrameworkFunctionalTests.SimpleHostBot31</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.2" />
<PackageReference Include="Microsoft.Bot.Builder.Dialogs" Version="$(BotBuilderVersion)" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Version="$(BotBuilderVersion)" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.Development.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

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

@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot31
{
public class Program
{
/// <summary>
/// The entry point of the application.
/// </summary>
/// <param name="args">The command line args.</param>
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
/// <summary>
/// Creates a new instance of the <see cref="HostBuilder"/> class with pre-configured defaults.
/// </summary>
/// <param name="args">The command line args.</param>
/// <returns>The initialized <see cref="IHostBuilder"/>.</returns>
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:35005",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"SimpleHostBotDotNet": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "http://localhost:35005",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

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

@ -0,0 +1,55 @@
// 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.Bot.Builder.FunctionalTestsBots.SimpleHostBot31
{
/// <summary>
/// A helper class that loads Skills information from configuration.
/// </summary>
public class SkillsConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="SkillsConfiguration"/> class to load skills information from configuration.
/// </summary>
/// <param name="configuration">The configuration properties.</param>
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);
}
}
/// <summary>
/// Gets the URI representing the endpoint of the host bot.
/// </summary>
/// <value>
/// The URI representing the endpoint of the host bot.
/// </value>
public Uri SkillHostEndpoint { get; }
/// <summary>
/// Gets the key-value pairs with the skills bots.
/// </summary>
/// <value>
/// The key-value pairs with the skills bots.
/// </value>
public Dictionary<string, BotFrameworkSkill> Skills { get; } = new Dictionary<string, BotFrameworkSkill>(StringComparer.OrdinalIgnoreCase);
}
}

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

@ -0,0 +1,123 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Bot.Builder.BotFramework;
using Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot31.Bots;
using Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot31.Dialogs;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Builder.Skills;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot31
{
public class Startup
{
public Startup(IConfiguration config)
{
Configuration = config;
}
public IConfiguration Configuration { get; }
/// <summary>
/// This method gets called by the runtime. Use this method to add services to the container.
/// </summary>
/// <param name="services">The collection of services to add to the container.</param>
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers().AddNewtonsoftJson();
// Register the skills configuration class
services.AddSingleton<SkillsConfiguration>();
// Register AuthConfiguration to enable custom claim validation.
services.AddSingleton(sp =>
{
var allowedSkills = sp.GetService<SkillsConfiguration>().Skills.Values.Select(s => s.AppId).ToList();
var claimsValidator = new AllowedSkillsClaimsValidator(allowedSkills);
// If TenantId is specified in config, add the tenant as a valid JWT token issuer for Bot to Skill conversation.
// The token issuer for MSI and single tenant scenarios will be the tenant where the bot is registered.
var validTokenIssuers = new List<string>();
var tenantId = sp.GetService<IConfiguration>().GetSection(MicrosoftAppCredentials.MicrosoftAppTenantIdKey)?.Value;
if (!string.IsNullOrWhiteSpace(tenantId))
{
// For SingleTenant/MSI auth, the JWT tokens will be issued from the bot's home tenant.
// Therefore, these issuers need to be added to the list of valid token issuers for authenticating activity requests.
validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidTokenIssuerUrlTemplateV1, tenantId));
validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidTokenIssuerUrlTemplateV2, tenantId));
validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidGovernmentTokenIssuerUrlTemplateV1, tenantId));
validTokenIssuers.Add(string.Format(CultureInfo.InvariantCulture, AuthenticationConstants.ValidGovernmentTokenIssuerUrlTemplateV2, tenantId));
}
return new AuthenticationConfiguration
{
ClaimsValidator = claimsValidator,
ValidTokenIssuers = validTokenIssuers
};
});
services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>();
// 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<CloudAdapter, AdapterWithErrorHandler>();
services.AddSingleton<IBotFrameworkHttpAdapter>(sp => sp.GetService<CloudAdapter>());
services.AddSingleton<BotAdapter>(sp => sp.GetService<CloudAdapter>());
// Register the skills conversation ID factory, the client and the request handler.
services.AddSingleton<SkillConversationIdFactoryBase, SkillConversationIdFactory>();
services.AddSingleton<ChannelServiceHandlerBase, CloudSkillHandler>();
// 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>();
// Create SetupDialog
services.AddSingleton<SetupDialog>();
// Register the bot as a transient. In this case the ASP Controller is expecting an IBot.
services.AddTransient<IBot, HostBot>();
if (!string.IsNullOrEmpty(Configuration["ChannelService"]))
{
// Register a ConfigurationChannelProvider -- this is only for Azure Gov.
services.AddSingleton<IChannelProvider, ConfigurationChannelProvider>();
}
}
/// <summary>
/// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
/// </summary>
/// <param name="app">The application request pipeline to be configured.</param>
/// <param name="env">The web hosting environment.</param>
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseDefaultFiles()
.UseStaticFiles()
.UseRouting()
.UseAuthorization()
.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

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

@ -0,0 +1,45 @@
{
"MicrosoftAppType": "",
"MicrosoftAppId": "",
"MicrosoftAppPassword": "",
"MicrosoftAppTenantId": "",
"ChannelService": "",
"SkillHostEndpoint": "http://localhost:35005/api/skills/",
"BotFrameworkSkills": [
{
"Id": "EchoSkillBotComposerDotNet",
"AppId": "",
"SkillEndpoint": "http://localhost:35410/api/messages"
},
{
"Id": "EchoSkillBotDotNet",
"AppId": "",
"SkillEndpoint": "http://localhost:35400/api/messages"
},
{
"Id": "EchoSkillBotDotNet31",
"AppId": "",
"SkillEndpoint": "http://localhost:35405/api/messages"
},
{
"Id": "EchoSkillBotDotNetV3",
"AppId": "",
"SkillEndpoint": "http://localhost:35407/api/messages"
},
{
"Id": "EchoSkillBotJS",
"AppId": "",
"SkillEndpoint": "http://localhost:36400/api/messages"
},
{
"Id": "EchoSkillBotJSV3",
"AppId": "",
"SkillEndpoint": "http://localhost:36407/api/messages"
},
{
"Id": "EchoSkillBotPython",
"AppId": "",
"SkillEndpoint": "http://localhost:37400/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>SimpleHostBotDotNet</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: 40px;
}
.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">SimpleRootBotDotNet 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:35005/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>

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

@ -10,6 +10,9 @@ skill_EchoSkillBotComposerDotNet_endpoint=http://localhost:35410/api/messages
skill_EchoSkillBotDotNet_appId=
skill_EchoSkillBotDotNet_endpoint=http://localhost:35400/api/messages
skill_EchoSkillBotDotNet31_appId=
skill_EchoSkillBotDotNet31_endpoint=http://localhost:35405/api/messages
skill_EchoSkillBotDotNetV3_appId=
skill_EchoSkillBotDotNetV3_endpoint=http://localhost:35407/api/messages

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

@ -8,6 +8,9 @@ skill_EchoSkillBotComposerDotNet_endpoint=http://localhost:35410/api/messages
skill_EchoSkillBotDotNet_appId=
skill_EchoSkillBotDotNet_endpoint=http://localhost:35400/api/messages
skill_EchoSkillBotDotNet31_appId=
skill_EchoSkillBotDotNet31_endpoint=http://localhost:35405/api/messages
skill_EchoSkillBotDotNetV3_appId=
skill_EchoSkillBotDotNetV3_endpoint=http://localhost:35407/api/messages

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

@ -0,0 +1,17 @@
{
"name": "SimpleHostBotDotNet31",
"description": "",
"services": [
{
"type": "endpoint",
"appId": "",
"appPassword": "",
"endpoint": "http://localhost:35005/api/messages",
"id": "eefb3b80-6250-11eb-9298-2763eb2fc29e",
"name": "http://localhost:35005/api/messages"
}
],
"padlock": "",
"version": "2.0",
"overrides": null
}