Merge branch 'main' into southworks/add/deploy-auth-type-bots

This commit is contained in:
sw-joelmut 2022-04-22 12:30:51 -03:00
Родитель 2f6d0edf2a 7b2f8537d6
Коммит 162a661268
206 изменённых файлов: 21816 добавлений и 1770 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -374,4 +374,3 @@ Bots/JavaScript/.yarn/*
!Bots/JavaScript/.yarn/plugins
!Bots/JavaScript/.yarn/sdks
!Bots/JavaScript/.yarn/versions
Bots/JavaScript/.pnp.*

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

@ -1,15 +1,14 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Runtime.Settings;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace ComposerSkillBotDotNet.Controllers
namespace Microsoft.Bot.Builder.FunctionalTestsBots.ComposerSkillBotDotNet.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

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

@ -1,12 +1,11 @@
using System;
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Logging;
namespace ComposerSkillBotDotNet.Controllers
namespace Microsoft.Bot.Builder.FunctionalTestsBots.ComposerSkillBotDotNet.Controllers
{
/// <summary>
/// A controller that handles skill replies to the bot.

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

@ -1,10 +1,10 @@
using System;
using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Runtime.Extensions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
namespace ComposerSkillBotDotNet
namespace Microsoft.Bot.Builder.FunctionalTestsBots.ComposerSkillBotDotNet
{
public class Program
{
@ -30,4 +30,4 @@ namespace ComposerSkillBotDotNet
webBuilder.UseStartup<Startup>();
});
}
}
}

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

@ -1,15 +1,13 @@
using System.Collections.Concurrent;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Runtime.Extensions;
using Microsoft.Bot.Builder.LanguageGeneration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace ComposerSkillBotDotNet
namespace Microsoft.Bot.Builder.FunctionalTestsBots.ComposerSkillBotDotNet
{
public class Startup
{

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

@ -120,6 +120,33 @@
}
}
]
},
{
"$kind": "Microsoft.OnEventActivity",
"$designer": {
"id": "7kJtr3",
"name": "Event received (Event activity)"
},
"actions": [
{
"$kind": "Microsoft.IfCondition",
"$designer": {
"id": "FpQfAQ"
},
"actions": [
{
"$kind": "Microsoft.SetProperty",
"$designer": {
"id": "ulNzSl"
},
"property": "dialog.token",
"value": "turn.activity.value.token"
}
],
"condition": "=turn.activity.name==tokens/response"
}
],
"condition": "=not(empty(turn.activity.value))"
}
],
"generator": "AuthDialog.lg",

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

@ -3,6 +3,7 @@
# SendActivity_kNHtMW()
[Activity
Text = ${SendActivity_kNHtMW_text()}
InputHint = ignoringInput
]
# SendActivity_kNHtMW_text()
@ -10,6 +11,7 @@
# ConfirmInput_Prompt_WxLXxu()
[Activity
Text = ${ConfirmInput_Prompt_WxLXxu_text()}
InputHint = acceptingInput
]
# ConfirmInput_Prompt_WxLXxu_text()
@ -24,6 +26,7 @@
# SendActivity_oj6Ert()
[Activity
Text = ${SendActivity_oj6Ert_text()}
InputHint = ignoringInput
]
# SendActivity_oj6Ert_text()
@ -31,6 +34,7 @@
# ConfirmInput_Prompt_pBqV4a()
[Activity
Text = ${ConfirmInput_Prompt_pBqV4a_text()}
InputHint = acceptingInput
]
# ConfirmInput_Prompt_pBqV4a_text()
@ -38,6 +42,8 @@
# SendActivity_ooIaKS()
[Activity
Text = ${SendActivity_ooIaKS_text()}
Speak = Here is your token:
InputHint = ignoringInput
]
# SendActivity_ooIaKS_text()

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

@ -1,107 +1,108 @@
[import](common.lg)
> !# @strict = false
> !# @namespace = cards
> !# @exports = UnsupportedChannelCards
#UnsupportedChannelCards(channel)
- IF:${channel == 'emulator'}
- AdaptiveCardTeamsTaskModule, AdaptiveUpdate, O365, TeamsFileConsent
- ELSEIF:${channel == 'directline'}
- AdaptiveUpdate
- ELSEIF:${channel == 'telegram'}
- AdaptiveCardBotAction, AdaptiveCardTeamsTaskModule, AdaptiveCardSubmitAction, List, TeamsFileConsent
- ELSE:
-
# ChoiceInput_Prompt_CQChcR()
[Activity
Text = ${ChoiceInput_Prompt_CQChcR_text()}
]
# ChoiceInput_Prompt_CQChcR_text()
- What card do you want?
# SendActivity_FNEXZh()
-${HeroCard()}
# SendActivity_lEVbJV()
-${ThumbnailCard()}
# SendActivity_RTN27M()
-${SigninCard()}
# SendActivity_MKkvvu()
- ${AnimationCard()}
# SendActivity_uTm0n2()
- ${VideoCard()}
# SendActivity_QuTjbR()
- ${AudioCard()}
# SendActivity_mxKi3X()
-${CarouselTemplate()}
# SendActivity_72oOOw()
-${ListTemplate()}
# SendActivity_JsAcV8()
-${ReceiptCard()}
# SendActivity_pYKkwT()
[Activity
Text = ${SendActivity_pYKkwT_text()}
]
# SendActivity_pYKkwT_text()
- I received an activity with this data in the value field ${turn.activity.value}
# SendActivity_muQ5dx()
[Activity
Text = ${SendActivity_muQ5dx_text()}
]
# SendActivity_muQ5dx_text()
- ${dialog.CardType} cards are not supported in the ${turn.activity.channelId} channel.
# SendActivity_xQaXd3()
- ${AdaptiveCardBotAction()}
# SendActivity_gi7Il1()
- ${AdaptiveCardTaskModule()}
# SendActivity_wdjazJ()
- ${AdaptiveCardSubmitAction()}
# SendActivity_CQMkYP()
- ${HeroCard()}
# SendActivity_yPmKNE()
- ${ThumbnailCard()}
# SendActivity_cRm57W()
- ${ReceiptCard()}
# SendActivity_jFdVpH()
- ${SigninCard()}
# SendActivity_WqKTFa()
- ${CarouselTemplate()}
# SendActivity_d764md()
- ${ListTemplate()}
# SendActivity_sAEgJI()
- ${AnimationCard()}
# SendActivity_LWdTvT()
- ${AudioCard()}
# SendActivity_jLEgbL()
- ${VideoCard()}
# SendActivity_Mvport()
- ${TeamsFileConsent()}
# SendActivity_0TKIF9()
[Activity
Text = ${SendActivity_0TKIF9_text()}
]
# SendActivity_0TKIF9_text()
- Update activity is not supported in the ${turn.activity.channelId} channel.
# SendActivity_QwGHzg()
[Activity
Text = ${SendActivity_QwGHzg_text()}
]
# SendActivity_QwGHzg_text()
- I received an activity with this data in the text field ${turn.activity.text} and this data in the value field ${turn.activity.value}
# SendActivity_73NchW()
- ${UpdateCardTemplate('Update card', 'Update Card Action', 'Update card title', 'Update card text', '')}
# UpdateActivity_Activity_Y72Ndw()
- ${UpdateCardTemplate('Newly updated card', `Update count - ${user.UpdateCount}`, 'Update card', 'UpdateCardAction', '')}
# SendActivity_nB1AD7()
- ${O365Card()}
# ChoiceInput_UnrecognizedPrompt_CQChcR()
[Activity
Text = ${ChoiceInput_UnrecognizedPrompt_CQChcR_text()}
]
# ChoiceInput_UnrecognizedPrompt_CQChcR_text()
[import](common.lg)
> !# @strict = false
> !# @namespace = cards
> !# @exports = UnsupportedChannelCards
#UnsupportedChannelCards(channel)
- IF:${channel == 'emulator'}
- AdaptiveCardTeamsTaskModule, AdaptiveUpdate, O365, TeamsFileConsent
- ELSEIF:${channel == 'directline'}
- AdaptiveUpdate
- ELSEIF:${channel == 'telegram'}
- AdaptiveCardBotAction, AdaptiveCardTeamsTaskModule, AdaptiveCardSubmitAction, List, TeamsFileConsent
- ELSE:
-
# ChoiceInput_Prompt_CQChcR()
[Activity
Text = ${ChoiceInput_Prompt_CQChcR_text()}
Speak = ${ChoiceInput_Prompt_CQChcR_text()}
]
# ChoiceInput_Prompt_CQChcR_text()
- What card do you want?
# SendActivity_FNEXZh()
-${HeroCard()}
# SendActivity_lEVbJV()
-${ThumbnailCard()}
# SendActivity_RTN27M()
-${SigninCard()}
# SendActivity_MKkvvu()
- ${AnimationCard()}
# SendActivity_uTm0n2()
- ${VideoCard()}
# SendActivity_QuTjbR()
- ${AudioCard()}
# SendActivity_mxKi3X()
-${CarouselTemplate()}
# SendActivity_72oOOw()
-${ListTemplate()}
# SendActivity_JsAcV8()
-${ReceiptCard()}
# SendActivity_pYKkwT()
[Activity
Text = ${SendActivity_pYKkwT_text()}
]
# SendActivity_pYKkwT_text()
- I received an activity with this data in the value field ${turn.activity.value}
# SendActivity_muQ5dx()
[Activity
Text = ${SendActivity_muQ5dx_text()}
]
# SendActivity_muQ5dx_text()
- ${dialog.CardType} cards are not supported in the ${turn.activity.channelId} channel.
# SendActivity_xQaXd3()
- ${AdaptiveCardBotAction()}
# SendActivity_gi7Il1()
- ${AdaptiveCardTaskModule()}
# SendActivity_wdjazJ()
- ${AdaptiveCardSubmitAction()}
# SendActivity_CQMkYP()
- ${HeroCard()}
# SendActivity_yPmKNE()
- ${ThumbnailCard()}
# SendActivity_cRm57W()
- ${ReceiptCard()}
# SendActivity_jFdVpH()
- ${SigninCard()}
# SendActivity_WqKTFa()
- ${CarouselTemplate()}
# SendActivity_d764md()
- ${ListTemplate()}
# SendActivity_sAEgJI()
- ${AnimationCard()}
# SendActivity_LWdTvT()
- ${AudioCard()}
# SendActivity_jLEgbL()
- ${VideoCard()}
# SendActivity_Mvport()
- ${TeamsFileConsent()}
# SendActivity_0TKIF9()
[Activity
Text = ${SendActivity_0TKIF9_text()}
]
# SendActivity_0TKIF9_text()
- Update activity is not supported in the ${turn.activity.channelId} channel.
# SendActivity_QwGHzg()
[Activity
Text = ${SendActivity_QwGHzg_text()}
]
# SendActivity_QwGHzg_text()
- I received an activity with this data in the text field ${turn.activity.text} and this data in the value field ${turn.activity.value}
# SendActivity_73NchW()
- ${UpdateCardTemplate('Update card', 'Update Card Action', 'Update card title', 'Update card text', '')}
# UpdateActivity_Activity_Y72Ndw()
- ${UpdateCardTemplate('Newly updated card', `Update count - ${user.UpdateCount}`, 'Update card', 'UpdateCardAction', '')}
# SendActivity_nB1AD7()
- ${O365Card()}
# ChoiceInput_UnrecognizedPrompt_CQChcR()
[Activity
Text = ${ChoiceInput_UnrecognizedPrompt_CQChcR_text()}
]
# ChoiceInput_UnrecognizedPrompt_CQChcR_text()
- Got: ${turn.activity}\n\nWhat card do you want?

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

@ -3,6 +3,7 @@
# AttachmentInput_Prompt_JKtU0T()
[Activity
Text = ${AttachmentInput_Prompt_JKtU0T_text()}
InputHint = acceptingInput
]
# AttachmentInput_Prompt_JKtU0T_text()
@ -10,6 +11,7 @@
# AttachmentInput_UnrecognizedPrompt_JKtU0T()
[Activity
Text = ${AttachmentInput_UnrecognizedPrompt_JKtU0T_text()}
InputHint = acceptingInput
]
# AttachmentInput_UnrecognizedPrompt_JKtU0T_text()
@ -17,11 +19,11 @@
# SendActivity_ZBBqzI()
[Activity
Text = ${SendActivity_ZBBqzI_text()}
InputHint = acceptingInput
]
# SendActivity_ZBBqzI_text()
- ```Attachment "${dialog.file.Name}" has been received.
File content: ${dialog.fileContent}```
- ```Attachment "${dialog.file.Name}" has been received.\r\nFile content: ${dialog.fileContent}```
# ConfirmInput_Prompt_9WovlS()
[Activity
Text = ${ConfirmInput_Prompt_9WovlS_text()}

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

@ -22,6 +22,7 @@
# SendActivity_uWZAVE()
[Activity
Text = ${SendActivity_uWZAVE_text()}
InputHint = ignoringInput
Attachments = ${AttachmentTemplate('Files/architecture-resize.png', 'image/png', `${conversation.HostUrl}/images/architecture-resize.png`)}
]
# SendActivity_uWZAVE_text()
@ -43,6 +44,7 @@
# SendActivity_4HOnje()
[Activity
Text = ${SendActivity_4HOnje_text()}
InputHint = ignoringInput
Attachments = ${AttachmentTemplate('Files/architecture-resize.png', 'image/png', `data:image/png;base64,${base64(GetFile())}`)}
]

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

@ -69,12 +69,14 @@
[Activity
Attachments = ${HeroCard()} | ${HeroCard()} | ${HeroCard()}
AttachmentLayout = carousel
InputHint = acceptingInput
]
# ListTemplate
[Activity
Attachments = ${HeroCard()} | ${HeroCard()} | ${HeroCard()}
AttachmentLayout = list
InputHint = acceptingInput
]
# AnimationCard
@ -122,16 +124,22 @@
# AdaptiveCardBotAction
[Activity
Attachments = ${json(adaptivecardbotactionjson())}
AttachmentLayout = list
InputHint = acceptingInput
]
# AdaptiveCardSubmitAction
[Activity
Attachments = ${json(adaptivecardsubmitactionjson())}
AttachmentLayout = list
InputHint = acceptingInput
]
# AdaptiveCardTaskModule
[Activity
Attachments = ${json(adaptivecardtaskmodulejson())}
AttachmentLayout = list
InputHint = acceptingInput
]
# TeamsFileConsent
@ -142,6 +150,8 @@
# O365Card
[Activity
Attachments = ${json(o365cardjson())}
AttachmentLayout = list
InputHint = acceptingInput
]
# adaptivecardbotactionjson()
@ -282,11 +292,242 @@
# o365cardjson()
- ```
{
"type": "MessageCard",
"type": "application/vnd.microsoft.teams.card.o365connector",
"context": "http://schema.org/extensions",
"summary": "John Doe commented on Trello",
"title": "Project Tango",
"sections": [],
"potentialAction": []
"summary": "O365 card summary",
"title": "card title",
"text": "card text",
"themeColor": "#E67A9E",
"sections": [
{
"title": "**section title**",
"text": "section text",
"activityTitle": "activity title",
"activitySubtitle": "activity subtitle",
"activityText": "activity text",
"activityImage": "http://connectorsdemo.azurewebsites.net/images/MSC12_Oscar_002.jpg",
"activityImageType": "avatar",
"markdown": true,
"facts": [
{
"name": "Fact name 1",
"value": "Fact value 1"
},
{
"name": "Fact name 2",
"value": "Fact value 2"
}
],
"images": [
{
"image": "http://connectorsdemo.azurewebsites.net/images/MicrosoftSurface_024_Cafe_OH-06315_VS_R1c.jpg",
"title": "image 1"
},
{
"image": "http://connectorsdemo.azurewebsites.net/images/WIN12_Scene_01.jpg",
"title": "image 2"
},
{
"image": "http://connectorsdemo.azurewebsites.net/images/WIN12_Anthony_02.jpg",
"title": "image 3"
}
]
}
],
"potentialAction": [
{
"@type": "ActionCard",
"inputs": [
{
"@type": "MultichoiceInput",
"choices": [
{
"display": "Choice 1"
},
{
"display": "Choice 2"
},
{
"display": "Choice 3"
}
],
"style": "expanded",
"isMultiSelect": true,
"id": "list-1",
"isRequired": true,
"title": "Pick multiple options"
},
{
"@type": "MultichoiceInput",
"choices": [
{
"display": "Choice 4"
},
{
"display": "Choice 5"
},
{
"display": "Choice 6"
}
],
"style": "compact",
"isMultiSelect": true,
"id": "list-2",
"isRequired": true,
"title": "Pick multiple options"
},
{
"@type": "MultichoiceInput",
"choices": [
{
"display": "Choice a",
"value": "a"
},
{
"display": "Choice b",
"value": "b"
},
{
"display": "Choice c",
"value": "c"
}
],
"style": "expanded",
"isMultiSelect": false,
"id": "list-3",
"isRequired": false,
"title": "Pick an option"
},
{
"@type": "MultichoiceInput",
"choices": [
{
"display": "Choice x",
"value": "x"
},
{
"display": "Choice y",
"value": "y"
},
{
"display": "Choice z",
"value": "z"
}
],
"style": "compact",
"isMultiSelect": false,
"id": "list-4",
"isRequired": false,
"title": "Pick an option"
}
],
"actions": [
{
"@type": "HttpPOST",
"name": "Send",
"@id": "card-1-btn-1"
}
],
"name": "Multiple Choice",
"@id": "card-1"
},
{
"@type": "ActionCard",
"inputs": [
{
"@type": "TextInput",
"isMultiline": true,
"id": "text-1",
"isRequired": false,
"title": "multiline, no maxLength"
},
{
"@type": "TextInput",
"isMultiline": false,
"id": "text-2",
"isRequired": false,
"title": "single line, no maxLength"
},
{
"@type": "TextInput",
"isMultiline": true,
"id": "text-3",
"isRequired": true,
"title": "multiline, max len = 10, isRequired"
},
{
"@type": "TextInput",
"isMultiline": false,
"id": "text-4",
"isRequired": true,
"title": "single line, max len = 10, isRequired"
}
],
"actions": [
{
"@type": "HttpPOST",
"name": "Send",
"@id": "card-2-btn-1"
}
],
"name": "Text Input",
"@id": "card-2"
},
{
"@type": "ActionCard",
"inputs": [
{
"@type": "DateInput",
"includeTime": true,
"id": "date-1",
"isRequired": true,
"title": "date with time"
},
{
"@type": "DateInput",
"includeTime": false,
"id": "date-2",
"isRequired": false,
"title": "date only"
}
],
"actions": [
{
"@type": "HttpPOST",
"name": "Send",
"@id": "card-3-btn-1"
}
],
"name": "Date Input",
"@id": "card-3"
},
{
"@type": "ViewAction",
"name": "View Action"
},
{
"@type": "OpenUri",
"targets": [
{
"os": "default",
"uri": "http://microsoft.com"
},
{
"os": "iOS",
"uri": "http://microsoft.com"
},
{
"os": "android",
"uri": "http://microsoft.com"
},
{
"os": "windows",
"uri": "http://microsoft.com"
}
],
"name": "Open Uri",
"@id": "open-uri"
}
]
}
```

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

@ -10,7 +10,7 @@ using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Controllers;
namespace Microsoft.BotFrameworkFunctionalTests.EchoSkillBotv3
namespace Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBotv3
{
/// <summary>
/// Web Api Controller to intersect HTTP operations when the Action Invoker is triggered to capture exceptions and send it to the bot as an Activity.

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

@ -6,7 +6,7 @@ using Newtonsoft.Json.Serialization;
using System.Web.Http;
using System.Web.Http.Controllers;
namespace Microsoft.BotFrameworkFunctionalTests.EchoSkillBotv3
namespace Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBotv3
{
public static class WebApiConfig
{

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

@ -8,7 +8,7 @@ using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Microsoft.BotFrameworkFunctionalTests.EchoSkillBotv3.Authentication
namespace Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBotv3.Authentication
{
/// <summary>
/// Sample claims validator that loads an allowed list from configuration if present

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

@ -5,7 +5,7 @@ using Microsoft.Bot.Connector.SkillAuthentication;
using System.Configuration;
using System.Linq;
namespace Microsoft.BotFrameworkFunctionalTests.EchoSkillBotv3.Authentication
namespace Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBotv3.Authentication
{
public class CustomSkillAuthenticationConfiguration : AuthenticationConfiguration
{

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

@ -10,11 +10,11 @@ using System.Web.Http;
using Autofac;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Internals;
using Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBotv3.Authentication;
using Microsoft.Bot.Connector;
using Microsoft.Bot.Connector.SkillAuthentication;
using Microsoft.BotFrameworkFunctionalTests.EchoSkillBotv3.Authentication;
namespace Microsoft.BotFrameworkFunctionalTests.EchoSkillBotv3
namespace Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBotv3
{
// Specify which type provides the authentication configuration to allow for validation for skills.
[SkillBotAuthentication(AuthenticationConfigurationProviderType = typeof(CustomSkillAuthenticationConfiguration))]

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

@ -6,7 +6,7 @@ using System.Threading.Tasks;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;
namespace Microsoft.BotFrameworkFunctionalTests.EchoSkillBotv3.Dialogs
namespace Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBotv3.Dialogs
{
[Serializable]
public class RootDialog : IDialog<object>

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

@ -1 +1 @@
<%@ Application Codebehind="Global.asax.cs" Inherits="Microsoft.BotFrameworkFunctionalTests.EchoSkillBotv3.WebApiApplication" Language="C#" %>
<%@ Application Codebehind="Global.asax.cs" Inherits="Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBotv3.WebApiApplication" Language="C#" %>

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

@ -9,7 +9,7 @@ using Autofac;
using Microsoft.Bot.Connector;
using System.Reflection;
namespace Microsoft.BotFrameworkFunctionalTests.EchoSkillBotv3
namespace Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBotv3
{
public class WebApiApplication : System.Web.HttpApplication
{

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

@ -3,10 +3,9 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Schema;
namespace Microsoft.BotFrameworkFunctionalTests.EchoSkillBot.Bots
namespace Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBot.Bots
{
public class EchoBot : ActivityHandler
{

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

@ -3,10 +3,9 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
namespace Microsoft.BotFrameworkFunctionalTests.EchoSkillBot.Controllers
namespace Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBot.Controllers
{
/// <summary>
/// This ASP Controller is created to handle a request. Dependency Injection will provide the Adapter and IBot implementation at runtime.

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

@ -4,7 +4,7 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace Microsoft.BotFrameworkFunctionalTests.EchoSkillBot
namespace Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBot
{
public class Program
{

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

@ -2,14 +2,13 @@
// 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.Logging;
namespace Microsoft.BotFrameworkFunctionalTests.EchoSkillBot
namespace Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBot
{
public class SkillAdapterWithErrorHandler : CloudAdapter
{

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

@ -1,85 +1,97 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
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.Bot.Schema;
using Microsoft.BotFrameworkFunctionalTests.EchoSkillBot.Bots;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Microsoft.BotFrameworkFunctionalTests.EchoSkillBot
{
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();
using System.Collections.Generic;
using System.Globalization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBot.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.EchoSkillBot
{
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 =>
{
// AllowedCallers is the setting in the 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 edit the AllowedCallers and add the parent bot's Microsoft app ID to the list.
// In this sample, we allow all callers if AllowedCallers contains an "*".
var callersSection = Configuration.GetSection(CallersConfigKey);
var callers = callersSection.Get<string[]>();
if (callers == null)
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))
{
throw new ArgumentNullException($"\"{CallersConfigKey}\" not found in configuration.");
// 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 = new AllowedCallersClaimsValidator(callers) };
return new AuthenticationConfiguration
{
ClaimsValidator = claimsValidator,
ValidTokenIssuers = validTokenIssuers
};
});
services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>();
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();
});
}
}
}
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();
});
}
}
}

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

@ -1,6 +1,8 @@
{
"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.

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

@ -1,15 +1,14 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Runtime.Settings;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace EchoSkillBotComposer.Controllers
namespace Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBotComposer.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

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

@ -1,12 +1,11 @@
using System;
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Logging;
namespace EchoSkillBotComposer.Controllers
namespace Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBotComposer.Controllers
{
/// <summary>
/// A controller that handles skill replies to the bot.

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

@ -1,10 +1,10 @@
using System;
using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Runtime.Extensions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
namespace EchoSkillBotComposer
namespace Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBotComposer
{
public class Program
{
@ -30,4 +30,4 @@ namespace EchoSkillBotComposer
webBuilder.UseStartup<Startup>();
});
}
}
}

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

@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Runtime.Extensions;
@ -6,7 +6,7 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace EchoSkillBotComposer
namespace Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBotComposer
{
public class Startup
{

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

@ -3,21 +3,21 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30621.155
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleHostBot", "SimpleHostBot\SimpleHostBot.csproj", "{AAE978F8-D22B-41E8-B445-872FF4194713}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot", "SimpleHostBot\Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot.csproj", "{AAE978F8-D22B-41E8-B445-872FF4194713}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EchoSkillBot", "EchoSkillBot\EchoSkillBot.csproj", "{692F26DD-F7BA-49F3-AC6D-73047C1E5D61}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBot", "EchoSkillBot\Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBot.csproj", "{692F26DD-F7BA-49F3-AC6D-73047C1E5D61}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WaterfallHostBot", "WaterfallHostBot\WaterfallHostBot.csproj", "{15A946BE-39F9-4945-9895-0019ED3392FC}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot", "WaterfallHostBot\Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot.csproj", "{15A946BE-39F9-4945-9895-0019ED3392FC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WaterfallSkillBot", "WaterfallSkillBot\WaterfallSkillBot.csproj", "{E3BECBEF-E41F-48D1-9EEB-A4D7E1CD34E3}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot", "WaterfallSkillBot\Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.csproj", "{E3BECBEF-E41F-48D1-9EEB-A4D7E1CD34E3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EchoSkillBot-v3", "EchoSkillBot-v3\EchoSkillBot-v3.csproj", "{41BC9547-FD9E-40E1-B1D9-C3F25BC9A2F3}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBot-v3", "EchoSkillBot-v3\Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBot-v3.csproj", "{41BC9547-FD9E-40E1-B1D9-C3F25BC9A2F3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleHostBotComposer", "SimpleHostBotComposer\SimpleHostBotComposer.csproj", "{FDC53B3A-0E15-4FDF-A587-05C8F90BC2B6}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBotComposer", "SimpleHostBotComposer\Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBotComposer.csproj", "{FDC53B3A-0E15-4FDF-A587-05C8F90BC2B6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EchoSkillBotComposer", "EchoSkillBotComposer\EchoSkillBotComposer.csproj", "{9BA47CF9-7D90-4B5C-A9FA-01797A435D53}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBotComposer", "EchoSkillBotComposer\Microsoft.Bot.Builder.FunctionalTestsBots.EchoSkillBotComposer.csproj", "{9BA47CF9-7D90-4B5C-A9FA-01797A435D53}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ComposerSkillBotDotNet", "ComposerSkillBotDotNet\ComposerSkillBotDotNet.csproj", "{925E0E3F-194D-4E56-AE78-6FF6D49B7E7E}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.FunctionalTestsBots.ComposerSkillBotDotNet", "ComposerSkillBotDotNet\Microsoft.Bot.Builder.FunctionalTestsBots.ComposerSkillBotDotNet.csproj", "{925E0E3F-194D-4E56-AE78-6FF6D49B7E7E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ComposerHostBotDotNet", "ComposerHostBotDotNet\ComposerHostBotDotNet.csproj", "{E3FCDDD4-553D-44BA-8B2F-82BA778261BC}"
EndProject

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

@ -4,44 +4,40 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.BotFramework;
using Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot.Bots;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Builder.Integration.AspNet.Core.Skills;
using Microsoft.Bot.Builder.Skills;
using Microsoft.Bot.Builder.TraceExtensions;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Schema;
using Microsoft.BotFrameworkFunctionalTests.SimpleHostBot.Bots;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Microsoft.BotFrameworkFunctionalTests.SimpleHostBot
namespace Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot
{
public class AdapterWithErrorHandler : CloudAdapter
{
private readonly BotFrameworkAuthentication _auth;
private readonly ConversationState _conversationState;
private readonly IConfiguration _configuration;
private readonly ILogger _logger;
private readonly SkillHttpClient _skillClient;
private readonly SkillsConfiguration _skillsConfig;
/// <summary>
/// Initializes a new instance of the <see cref="AdapterWithErrorHandler"/> class to handle errors.
/// </summary>
/// <param name="botFrameworkAuthentication">The cloud environment for the bot.</param>
/// <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="skillClient">The HTTP client for the skills.</param>
/// <param name="skillsConfig">The skills configuration.</param>
public AdapterWithErrorHandler(BotFrameworkAuthentication botFrameworkAuthentication, IConfiguration configuration, ILogger<CloudAdapter> logger, ConversationState conversationState = null, SkillHttpClient skillClient = null, SkillsConfiguration skillsConfig = null)
: base(botFrameworkAuthentication, logger)
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));
_skillClient = skillClient;
_skillsConfig = skillsConfig;
OnTurnError = HandleTurnErrorAsync;
@ -104,7 +100,7 @@ namespace Microsoft.BotFrameworkFunctionalTests.SimpleHostBot
/// <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 || _skillClient == null || _skillsConfig == null)
if (_conversationState == null || _skillsConfig == null)
{
return;
}
@ -123,7 +119,9 @@ namespace Microsoft.BotFrameworkFunctionalTests.SimpleHostBot
endOfConversation.ApplyConversationReference(turnContext.Activity.GetConversationReference(), true);
await _conversationState.SaveChangesAsync(turnContext, true, cancellationToken);
await _skillClient.PostActivityAsync(botId, activeSkill, _skillsConfig.SkillHostEndpoint, (Activity)endOfConversation, 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)

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

@ -1,242 +1,259 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Integration.AspNet.Core.Skills;
using Microsoft.Bot.Builder.Skills;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Schema;
using Microsoft.BotFrameworkFunctionalTests.SimpleHostBot.Dialogs;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
namespace Microsoft.BotFrameworkFunctionalTests.SimpleHostBot.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 ConversationState _conversationState;
private readonly SkillHttpClient _skillClient;
private readonly SkillsConfiguration _skillsConfig;
private readonly Dialog _dialog;
/// <summary>
/// Initializes a new instance of the <see cref="HostBot"/> class.
/// </summary>
/// <param name="conversationState">A state management object for the conversation.</param>
/// <param name="skillsConfig">The skills configuration.</param>
/// <param name="skillClient">The HTTP client for the skills.</param>
/// <param name="configuration">The configuration properties.</param>
/// <param name="dialog">The dialog to use.</param>
public HostBot(ConversationState conversationState, SkillsConfiguration skillsConfig, SkillHttpClient skillClient, IConfiguration configuration, SetupDialog dialog)
{
_conversationState = conversationState ?? throw new ArgumentNullException(nameof(conversationState));
_skillsConfig = skillsConfig ?? throw new ArgumentNullException(nameof(skillsConfig));
_skillClient = skillClient ?? throw new ArgumentNullException(nameof(skillClient));
_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);
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 _skillClient.PostActivityAsync<ExpectedReplies>(_botId, targetSkill, _skillsConfig.SkillHostEndpoint, 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 responseActivities = expectRepliesResponse.Body?.Activities;
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 _skillClient.PostActivityAsync(_botId, targetSkill, _skillsConfig.SkillHostEndpoint, (Activity)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}");
}
}
}
}
}
// 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.SimpleHostBot.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.SimpleHostBot.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}");
}
}
}
}
}

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

@ -3,10 +3,9 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
namespace Microsoft.BotFrameworkFunctionalTests.SimpleHostBot.Controllers
namespace Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot.Controllers
{
/// <summary>
/// This ASP Controller is created to handle a request. Dependency Injection will provide the Adapter and IBot implementation at runtime.

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

@ -2,15 +2,14 @@
// Licensed under the MIT License.
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Builder.Skills;
namespace Microsoft.BotFrameworkFunctionalTests.SimpleHostBot.Controllers
namespace Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot.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.
/// This example uses the <see cref="CloudSkillHandler"/> that is registered as a <see cref="ChannelServiceHandlerBase"/> in startup.cs.
/// </summary>
[ApiController]
[Route("api/skills")]
@ -19,8 +18,8 @@ namespace Microsoft.BotFrameworkFunctionalTests.SimpleHostBot.Controllers
/// <summary>
/// Initializes a new instance of the <see cref="SkillController"/> class.
/// </summary>
/// <param name="handler">The skill handler registered as ChannelServiceHandler.</param>
public SkillController(ChannelServiceHandler handler)
/// <param name="handler">The skill handler registered as ChannelServiceHandlerBase.</param>
public SkillController(ChannelServiceHandlerBase handler)
: base(handler)
{
}

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

@ -6,14 +6,13 @@ 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.FunctionalTestsBots.SimpleHostBot.Bots;
using Microsoft.Bot.Builder.Skills;
using Microsoft.Bot.Schema;
using Microsoft.BotFrameworkFunctionalTests.SimpleHostBot.Bots;
namespace Microsoft.BotFrameworkFunctionalTests.SimpleHostBot.Dialogs
namespace Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot.Dialogs
{
/// <summary>
/// The setup dialog for this bot.

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

@ -4,7 +4,7 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace Microsoft.BotFrameworkFunctionalTests.SimpleHostBot
namespace Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot
{
public class Program
{

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

@ -6,7 +6,7 @@ using System.Collections.Generic;
using Microsoft.Bot.Builder.Skills;
using Microsoft.Extensions.Configuration;
namespace Microsoft.BotFrameworkFunctionalTests.SimpleHostBot
namespace Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot
{
/// <summary>
/// A helper class that loads Skills information from configuration.

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

@ -1,104 +1,123 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Linq;
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.Bot.Schema;
using Microsoft.BotFrameworkFunctionalTests.SimpleHostBot.Bots;
using Microsoft.BotFrameworkFunctionalTests.SimpleHostBot.Dialogs;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Microsoft.BotFrameworkFunctionalTests.SimpleHostBot
{
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>();
services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();
services.AddSingleton(sp => new AuthenticationConfiguration
{
ClaimsValidator = new AllowedSkillsClaimsValidator(
(from skill in sp.GetService<SkillsConfiguration>().Skills.Values select skill.AppId).ToList())
});
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 client and skills 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>();
// 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();
});
}
}
}
// 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.SimpleHostBot.Bots;
using Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBot.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.SimpleHostBot
{
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();
});
}
}
}

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

@ -1,6 +1,8 @@
{
"MicrosoftAppType": "",
"MicrosoftAppId": "",
"MicrosoftAppPassword": "",
"MicrosoftAppTenantId": "",
"ChannelService": "",
"SkillHostEndpoint": "http://localhost:35000/api/skills/",
"BotFrameworkSkills": [

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

@ -1,15 +1,14 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Runtime.Settings;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace SimpleHostBotComposer.Controllers
namespace Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBotComposer.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

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

@ -1,12 +1,11 @@
using System;
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Logging;
namespace SimpleHostBotComposer.Controllers
namespace Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBotComposer.Controllers
{
/// <summary>
/// A controller that handles skill replies to the bot.

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

@ -1,10 +1,10 @@
using System;
using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Runtime.Extensions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
namespace SimpleHostBotComposer
namespace Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBotComposer
{
public class Program
{
@ -30,4 +30,4 @@ namespace SimpleHostBotComposer
webBuilder.UseStartup<Startup>();
});
}
}
}

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

@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Runtime.Extensions;
@ -6,7 +6,7 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace SimpleHostBotComposer
namespace Microsoft.Bot.Builder.FunctionalTestsBots.SimpleHostBotComposer
{
public class Startup
{

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

@ -4,35 +4,33 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot.Dialogs;
using Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot.Middleware;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Builder.Integration.AspNet.Core.Skills;
using Microsoft.Bot.Builder.Skills;
using Microsoft.Bot.Builder.TraceExtensions;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Schema;
using Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot.Dialogs;
using Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot.Middleware;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot
{
public class AdapterWithErrorHandler : CloudAdapter
{
private readonly BotFrameworkAuthentication _auth;
private readonly IConfiguration _configuration;
private readonly ConversationState _conversationState;
private readonly ILogger _logger;
private readonly SkillHttpClient _skillClient;
private readonly SkillsConfiguration _skillsConfig;
public AdapterWithErrorHandler(BotFrameworkAuthentication botFrameworkAuthentication, IConfiguration configuration, ILogger<CloudAdapter> logger, ConversationState conversationState, SkillHttpClient skillClient = null, SkillsConfiguration skillsConfig = null)
: base(botFrameworkAuthentication, logger)
{
public AdapterWithErrorHandler(BotFrameworkAuthentication auth, IConfiguration configuration, ILogger<CloudAdapter> logger, ConversationState conversationState, SkillsConfiguration skillsConfig = null)
: base(auth, logger)
{
_auth = auth ?? throw new ArgumentNullException(nameof(auth));
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
_conversationState = conversationState ?? throw new ArgumentNullException(nameof(conversationState));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_skillClient = skillClient;
_skillsConfig = skillsConfig;
OnTurnError = HandleTurnError;
@ -77,7 +75,7 @@ namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot
private async Task EndSkillConversationAsync(ITurnContext turnContext)
{
if (_skillClient == null || _skillsConfig == null)
if (_skillsConfig == null)
{
return;
}
@ -97,7 +95,8 @@ namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot
endOfConversation.ApplyConversationReference(turnContext.Activity.GetConversationReference(), true);
await _conversationState.SaveChangesAsync(turnContext, true);
await _skillClient.PostActivityAsync(botId, activeSkill, _skillsConfig.SkillHostEndpoint, (Activity)endOfConversation, CancellationToken.None);
using var client = _auth.CreateBotFrameworkClient();
await client.PostActivityAsync(botId, activeSkill.AppId, activeSkill.SkillEndpoint, _skillsConfig.SkillHostEndpoint, endOfConversation.Conversation.Id, (Activity)endOfConversation, CancellationToken.None);
}
}
catch (Exception ex)

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

@ -5,12 +5,11 @@ 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.BotFrameworkFunctionalTests.WaterfallHostBot.Bots
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot.Bots
{
public class RootBot<T> : ActivityHandler
where T : Dialog

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

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot
{
internal class ComponentSettings
{

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

@ -4,11 +4,10 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Extensions.Logging;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot.Controllers
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot.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

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

@ -4,17 +4,16 @@
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;
using Microsoft.Extensions.Logging;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot.Controllers
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot.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.
/// This example uses the <see cref="CloudSkillHandler"/> that is registered as a <see cref="ChannelServiceHandlerBase"/> in startup.cs.
/// </summary>
[ApiController]
[Route("api/skills")]
@ -22,7 +21,7 @@ namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot.Controllers
{
private readonly ILogger _logger;
public SkillController(ChannelServiceHandler handler, ILogger<SkillController> logger)
public SkillController(ChannelServiceHandlerBase handler, ILogger<SkillController> logger)
: base(handler)
{
_logger = logger;

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

@ -6,19 +6,17 @@ 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.FunctionalTestsBots.WaterfallHostBot.Dialogs.Sso;
using Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot.Skills;
using Microsoft.Bot.Builder.Skills;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Schema;
using Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot.Dialogs.Sso;
using Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot.Skills;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot.Dialogs
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot.Dialogs
{
/// <summary>
/// The main dialog for this bot. It uses a <see cref="SkillDialog"/> to call skills.
@ -32,24 +30,21 @@ namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot.Dialogs
private readonly IStatePropertyAccessor<BotFrameworkSkill> _activeSkillProperty;
private readonly string _deliveryMode = $"{typeof(MainDialog).FullName}.DeliveryMode";
private readonly string _selectedSkillKey = $"{typeof(MainDialog).FullName}.SelectedSkillKey";
private readonly BotFrameworkAuthentication _auth;
private readonly SkillsConfiguration _skillsConfig;
private readonly IConfiguration _configuration;
// Dependency injection uses this constructor to instantiate MainDialog.
public MainDialog(ConversationState conversationState, SkillConversationIdFactoryBase conversationIdFactory, SkillHttpClient skillClient, SkillsConfiguration skillsConfig, IConfiguration configuration)
public MainDialog(BotFrameworkAuthentication auth, ConversationState conversationState, SkillConversationIdFactoryBase conversationIdFactory, SkillsConfiguration skillsConfig, IConfiguration configuration)
: base(nameof(MainDialog))
{
_auth = auth ?? throw new ArgumentNullException(nameof(auth));
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
var botId = configuration.GetSection(MicrosoftAppCredentials.MicrosoftAppIdKey)?.Value;
_skillsConfig = skillsConfig ?? throw new ArgumentNullException(nameof(skillsConfig));
if (skillClient == null)
{
throw new ArgumentNullException(nameof(skillClient));
}
if (conversationState == null)
{
throw new ArgumentNullException(nameof(conversationState));
@ -62,7 +57,7 @@ namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot.Dialogs
AddDialog(new TangentDialog());
// Create and add SkillDialog instances for the configured skills.
AddSkillDialogs(conversationState, conversationIdFactory, skillClient, skillsConfig, botId);
AddSkillDialogs(conversationState, conversationIdFactory, skillsConfig, botId);
// Add ChoicePrompt to render available delivery modes.
AddDialog(new ChoicePrompt("DeliveryModePrompt"));
@ -304,7 +299,7 @@ namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot.Dialogs
}
// Helper method that creates and adds SkillDialog instances for the configured skills.
private void AddSkillDialogs(ConversationState conversationState, SkillConversationIdFactoryBase conversationIdFactory, SkillHttpClient skillClient, SkillsConfiguration skillsConfig, string botId)
private void AddSkillDialogs(ConversationState conversationState, SkillConversationIdFactoryBase conversationIdFactory, SkillsConfiguration skillsConfig, string botId)
{
foreach (var skillInfo in _skillsConfig.Skills.Values)
{
@ -313,7 +308,7 @@ namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot.Dialogs
{
BotId = botId,
ConversationIdFactory = conversationIdFactory,
SkillClient = skillClient,
SkillClient = _auth.CreateBotFrameworkClient(),
SkillHostEndpoint = skillsConfig.SkillHostEndpoint,
ConversationState = conversationState,
Skill = skillInfo

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

@ -5,13 +5,12 @@ using System;
using System.Collections.Generic;
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.Connector.Authentication;
using Microsoft.Bot.Schema;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot.Dialogs.Sso
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot.Dialogs.Sso
{
/// <summary>
/// Helps prepare the host for SSO operations and provides helpers to check the status and invoke the skill.

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

@ -6,7 +6,7 @@ using System.Threading.Tasks;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Schema;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot.Dialogs.Sso
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot.Dialogs.Sso
{
public class SsoSignInDialog : ComponentDialog
{

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

@ -3,11 +3,10 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Schema;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot.Dialogs
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot.Dialogs
{
/// <summary>
/// A simple waterfall dialog used to test triggering tangents from <see cref="MainDialog"/>.

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

@ -5,12 +5,11 @@ 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.BotFrameworkFunctionalTests.WaterfallHostBot.Middleware
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot.Middleware
{
/// <summary>
/// Uses an ILogger instance to log user and bot messages. It filters out ContinueConversation events coming from skill responses.

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

@ -4,7 +4,7 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot
{
public class Program
{

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

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using Microsoft.Bot.Schema;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot.Skills
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot.Skills
{
public class EchoSkill : SkillDefinition
{

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

@ -6,7 +6,7 @@ using System.Collections.Generic;
using Microsoft.Bot.Builder.Skills;
using Microsoft.Bot.Schema;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot.Skills
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot.Skills
{
/// <summary>
/// Extends <see cref="BotFrameworkSkill"/> and provides methods to return the actions and the begin activity to start a skill.

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

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using Microsoft.Bot.Schema;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot.Skills
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot.Skills
{
public class TeamsSkill : SkillDefinition
{

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

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using Microsoft.Bot.Schema;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot.Skills
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot.Skills
{
public class WaterfallSkill : SkillDefinition
{

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

@ -4,10 +4,10 @@
using System;
using System.Collections.Generic;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot.Skills;
using Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot.Skills;
using Microsoft.Extensions.Configuration;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot
{
/// <summary>
/// A helper class that loads Skills information from configuration.

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

@ -1,103 +1,124 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Linq;
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.Bot.Schema;
using Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot.Bots;
using Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot.Dialogs;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot
{
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 the skills configuration class.
// 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;
using Microsoft.Bot.Builder.BotFramework;
using Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot.Bots;
using Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot.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.WaterfallHostBot
{
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 the skills configuration class.
services.AddSingleton<SkillsConfiguration>();
services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();
services.AddSingleton(sp => new AuthenticationConfiguration
// Register AuthConfiguration to enable custom claim validation.
services.AddSingleton(sp =>
{
ClaimsValidator = new AllowedSkillsClaimsValidator(
(from skill in sp.GetService<SkillsConfiguration>().Skills.Values select skill.AppId).ToList())
var allowedSkills = sp.GetService<SkillsConfiguration>().Skills.Values.Select(skill => skill.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 expect a BotAdapter and some expect a BotFrameworkHttpAdapter, so
// register the same adapter instance for both types.
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.AddHttpClient<SkillHttpClient>();
// Create the Bot Framework Authentication to be used with the Bot Adapter.
services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>();
services.AddSingleton<ChannelServiceHandler, TokenExchangeSkillHandler>();
// 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>();
services.AddSingleton<UserState>();
// 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();
// Uncomment this to support HTTPS.
// app.UseHttpsRedirection();
app.UseRouting();
app.UseWebSockets();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
// Register the Bot Framework Adapter with error handling enabled.
// Note: some classes expect a BotAdapter and some expect a BotFrameworkHttpAdapter, so
// register the same adapter instance for both types.
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>();
services.AddSingleton<ChannelServiceHandlerBase, TokenExchangeSkillHandler>();
// 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>();
services.AddSingleton<UserState>();
// 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();
// Uncomment this to support HTTPS.
// app.UseHttpsRedirection();
app.UseRouting();
app.UseWebSockets();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

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

@ -8,20 +8,20 @@ using System.Security.Claims;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Skills;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Newtonsoft.Json.Linq;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallHostBot
{
/// <summary>
/// A <see cref="SkillHandler"/> specialized to support SSO Token exchanges.
/// A <see cref="CloudSkillHandler"/> specialized to support SSO Token exchanges.
/// </summary>
public class TokenExchangeSkillHandler : SkillHandler
public class TokenExchangeSkillHandler : CloudSkillHandler
{
private const string WaterfallSkillBot = "WaterfallSkillBot";
private const string ComposerSkillBot = "ComposerSkillBot";
@ -32,36 +32,31 @@ namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot
private readonly SkillConversationIdFactoryBase _conversationIdFactory;
private readonly ILogger _logger;
private readonly IConfiguration _configuration;
private readonly AuthenticationConfiguration _authConfig;
private readonly BotFrameworkAuthentication _botAuth;
private readonly BotFrameworkAuthentication _auth;
public TokenExchangeSkillHandler(
BotAdapter adapter,
IBot bot,
IConfiguration configuration,
ICredentialProvider credentialProvider,
SkillConversationIdFactoryBase conversationIdFactory,
AuthenticationConfiguration authConfig,
BotFrameworkAuthentication botAuth,
BotFrameworkAuthentication auth,
SkillsConfiguration skillsConfig,
IChannelProvider channelProvider = null,
ILogger<TokenExchangeSkillHandler> logger = null)
: base(adapter, bot, conversationIdFactory, credentialProvider, authConfig, channelProvider, logger)
: base(adapter, bot, conversationIdFactory, auth, logger)
{
_adapter = adapter;
_configuration = configuration;
_botAuth = botAuth;
_authConfig = authConfig;
_adapter = adapter ?? throw new ArgumentNullException(nameof(adapter));
_auth = auth ?? throw new ArgumentNullException(nameof(auth));
_conversationIdFactory = conversationIdFactory;
_skillsConfig = skillsConfig ?? new SkillsConfiguration(configuration);
_configuration = configuration;
_botId = configuration.GetSection(MicrosoftAppCredentials.MicrosoftAppIdKey)?.Value;
_logger = logger;
_logger = logger ?? NullLogger<TokenExchangeSkillHandler>.Instance;
}
protected override async Task<ResourceResponse> OnSendToConversationAsync(ClaimsIdentity claimsIdentity, string conversationId, Activity activity, CancellationToken cancellationToken = default(CancellationToken))
{
if (await InterceptOAuthCards(claimsIdentity, activity).ConfigureAwait(false))
if (await InterceptOAuthCards(claimsIdentity, activity, cancellationToken).ConfigureAwait(false))
{
return new ResourceResponse(Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture));
}
@ -71,7 +66,7 @@ namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot
protected override async Task<ResourceResponse> OnReplyToActivityAsync(ClaimsIdentity claimsIdentity, string conversationId, string activityId, Activity activity, CancellationToken cancellationToken = default(CancellationToken))
{
if (await InterceptOAuthCards(claimsIdentity, activity).ConfigureAwait(false))
if (await InterceptOAuthCards(claimsIdentity, activity, cancellationToken).ConfigureAwait(false))
{
return new ResourceResponse(Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture));
}
@ -91,60 +86,60 @@ namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot
return _skillsConfig.Skills.Values.FirstOrDefault(s => string.Equals(s.AppId, appId, StringComparison.InvariantCultureIgnoreCase));
}
private async Task<bool> InterceptOAuthCards(ClaimsIdentity claimsIdentity, Activity activity)
private async Task<bool> InterceptOAuthCards(ClaimsIdentity claimsIdentity, Activity activity, CancellationToken cancellationToken)
{
var oauthCardAttachment = activity.Attachments?.FirstOrDefault(a => a?.ContentType == OAuthCard.ContentType);
if (oauthCardAttachment != null)
if (oauthCardAttachment == null)
{
return false;
}
var targetSkill = GetCallingSkill(claimsIdentity);
if (targetSkill == null)
{
return false;
}
var oauthCard = ((JObject)oauthCardAttachment.Content).ToObject<OAuthCard>();
if (string.IsNullOrWhiteSpace(oauthCard?.TokenExchangeResource?.Uri))
{
return false;
}
using var tokenClient = await _auth.CreateUserTokenClientAsync(claimsIdentity, cancellationToken).ConfigureAwait(false);
using var context = new TurnContext(_adapter, activity);
context.TurnState.Add<IIdentity>(BotAdapter.BotIdentityKey, claimsIdentity);
// We need to know what connection name to use for the token exchange so we figure that out here
var connectionName = targetSkill.Id.Contains(WaterfallSkillBot) || targetSkill.Id.Contains(ComposerSkillBot) ? _configuration.GetSection("SsoConnectionName").Value : _configuration.GetSection("SsoConnectionNameTeams").Value;
// AAD token exchange
try
{
var targetSkill = GetCallingSkill(claimsIdentity);
if (targetSkill != null)
var result = await tokenClient.ExchangeTokenAsync(
activity.Recipient.Id,
connectionName,
activity.ChannelId,
new TokenExchangeRequest { Uri = oauthCard.TokenExchangeResource.Uri },
cancellationToken).ConfigureAwait(false);
if (!string.IsNullOrEmpty(result?.Token))
{
var oauthCard = ((JObject)oauthCardAttachment.Content).ToObject<OAuthCard>();
if (!string.IsNullOrWhiteSpace(oauthCard?.TokenExchangeResource?.Uri))
{
using (var context = new TurnContext(_adapter, activity))
{
context.TurnState.Add<IIdentity>("BotIdentity", claimsIdentity);
// We need to know what connection name to use for the token exchange so we figure that out here
var connectionName = targetSkill.Id.Contains(WaterfallSkillBot) || targetSkill.Id.Contains(ComposerSkillBot) ? _configuration.GetSection("SsoConnectionName").Value : _configuration.GetSection("SsoConnectionNameTeams").Value;
// AAD token exchange
try
{
var tokenClient = await _botAuth.CreateUserTokenClientAsync(claimsIdentity, CancellationToken.None).ConfigureAwait(false);
var result = await tokenClient.ExchangeTokenAsync(
activity.Recipient.Id,
connectionName,
activity.ChannelId,
new TokenExchangeRequest { Uri = oauthCard.TokenExchangeResource.Uri },
CancellationToken.None).ConfigureAwait(false);
if (!string.IsNullOrEmpty(result?.Token))
{
// If token above is null, then SSO has failed and hence we return false.
// If not, send an invoke to the skill with the token.
return await SendTokenExchangeInvokeToSkillAsync(activity, oauthCard.TokenExchangeResource.Id, result.Token, oauthCard.ConnectionName, targetSkill, default).ConfigureAwait(false);
}
}
catch (Exception ex)
{
// Show oauth card if token exchange fails.
_logger.LogWarning("Unable to exchange token.", ex);
return false;
}
return false;
}
}
// If token above is null, then SSO has failed and hence we return false.
// If not, send an invoke to the skill with the token.
return await SendTokenExchangeInvokeToSkill(activity, oauthCard.TokenExchangeResource.Id, result.Token, oauthCard.ConnectionName, targetSkill, default).ConfigureAwait(false);
}
}
catch (InvalidOperationException ex)
{
// Show oauth card if token exchange fails.
_logger.LogWarning("Unable to exchange token.", ex);
}
return false;
}
private async Task<bool> SendTokenExchangeInvokeToSkillAsync(Activity incomingActivity, string id, string token, string connectionName, BotFrameworkSkill targetSkill, CancellationToken cancellationToken)
private async Task<bool> SendTokenExchangeInvokeToSkill(Activity incomingActivity, string id, string token, string connectionName, BotFrameworkSkill targetSkill, CancellationToken cancellationToken)
{
var activity = incomingActivity.CreateReply();
activity.Type = ActivityTypes.Invoke;
@ -158,13 +153,14 @@ namespace Microsoft.BotFrameworkFunctionalTests.WaterfallHostBot
var skillConversationReference = await _conversationIdFactory.GetSkillConversationReferenceAsync(incomingActivity.Conversation.Id, cancellationToken).ConfigureAwait(false);
activity.Conversation = skillConversationReference.ConversationReference.Conversation;
activity.ServiceUrl = skillConversationReference.ConversationReference.ServiceUrl;
// route the activity to the skill
using var client = _botAuth.CreateBotFrameworkClient();
var response = await client.PostActivityAsync(_botId, targetSkill.AppId, targetSkill.SkillEndpoint, _skillsConfig.SkillHostEndpoint, incomingActivity.Conversation.Id, activity, cancellationToken);
// Check response status: true if success, false if failure
return response.Status >= 200 && response.Status <= 299;
using var client = _auth.CreateBotFrameworkClient();
var response = await client.PostActivityAsync(_botId, targetSkill.AppId, targetSkill.SkillEndpoint, _skillsConfig.SkillHostEndpoint, incomingActivity.Conversation.Id, activity, cancellationToken);
// Check response status: true if success, false if failure
return response.IsSuccessStatusCode();
}
}
}

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

@ -8,8 +8,10 @@
},
"AllowedHosts": "*",
"MicrosoftAppType": "",
"MicrosoftAppId": "",
"MicrosoftAppPassword": "",
"MicrosoftAppTenantId": "",
"SsoConnectionName": "",
"SsoConnectionNameTeams": "",
"SkillHostEndpoint": "http://localhost:35020/api/skills/",

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

@ -6,11 +6,10 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Schema;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Bots
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Bots
{
public class SkillBot<T> : ActivityHandler
where T : Dialog

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

@ -4,11 +4,10 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Extensions.Logging;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Controllers
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.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

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

@ -4,7 +4,7 @@
using System.IO;
using Microsoft.AspNetCore.Mvc;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Controllers
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.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

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

@ -8,13 +8,12 @@ using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs;
using Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.Proactive;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs;
using Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Proactive;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Controllers
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Controllers
{
[Route("api/notify")]
[ApiController]

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

@ -4,22 +4,21 @@
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.BotFrameworkFunctionalTests.WaterfallSkillBot.Controllers
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.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.
/// 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
{
public SkillController(ChannelServiceHandler handler)
public SkillController(ChannelServiceHandlerBase handler)
: base(handler)
{
}

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

@ -6,25 +6,24 @@ using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.Auth;
using Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.Cards;
using Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.Delete;
using Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.FileUpload;
using Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.MessageWithAttachment;
using Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.Proactive;
using Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.Sso;
using Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.Update;
using Microsoft.Bot.Builder.Integration.AspNet.Core.Skills;
using Microsoft.Bot.Builder.Skills;
using Microsoft.Bot.Builder.TraceExtensions;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Schema;
using Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Auth;
using Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Cards;
using Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Delete;
using Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.FileUpload;
using Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.MessageWithAttachment;
using Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Proactive;
using Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Sso;
using Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Update;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs
{
/// <summary>
/// A root dialog that can route activities sent to the skill to different sub-dialogs.
@ -32,10 +31,13 @@ namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs
public class ActivityRouterDialog : ComponentDialog
{
private static readonly string _echoSkill = "EchoSkill";
private readonly BotFrameworkAuthentication _auth;
public ActivityRouterDialog(IConfiguration configuration, IHttpContextAccessor httpContextAccessor, ConversationState conversationState, SkillConversationIdFactoryBase conversationIdFactory, SkillHttpClient skillClient, ConcurrentDictionary<string, ContinuationParameters> continuationParametersStore)
public ActivityRouterDialog(BotFrameworkAuthentication auth, IConfiguration configuration, IHttpContextAccessor httpContextAccessor, ConversationState conversationState, SkillConversationIdFactoryBase conversationIdFactory, ConcurrentDictionary<string, ContinuationParameters> continuationParametersStore)
: base(nameof(ActivityRouterDialog))
{
_auth = auth ?? throw new ArgumentNullException(nameof(auth));
AddDialog(new CardDialog(httpContextAccessor));
AddDialog(new MessageWithAttachmentDialog(new Uri($"{httpContextAccessor.HttpContext.Request.Scheme}://{httpContextAccessor.HttpContext.Request.Host.Value}")));
AddDialog(new WaitForProactiveDialog(httpContextAccessor, continuationParametersStore));
@ -45,7 +47,7 @@ namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs
AddDialog(new DeleteDialog());
AddDialog(new UpdateDialog());
AddDialog(CreateEchoSkillDialog(conversationState, conversationIdFactory, skillClient, configuration));
AddDialog(CreateEchoSkillDialog(conversationState, conversationIdFactory, configuration));
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[] { ProcessActivityAsync }));
@ -53,7 +55,7 @@ namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs
InitialDialogId = nameof(WaterfallDialog);
}
private static SkillDialog CreateEchoSkillDialog(ConversationState conversationState, SkillConversationIdFactoryBase conversationIdFactory, SkillHttpClient skillClient, IConfiguration configuration)
private SkillDialog CreateEchoSkillDialog(ConversationState conversationState, SkillConversationIdFactoryBase conversationIdFactory, IConfiguration configuration)
{
var botId = configuration.GetSection(MicrosoftAppCredentials.MicrosoftAppIdKey)?.Value;
@ -69,14 +71,13 @@ namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs
{
BotId = botId,
ConversationIdFactory = conversationIdFactory,
SkillClient = skillClient,
SkillClient = _auth.CreateBotFrameworkClient(),
SkillHostEndpoint = new Uri(skillHostEndpoint),
ConversationState = conversationState,
Skill = skillInfo
};
var echoSkillDialog = new SkillDialog(skillDialogOptions);
var echoSkillDialog = new SkillDialog(skillDialogOptions, _echoSkill);
echoSkillDialog.Id = _echoSkill;
return echoSkillDialog;
}

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

@ -3,12 +3,11 @@
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.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Auth
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.Auth
{
public class AuthDialog : ComponentDialog
{

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

@ -7,7 +7,7 @@ using Microsoft.Bot.Schema;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Cards
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.Cards
{
public static class AdaptiveCardExtensions
{

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

@ -9,7 +9,6 @@ using System.Threading;
using System.Threading.Tasks;
using AdaptiveCards;
using Microsoft.AspNetCore.Http;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Choices;
using Microsoft.Bot.Schema;
@ -17,7 +16,7 @@ using Microsoft.Bot.Schema.Teams;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Cards
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.Cards
{
public class CardDialog : ComponentDialog
{

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

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Cards
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.Cards
{
public enum CardOptions
{

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

@ -8,7 +8,7 @@ using Microsoft.Bot.Schema;
using Microsoft.Bot.Schema.Teams;
using Newtonsoft.Json.Linq;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Cards
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.Cards
{
public static class CardSampleHelper
{

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

@ -4,7 +4,7 @@
using System.Collections.Generic;
using Microsoft.Bot.Connector;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Cards
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.Cards
{
public static class ChannelSupportedCards
{

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

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Cards
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.Cards
{
public class SampleData
{

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

@ -4,11 +4,10 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Delete
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.Delete
{
public class DeleteDialog : ComponentDialog
{

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

@ -5,11 +5,10 @@ using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Schema;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.FileUpload
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.FileUpload
{
public class FileUploadDialog : ComponentDialog
{

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

@ -6,12 +6,11 @@ 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.Builder.Dialogs.Choices;
using Microsoft.Bot.Schema;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.MessageWithAttachment
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.MessageWithAttachment
{
public class MessageWithAttachmentDialog : ComponentDialog
{

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

@ -4,7 +4,7 @@
using System.Security.Principal;
using Microsoft.Bot.Schema;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Proactive
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.Proactive
{
/// <summary>
/// Stores the information needed to resume a conversation when a proactive message arrives.

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

@ -7,11 +7,10 @@ using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Schema;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Proactive
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.Proactive
{
public class WaitForProactiveDialog : Dialog
{

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

@ -5,13 +5,13 @@ using System;
using System.Collections.Generic;
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.Connector.Authentication;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Configuration;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Sso
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.Sso
{
public class SsoSkillDialog : ComponentDialog
{
@ -54,8 +54,9 @@ namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Sso
private async Task<List<Choice>> GetPromptChoicesAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var promptChoices = new List<Choice>();
var adapter = (IUserTokenProvider)stepContext.Context.Adapter;
var token = await adapter.GetUserTokenAsync(stepContext.Context, _connectionName, null, cancellationToken);
var userId = stepContext.Context.Activity?.From?.Id;
var userTokenClient = stepContext.Context.TurnState.Get<UserTokenClient>();
var token = await userTokenClient.GetUserTokenAsync(userId, _connectionName, stepContext.Context.Activity?.ChannelId, null, cancellationToken);
if (token == null)
{
@ -75,6 +76,8 @@ namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Sso
private async Task<DialogTurnResult> HandleActionStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var action = ((FoundChoice)stepContext.Result).Value.ToLowerInvariant();
var userId = stepContext.Context.Activity?.From?.Id;
var userTokenClient = stepContext.Context.TurnState.Get<UserTokenClient>();
switch (action)
{
@ -82,14 +85,12 @@ namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Sso
return await stepContext.BeginDialogAsync(nameof(SsoSkillSignInDialog), null, cancellationToken);
case "logout":
var adapter = (IUserTokenProvider)stepContext.Context.Adapter;
await adapter.SignOutUserAsync(stepContext.Context, _connectionName, cancellationToken: cancellationToken);
await userTokenClient.SignOutUserAsync(userId, _connectionName, stepContext.Context.Activity?.ChannelId, cancellationToken);
await stepContext.Context.SendActivityAsync("You have been signed out.", cancellationToken: cancellationToken);
return await stepContext.NextAsync(cancellationToken: cancellationToken);
case "show token":
var tokenProvider = (IUserTokenProvider)stepContext.Context.Adapter;
var token = await tokenProvider.GetUserTokenAsync(stepContext.Context, _connectionName, null, cancellationToken);
var token = await userTokenClient.GetUserTokenAsync(userId, _connectionName, stepContext.Context.Activity?.ChannelId, null, cancellationToken);
if (token == null)
{
await stepContext.Context.SendActivityAsync("User has no cached token.", cancellationToken: cancellationToken);

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

@ -5,9 +5,8 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Configuration;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Sso
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.Sso
{
public class SsoSkillSignInDialog : ComponentDialog
{

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

@ -4,11 +4,10 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Update
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.Update
{
public class UpdateDialog : ComponentDialog
{

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

@ -6,10 +6,9 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Schema;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Middleware
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Middleware
{
/// <summary>
/// A middleware that ensures conversation state is saved when an OAuthCard is returned by the skill.

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

@ -4,7 +4,7 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot
{
public class Program
{

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

@ -3,15 +3,14 @@
using System;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Middleware;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Builder.TraceExtensions;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Schema;
using Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Middleware;
using Microsoft.Extensions.Logging;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot
{
public class SkillAdapterWithErrorHandler : CloudAdapter
{

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

@ -3,22 +3,22 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.BotFramework;
using Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Bots;
using Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs;
using Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot.Dialogs.Proactive;
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.BotFrameworkFunctionalTests.WaterfallSkillBot.Bots;
using Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs;
using Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot.Dialogs.Proactive;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot
namespace Microsoft.Bot.Builder.FunctionalTestsBots.WaterfallSkillBot
{
public class Startup
{
@ -37,21 +37,33 @@ namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot
services.AddControllers()
.AddNewtonsoftJson();
services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();
// Register AuthConfiguration to enable custom claim validation.
services.AddSingleton(sp =>
{
// AllowedCallers is the setting in the 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 edit the AllowedCallers and add the parent bot's Microsoft app ID to the list.
// In this sample, we allow all callers if AllowedCallers contains an "*".
var callersSection = Configuration.GetSection(CallersConfigKey);
var callers = callersSection.Get<string[]>();
if (callers == null)
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))
{
throw new ArgumentNullException($"\"{CallersConfigKey}\" not found in configuration.");
// 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 = new AllowedCallersClaimsValidator(callers) };
return new AuthenticationConfiguration
{
ClaimsValidator = claimsValidator,
ValidTokenIssuers = validTokenIssuers
};
});
services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>();
@ -65,9 +77,7 @@ namespace Microsoft.BotFrameworkFunctionalTests.WaterfallSkillBot
// Register the skills conversation ID factory, the client and the request handler.
services.AddSingleton<SkillConversationIdFactoryBase, SkillConversationIdFactory>();
services.AddHttpClient<SkillHttpClient>();
services.AddSingleton<ChannelServiceHandler, SkillHandler>();
services.AddSingleton<ChannelServiceHandlerBase, CloudSkillHandler>();
// Create the storage we'll be using for User and Conversation state. (Memory is great for testing purposes.)
services.AddSingleton<IStorage, MemoryStorage>();

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

@ -1,6 +1,8 @@
{
"MicrosoftAppType": "",
"MicrosoftAppId": "",
"MicrosoftAppPassword": "",
"MicrosoftAppTenantId": "",
"ConnectionName": "TestOAuthProvider",
"SsoConnectionName": "",
"ChannelService": "",

1
Bots/JavaScript/.env Normal file
Просмотреть файл

@ -0,0 +1 @@
BotBuilderVersion=~4.15.0

3
Bots/JavaScript/.gitignore поставляемый
Просмотреть файл

@ -1,3 +1,4 @@
# Ignore web.config files generated to deploy JS to IIS on Windows
**/web.config
**/web.config
Utils/temp

17813
Bots/JavaScript/.pnp.cjs сгенерированный Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

249
Bots/JavaScript/.pnp.loader.mjs сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,249 @@
import { URL, fileURLToPath, pathToFileURL } from 'url';
import fs from 'fs';
import path from 'path';
import moduleExports, { Module } from 'module';
var PathType;
(function(PathType2) {
PathType2[PathType2["File"] = 0] = "File";
PathType2[PathType2["Portable"] = 1] = "Portable";
PathType2[PathType2["Native"] = 2] = "Native";
})(PathType || (PathType = {}));
const npath = Object.create(path);
const ppath = Object.create(path.posix);
npath.cwd = () => process.cwd();
ppath.cwd = () => toPortablePath(process.cwd());
ppath.resolve = (...segments) => {
if (segments.length > 0 && ppath.isAbsolute(segments[0])) {
return path.posix.resolve(...segments);
} else {
return path.posix.resolve(ppath.cwd(), ...segments);
}
};
const contains = function(pathUtils, from, to) {
from = pathUtils.normalize(from);
to = pathUtils.normalize(to);
if (from === to)
return `.`;
if (!from.endsWith(pathUtils.sep))
from = from + pathUtils.sep;
if (to.startsWith(from)) {
return to.slice(from.length);
} else {
return null;
}
};
npath.fromPortablePath = fromPortablePath;
npath.toPortablePath = toPortablePath;
npath.contains = (from, to) => contains(npath, from, to);
ppath.contains = (from, to) => contains(ppath, from, to);
const WINDOWS_PATH_REGEXP = /^([a-zA-Z]:.*)$/;
const UNC_WINDOWS_PATH_REGEXP = /^\\\\(\.\\)?(.*)$/;
const PORTABLE_PATH_REGEXP = /^\/([a-zA-Z]:.*)$/;
const UNC_PORTABLE_PATH_REGEXP = /^\/unc\/(\.dot\/)?(.*)$/;
function fromPortablePath(p) {
if (process.platform !== `win32`)
return p;
let portablePathMatch, uncPortablePathMatch;
if (portablePathMatch = p.match(PORTABLE_PATH_REGEXP))
p = portablePathMatch[1];
else if (uncPortablePathMatch = p.match(UNC_PORTABLE_PATH_REGEXP))
p = `\\\\${uncPortablePathMatch[1] ? `.\\` : ``}${uncPortablePathMatch[2]}`;
else
return p;
return p.replace(/\//g, `\\`);
}
function toPortablePath(p) {
if (process.platform !== `win32`)
return p;
let windowsPathMatch, uncWindowsPathMatch;
if (windowsPathMatch = p.match(WINDOWS_PATH_REGEXP))
p = `/${windowsPathMatch[1]}`;
else if (uncWindowsPathMatch = p.match(UNC_WINDOWS_PATH_REGEXP))
p = `/unc/${uncWindowsPathMatch[1] ? `.dot/` : ``}${uncWindowsPathMatch[2]}`;
return p.replace(/\\/g, `/`);
}
const builtinModules = new Set(Module.builtinModules || Object.keys(process.binding(`natives`)));
const isBuiltinModule = (request) => request.startsWith(`node:`) || builtinModules.has(request);
function readPackageScope(checkPath) {
const rootSeparatorIndex = checkPath.indexOf(npath.sep);
let separatorIndex;
do {
separatorIndex = checkPath.lastIndexOf(npath.sep);
checkPath = checkPath.slice(0, separatorIndex);
if (checkPath.endsWith(`${npath.sep}node_modules`))
return false;
const pjson = readPackage(checkPath + npath.sep);
if (pjson) {
return {
data: pjson,
path: checkPath
};
}
} while (separatorIndex > rootSeparatorIndex);
return false;
}
function readPackage(requestPath) {
const jsonPath = npath.resolve(requestPath, `package.json`);
if (!fs.existsSync(jsonPath))
return null;
return JSON.parse(fs.readFileSync(jsonPath, `utf8`));
}
async function tryReadFile(path2) {
try {
return await fs.promises.readFile(path2, `utf8`);
} catch (error) {
if (error.code === `ENOENT`)
return null;
throw error;
}
}
function tryParseURL(str) {
try {
return new URL(str);
} catch {
return null;
}
}
function getFileFormat(filepath) {
var _a;
const ext = path.extname(filepath);
switch (ext) {
case `.mjs`: {
return `module`;
}
case `.cjs`: {
return `commonjs`;
}
case `.wasm`: {
throw new Error(`Unknown file extension ".wasm" for ${filepath}`);
}
case `.json`: {
throw new Error(`Unknown file extension ".json" for ${filepath}`);
}
case `.js`: {
const pkg = readPackageScope(filepath);
if (pkg) {
return (_a = pkg.data.type) != null ? _a : `commonjs`;
}
}
}
return null;
}
async function getFormat$1(resolved, context, defaultGetFormat) {
const url = tryParseURL(resolved);
if ((url == null ? void 0 : url.protocol) !== `file:`)
return defaultGetFormat(resolved, context, defaultGetFormat);
const format = getFileFormat(fileURLToPath(url));
if (format) {
return {
format
};
}
return defaultGetFormat(resolved, context, defaultGetFormat);
}
async function getSource$1(urlString, context, defaultGetSource) {
const url = tryParseURL(urlString);
if ((url == null ? void 0 : url.protocol) !== `file:`)
return defaultGetSource(urlString, context, defaultGetSource);
return {
source: await fs.promises.readFile(fileURLToPath(url), `utf8`)
};
}
async function load$1(urlString, context, defaultLoad) {
const url = tryParseURL(urlString);
if ((url == null ? void 0 : url.protocol) !== `file:`)
return defaultLoad(urlString, context, defaultLoad);
const filePath = fileURLToPath(url);
const format = getFileFormat(filePath);
if (!format)
return defaultLoad(urlString, context, defaultLoad);
return {
format,
source: await fs.promises.readFile(filePath, `utf8`)
};
}
const pathRegExp = /^(?![a-zA-Z]:[\\/]|\\\\|\.{0,2}(?:\/|$))((?:node:)?(?:@[^/]+\/)?[^/]+)\/*(.*|)$/;
async function resolve$1(originalSpecifier, context, defaultResolver) {
var _a;
const {findPnpApi} = moduleExports;
if (!findPnpApi || isBuiltinModule(originalSpecifier))
return defaultResolver(originalSpecifier, context, defaultResolver);
let specifier = originalSpecifier;
const url = tryParseURL(specifier);
if (url) {
if (url.protocol !== `file:`)
return defaultResolver(originalSpecifier, context, defaultResolver);
specifier = fileURLToPath(specifier);
}
const {parentURL, conditions = []} = context;
const issuer = parentURL ? fileURLToPath(parentURL) : process.cwd();
const pnpapi = (_a = findPnpApi(issuer)) != null ? _a : url ? findPnpApi(specifier) : null;
if (!pnpapi)
return defaultResolver(originalSpecifier, context, defaultResolver);
const dependencyNameMatch = specifier.match(pathRegExp);
let allowLegacyResolve = false;
if (dependencyNameMatch) {
const [, dependencyName, subPath] = dependencyNameMatch;
if (subPath === ``) {
const resolved = pnpapi.resolveToUnqualified(`${dependencyName}/package.json`, issuer);
if (resolved) {
const content = await tryReadFile(resolved);
if (content) {
const pkg = JSON.parse(content);
allowLegacyResolve = pkg.exports == null;
}
}
}
}
const result = pnpapi.resolveRequest(specifier, issuer, {
conditions: new Set(conditions),
extensions: allowLegacyResolve ? void 0 : []
});
if (!result)
throw new Error(`Resolving '${specifier}' from '${issuer}' failed`);
return {
url: pathToFileURL(result).href
};
}
const binding = process.binding(`fs`);
const originalfstat = binding.fstat;
const ZIP_FD = 2147483648;
binding.fstat = function(...args) {
const [fd, useBigint, req] = args;
if ((fd & ZIP_FD) !== 0 && useBigint === false && req === void 0) {
try {
const stats = fs.fstatSync(fd);
return new Float64Array([
stats.dev,
stats.mode,
stats.nlink,
stats.uid,
stats.gid,
stats.rdev,
stats.blksize,
stats.ino,
stats.size,
stats.blocks
]);
} catch {
}
}
return originalfstat.apply(this, args);
};
const [major, minor] = process.versions.node.split(`.`).map((value) => parseInt(value, 10));
const hasConsolidatedHooks = major > 16 || major === 16 && minor >= 12;
const resolve = resolve$1;
const getFormat = hasConsolidatedHooks ? void 0 : getFormat$1;
const getSource = hasConsolidatedHooks ? void 0 : getSource$1;
const load = hasConsolidatedHooks ? load$1 : void 0;
export { getFormat, getSource, load, resolve };

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