Merge master into 4.Future
This commit is contained in:
Родитель
10ab9b5bc4
Коммит
02e7a48813
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RuleSet Name="Rules for StyleCop.Analyzers" Description="Source analysis rules treat as errors for BotBuilder-DotNet solution." ToolsVersion="16.0">
|
||||
<Rules AnalyzerId="AsyncUsageAnalyzers" RuleNamespace="AsyncUsageAnalyzers">
|
||||
<Rule Id="AvoidAsyncVoid" Action="Warning" />
|
||||
<Rule Id="AvoidAsyncVoid" Action="Error" />
|
||||
<Rule Id="UseConfigureAwait" Action="Error" />
|
||||
<Rule Id="UseAsyncSuffix" Action="None" />
|
||||
<Rule Id="UseConfigureAwait" Action="Warning" />
|
||||
</Rules>
|
||||
<Rules AnalyzerId="Microsoft.CodeAnalysis.CSharp.Features" RuleNamespace="Microsoft.CodeAnalysis.CSharp.Features">
|
||||
<Rule Id="IDE0003" Action="None" />
|
||||
|
@ -29,4 +29,15 @@
|
|||
<Rule Id="SA1629" Action="None" />
|
||||
<Rule Id="SA1633" Action="None" />
|
||||
</Rules>
|
||||
|
||||
<Rules AnalyzerId="Microsoft.CodeQuality.Analyzers" RuleNamespace="Microsoft.CodeQuality.Analyzers">
|
||||
<Rule Id="CA1054" Action="None" /> <!-- UriParametersShouldNotBeStrings -->
|
||||
<Rule Id="CA1056" Action="None" /> <!-- UriPropertiesShouldNotBeStrings -->
|
||||
<Rule Id="CA1062" Action="None" /> <!-- ValidateArgumentsOfPublicMethods -->
|
||||
<Rule Id="CA2227" Action="None" /> <!-- CollectionPropertiesShouldBeReadOnly -->
|
||||
</Rules>
|
||||
|
||||
<Rules AnalyzerId="Microsoft.NetCore.Analyzers" RuleNamespace="Microsoft.NetCore.Analyzers">
|
||||
<Rule Id="CA1303" Action="None" /> <!-- DoNotPassLiteralsAsLocalizedParameters -->
|
||||
</Rules>
|
||||
</RuleSet>
|
|
@ -9,12 +9,15 @@
|
|||
<ModulePaths>
|
||||
<Include>
|
||||
<ModulePath>.*\Microsoft.Bot.Builder.dll$</ModulePath>
|
||||
<ModulePath>.*\Microsoft.Bot.Builder.Adapters.Twilio.dll$</ModulePath>
|
||||
<ModulePath>.*\Microsoft.Bot.Builder.Teams.dll$</ModulePath>
|
||||
<ModulePath>.*\Microsoft.Bot.Builder.AI.Luis.dll$</ModulePath>
|
||||
<ModulePath>.*\Microsoft.Bot.Builder.AI.QnA.dll$</ModulePath>
|
||||
<ModulePath>.*\Microsoft.Bot.Builder.ApplicationInsights.dll$</ModulePath>
|
||||
<ModulePath>.*\Microsoft.Bot.Builder.Azure.dll$</ModulePath>
|
||||
<ModulePath>.*\Microsoft.Bot.Builder.Configuration.dll$</ModulePath>
|
||||
<ModulePath>.*\Microsoft.Bot.Builder.Connector.dll$</ModulePath>
|
||||
<ModulePath>.*\Microsoft.Bot.Builder.Connector.Teams.dll$</ModulePath>
|
||||
<ModulePath>.*\Microsoft.Bot.Builder.Dialogs.dll$</ModulePath>
|
||||
<ModulePath>.*\Microsoft.Bot.Builder.Integration.ApplicationInsights.Core.dll$</ModulePath>
|
||||
<ModulePath>.*\Microsoft.Bot.Builder.Integration.AspNet.Core.dll$</ModulePath>
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
<PropertyGroup>
|
||||
<!-- Rules found at: https://aka.ms/Microsoft-NuGet-Compliance -->
|
||||
<PackageProjectUrl>https://github.com/Microsoft/botbuilder-dotnet</PackageProjectUrl>
|
||||
<PackageIconUrl>http://docs.botframework.com/images/bot_icon.png</PackageIconUrl>
|
||||
<PackageIconUrl>https://raw.githubusercontent.com/microsoft/botframework-sdk/master/icon.png</PackageIconUrl>
|
||||
<PackageLicenseUrl>https://github.com/Microsoft/BotBuilder/blob/master/LICENSE</PackageLicenseUrl>
|
||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
|
||||
<RepositoryUrl>https://github.com/Microsoft/botbuilder-dotnet</RepositoryUrl>
|
||||
|
@ -52,7 +52,6 @@
|
|||
replace PackageLicenseUrl with PackageLicenseExpression.
|
||||
-->
|
||||
<NoWarn>$(NoWarn);NU5125</NoWarn>
|
||||
<LangVersion>7</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(Configuration) == 'Debug'">
|
||||
<!-- For debug builds, we don't generate documentation. Supress the StyleCop rule that warns about this. -->
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Bot.Builder.FunctionalTests.Configuration
|
||||
{
|
||||
internal static class EnvironmentConfig
|
||||
{
|
||||
public static string TestAppId()
|
||||
{
|
||||
var testAppId = Environment.GetEnvironmentVariable("TESTAPPID");
|
||||
if (string.IsNullOrWhiteSpace(testAppId))
|
||||
{
|
||||
throw new Exception("Environment variable 'TestAppId' not found.");
|
||||
}
|
||||
|
||||
return testAppId;
|
||||
}
|
||||
|
||||
public static string TestAppPassword()
|
||||
{
|
||||
var testPassword = Environment.GetEnvironmentVariable("TESTPASSWORD");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(testPassword))
|
||||
{
|
||||
throw new Exception("Environment variable 'TestPassword' not found.");
|
||||
}
|
||||
|
||||
return testPassword;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Bot.Builder.FunctionalTests.Configuration;
|
||||
using Microsoft.Bot.Connector.Authentication;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
|
@ -20,12 +21,13 @@ namespace Microsoft.Bot.Builder.FunctionalTests
|
|||
|
||||
public GetTokenRefreshTests()
|
||||
{
|
||||
testAppId = EnvironmentConfig.TestAppId();
|
||||
testPassword = EnvironmentConfig.TestAppPassword();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TokenTests_GetCredentialsWorks()
|
||||
{
|
||||
GetEnvironmentVarsTestAppIdPassword();
|
||||
MicrosoftAppCredentials credentials = new MicrosoftAppCredentials(testAppId, testPassword);
|
||||
var result = await credentials.GetTokenAsync();
|
||||
Assert.IsNotNull(result);
|
||||
|
@ -34,7 +36,6 @@ namespace Microsoft.Bot.Builder.FunctionalTests
|
|||
[TestMethod]
|
||||
public async Task TokenTests_RefreshTokenWorks()
|
||||
{
|
||||
GetEnvironmentVarsTestAppIdPassword();
|
||||
MicrosoftAppCredentials credentials = new MicrosoftAppCredentials(testAppId, testPassword);
|
||||
var result = await credentials.GetTokenAsync();
|
||||
Assert.IsNotNull(result);
|
||||
|
@ -48,7 +49,6 @@ namespace Microsoft.Bot.Builder.FunctionalTests
|
|||
[TestMethod]
|
||||
public async Task TokenTests_RefreshTestLoad()
|
||||
{
|
||||
GetEnvironmentVarsTestAppIdPassword();
|
||||
MicrosoftAppCredentials credentials = new MicrosoftAppCredentials(testAppId, testPassword);
|
||||
List<Task<string>> tasks = new List<Task<string>>();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
|
@ -107,24 +107,5 @@ namespace Microsoft.Bot.Builder.FunctionalTests
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GetEnvironmentVarsTestAppIdPassword()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(testAppId) || string.IsNullOrWhiteSpace(testPassword))
|
||||
{
|
||||
testAppId = Environment.GetEnvironmentVariable("TESTAPPID");
|
||||
if (string.IsNullOrWhiteSpace(testAppId))
|
||||
{
|
||||
throw new Exception("Environment variable 'TestAppId' not found.");
|
||||
}
|
||||
|
||||
testPassword = Environment.GetEnvironmentVariable("TESTPASSWORD");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(testPassword))
|
||||
{
|
||||
throw new Exception("Environment variable 'TestPassword' not found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,17 @@ using System.Collections.Generic;
|
|||
using System.Net.Http;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Bot.Builder.FunctionalTests.Configuration;
|
||||
using Microsoft.Bot.Connector.Authentication;
|
||||
using Microsoft.IdentityModel.Protocols;
|
||||
using Xunit;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace Microsoft.Bot.Connector.Tests.Authentication
|
||||
namespace Microsoft.Bot.Builder.FunctionalTests
|
||||
{
|
||||
[TestClass]
|
||||
#if !FUNCTIONALTESTS
|
||||
[Ignore("These integration tests run only when FUNCTIONALTESTS is defined")]
|
||||
#endif
|
||||
public class JwtTokenExtractorTests
|
||||
{
|
||||
private const string KeyId = "CtfQC8Le-8NsC7oC2zQkZpcrfOc";
|
||||
|
@ -34,46 +39,36 @@ namespace Microsoft.Bot.Connector.Tests.Authentication
|
|||
emptyClient = new HttpClient();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public async Task Connector_TokenExtractor_NullRequiredEndorsements_ShouldFail()
|
||||
{
|
||||
var configRetriever = new TestConfigurationRetriever();
|
||||
|
||||
configRetriever.EndorsementTable.Add(KeyId, new HashSet<string>() { RandomEndorsement, ComplianceEndorsement, TestChannelName });
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(async () => await RunTestCase(configRetriever));
|
||||
await RunTestCase(configRetriever);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[TestMethod]
|
||||
public async Task Connector_TokenExtractor_EmptyRequireEndorsements_ShouldValidate()
|
||||
{
|
||||
var configRetriever = new TestConfigurationRetriever();
|
||||
|
||||
configRetriever.EndorsementTable.Add(KeyId, new HashSet<string>() { RandomEndorsement, ComplianceEndorsement, TestChannelName });
|
||||
var claimsIdentity = await RunTestCase(configRetriever, new string[] { });
|
||||
Assert.True(claimsIdentity.IsAuthenticated);
|
||||
Assert.IsTrue(claimsIdentity.IsAuthenticated);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[TestMethod]
|
||||
public async Task Connector_TokenExtractor_RequiredEndorsementsPresent_ShouldValidate()
|
||||
{
|
||||
var configRetriever = new TestConfigurationRetriever();
|
||||
|
||||
configRetriever.EndorsementTable.Add(KeyId, new HashSet<string>() { RandomEndorsement, ComplianceEndorsement, TestChannelName });
|
||||
var claimsIdentity = await RunTestCase(configRetriever, new string[] { ComplianceEndorsement });
|
||||
Assert.True(claimsIdentity.IsAuthenticated);
|
||||
Assert.IsTrue(claimsIdentity.IsAuthenticated);
|
||||
}
|
||||
|
||||
#if broken
|
||||
[Fact]
|
||||
public async Task Connector_TokenExtractor_RequiredEndorsementsPartiallyPresent_ShouldNotValidate()
|
||||
{
|
||||
var configRetriever = new TestConfigurationRetriever();
|
||||
|
||||
configRetriever.EndorsementTable.Add(KeyId, new HashSet<string>() { RandomEndorsement, ComplianceEndorsement, TestChannelName });
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(async () => await RunTestCase(configRetriever, new string[] { ComplianceEndorsement, "notSatisfiedEndorsement" }));
|
||||
}
|
||||
#endif
|
||||
|
||||
private async Task<ClaimsIdentity> RunTestCase(IConfigurationRetriever<IDictionary<string, HashSet<string>>> configRetriever, string[] requiredEndorsements = null)
|
||||
{
|
||||
var tokenExtractor = new JwtTokenExtractor(
|
||||
|
@ -83,7 +78,7 @@ namespace Microsoft.Bot.Connector.Tests.Authentication
|
|||
AuthenticationConstants.AllowedSigningAlgorithms,
|
||||
new ConfigurationManager<IDictionary<string, HashSet<string>>>("http://test", configRetriever));
|
||||
|
||||
string header = $"Bearer {await new MicrosoftAppCredentials("2cd87869-38a0-4182-9251-d056e8f0ac24", "2.30Vs3VQLKt974F").GetTokenAsync()}";
|
||||
string header = $"Bearer {await new MicrosoftAppCredentials(EnvironmentConfig.TestAppId(), EnvironmentConfig.TestAppPassword()).GetTokenAsync()}";
|
||||
|
||||
return await tokenExtractor.GetIdentityAsync(header, "testChannel", requiredEndorsements);
|
||||
}
|
|
@ -1,12 +1,17 @@
|
|||
// 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.FunctionalTests.Configuration;
|
||||
using Microsoft.Bot.Connector.Authentication;
|
||||
using Microsoft.IdentityModel.Protocols;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace Microsoft.Bot.Connector.Tests.Authentication
|
||||
namespace Microsoft.Bot.Builder.FunctionalTests
|
||||
{
|
||||
public class TestConfigurationRetriever : IConfigurationRetriever<IDictionary<string, HashSet<string>>>
|
||||
{
|
|
@ -1,6 +1,6 @@
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29001.49
|
||||
VisualStudioVersion = 16.0.29123.88
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{4269F3C3-6B42-419B-B64A-3E6DC0F1574A}"
|
||||
EndProject
|
||||
|
@ -136,6 +136,32 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Schemas", "Schemas", "{EE56
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.TestBot.Json", "tests\Microsoft.Bot.Builder.TestBot.Json\Microsoft.Bot.Builder.TestBot.Json.csproj", "{2454BBCD-77BC-4E3D-B5A6-3562BED898D6}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.AI.Luis.TestUtils", "tests\Microsoft.Bot.Builder.AI.Luis.TestUtils\Microsoft.Bot.Builder.AI.Luis.TestUtils.csproj", "{685271A8-6C69-46E4-9B11-89AF9761CE0A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.AI.LuisV3", "libraries\Microsoft.Bot.Builder.AI.LuisV3\Microsoft.Bot.Builder.AI.LuisV3.csproj", "{EF46ABF9-0405-4EAA-BC1E-A2BC48DD1A69}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.AI.LuisV3.Tests", "tests\Microsoft.Bot.Builder.Ai.LUISV3.tests\Microsoft.Bot.Builder.AI.LuisV3.Tests.csproj", "{474C57B1-C9FC-4B71-A92B-B25BA27FAFA7}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Adapters", "Adapters", "{6230B915-B238-4E57-AAC4-06B4498F540F}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Adapters.Twilio", "libraries\Adapters\Microsoft.Bot.Builder.Adapters.Twilio\Microsoft.Bot.Builder.Adapters.Twilio.csproj", "{1D1AD39B-EBCF-4960-930E-84246DEF6AAE}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Adapters", "Adapters", "{E8CD434A-306F-41D9-B67D-BFFF3287354D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Adapters.Twilio.Tests", "tests\Adapters\Microsoft.Bot.Builder.Adapters.Twilio.Tests\Microsoft.Bot.Builder.Adapters.Twilio.Tests.csproj", "{6B51F54F-86E8-4FBC-8FDD-3C386E97D0E1}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Schema.Teams", "libraries\Microsoft.Bot.Schema.Teams\Microsoft.Bot.Schema.Teams.csproj", "{1F8ACA9B-7721-4D83-8545-6EE449B3A100}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Connector.Teams", "libraries\Microsoft.Bot.Connector.Teams\Microsoft.Bot.Connector.Teams.csproj", "{630CF216-BA97-4128-8563-214660B153DC}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Teams", "libraries\Microsoft.Bot.Builder.Teams\Microsoft.Bot.Builder.Teams.csproj", "{56230F58-02EF-4C88-9C28-BE37B8A4D074}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Teams.Tests", "tests\Microsoft.Bot.Builder.Teams.Tests\Microsoft.Bot.Builder.Teams.Tests.csproj", "{6EB0FEBB-DB84-44C8-9C5B-FD3D3D187A4F}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Connector.Teams.Tests", "tests\Microsoft.Bot.Connector.Teams.Tests\Microsoft.Bot.Connector.Teams.Tests.csproj", "{D9C6FA68-340F-4338-8158-3BCBF10F320D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Schema.Teams.Tests", "tests\Microsoft.Bot.Schema.Teams.Tests\Microsoft.Bot.Schema.Teams.Tests.csproj", "{A67CCC9B-3B0D-4E3D-A111-6D8A85FAA35B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug - NuGet Packages|Any CPU = Debug - NuGet Packages|Any CPU
|
||||
|
@ -461,6 +487,72 @@ Global
|
|||
{2454BBCD-77BC-4E3D-B5A6-3562BED898D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2454BBCD-77BC-4E3D-B5A6-3562BED898D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2454BBCD-77BC-4E3D-B5A6-3562BED898D6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{685271A8-6C69-46E4-9B11-89AF9761CE0A}.Debug - NuGet Packages|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{685271A8-6C69-46E4-9B11-89AF9761CE0A}.Debug - NuGet Packages|Any CPU.Build.0 = Debug|Any CPU
|
||||
{685271A8-6C69-46E4-9B11-89AF9761CE0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{685271A8-6C69-46E4-9B11-89AF9761CE0A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{685271A8-6C69-46E4-9B11-89AF9761CE0A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{685271A8-6C69-46E4-9B11-89AF9761CE0A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EF46ABF9-0405-4EAA-BC1E-A2BC48DD1A69}.Debug - NuGet Packages|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EF46ABF9-0405-4EAA-BC1E-A2BC48DD1A69}.Debug - NuGet Packages|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EF46ABF9-0405-4EAA-BC1E-A2BC48DD1A69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EF46ABF9-0405-4EAA-BC1E-A2BC48DD1A69}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EF46ABF9-0405-4EAA-BC1E-A2BC48DD1A69}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EF46ABF9-0405-4EAA-BC1E-A2BC48DD1A69}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{474C57B1-C9FC-4B71-A92B-B25BA27FAFA7}.Debug - NuGet Packages|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{474C57B1-C9FC-4B71-A92B-B25BA27FAFA7}.Debug - NuGet Packages|Any CPU.Build.0 = Debug|Any CPU
|
||||
{474C57B1-C9FC-4B71-A92B-B25BA27FAFA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{474C57B1-C9FC-4B71-A92B-B25BA27FAFA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{474C57B1-C9FC-4B71-A92B-B25BA27FAFA7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{474C57B1-C9FC-4B71-A92B-B25BA27FAFA7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1D1AD39B-EBCF-4960-930E-84246DEF6AAE}.Debug - NuGet Packages|Any CPU.ActiveCfg = Debug - NuGet Packages|Any CPU
|
||||
{1D1AD39B-EBCF-4960-930E-84246DEF6AAE}.Debug - NuGet Packages|Any CPU.Build.0 = Debug - NuGet Packages|Any CPU
|
||||
{1D1AD39B-EBCF-4960-930E-84246DEF6AAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1D1AD39B-EBCF-4960-930E-84246DEF6AAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1D1AD39B-EBCF-4960-930E-84246DEF6AAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1D1AD39B-EBCF-4960-930E-84246DEF6AAE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6B51F54F-86E8-4FBC-8FDD-3C386E97D0E1}.Debug - NuGet Packages|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6B51F54F-86E8-4FBC-8FDD-3C386E97D0E1}.Debug - NuGet Packages|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6B51F54F-86E8-4FBC-8FDD-3C386E97D0E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6B51F54F-86E8-4FBC-8FDD-3C386E97D0E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6B51F54F-86E8-4FBC-8FDD-3C386E97D0E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6B51F54F-86E8-4FBC-8FDD-3C386E97D0E1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1F8ACA9B-7721-4D83-8545-6EE449B3A100}.Debug - NuGet Packages|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1F8ACA9B-7721-4D83-8545-6EE449B3A100}.Debug - NuGet Packages|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1F8ACA9B-7721-4D83-8545-6EE449B3A100}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1F8ACA9B-7721-4D83-8545-6EE449B3A100}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1F8ACA9B-7721-4D83-8545-6EE449B3A100}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1F8ACA9B-7721-4D83-8545-6EE449B3A100}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{630CF216-BA97-4128-8563-214660B153DC}.Debug - NuGet Packages|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{630CF216-BA97-4128-8563-214660B153DC}.Debug - NuGet Packages|Any CPU.Build.0 = Debug|Any CPU
|
||||
{630CF216-BA97-4128-8563-214660B153DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{630CF216-BA97-4128-8563-214660B153DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{630CF216-BA97-4128-8563-214660B153DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{630CF216-BA97-4128-8563-214660B153DC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{56230F58-02EF-4C88-9C28-BE37B8A4D074}.Debug - NuGet Packages|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{56230F58-02EF-4C88-9C28-BE37B8A4D074}.Debug - NuGet Packages|Any CPU.Build.0 = Debug|Any CPU
|
||||
{56230F58-02EF-4C88-9C28-BE37B8A4D074}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{56230F58-02EF-4C88-9C28-BE37B8A4D074}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{56230F58-02EF-4C88-9C28-BE37B8A4D074}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{56230F58-02EF-4C88-9C28-BE37B8A4D074}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6EB0FEBB-DB84-44C8-9C5B-FD3D3D187A4F}.Debug - NuGet Packages|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6EB0FEBB-DB84-44C8-9C5B-FD3D3D187A4F}.Debug - NuGet Packages|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6EB0FEBB-DB84-44C8-9C5B-FD3D3D187A4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6EB0FEBB-DB84-44C8-9C5B-FD3D3D187A4F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6EB0FEBB-DB84-44C8-9C5B-FD3D3D187A4F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6EB0FEBB-DB84-44C8-9C5B-FD3D3D187A4F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D9C6FA68-340F-4338-8158-3BCBF10F320D}.Debug - NuGet Packages|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D9C6FA68-340F-4338-8158-3BCBF10F320D}.Debug - NuGet Packages|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D9C6FA68-340F-4338-8158-3BCBF10F320D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D9C6FA68-340F-4338-8158-3BCBF10F320D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D9C6FA68-340F-4338-8158-3BCBF10F320D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D9C6FA68-340F-4338-8158-3BCBF10F320D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A67CCC9B-3B0D-4E3D-A111-6D8A85FAA35B}.Debug - NuGet Packages|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A67CCC9B-3B0D-4E3D-A111-6D8A85FAA35B}.Debug - NuGet Packages|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A67CCC9B-3B0D-4E3D-A111-6D8A85FAA35B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A67CCC9B-3B0D-4E3D-A111-6D8A85FAA35B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A67CCC9B-3B0D-4E3D-A111-6D8A85FAA35B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A67CCC9B-3B0D-4E3D-A111-6D8A85FAA35B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -522,6 +614,19 @@ Global
|
|||
{B7DB6561-7D83-4706-9DE0-02C5520DE2B7} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
|
||||
{BDCAC491-8518-4CE6-B56A-9E36936C7E70} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
|
||||
{2454BBCD-77BC-4E3D-B5A6-3562BED898D6} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
|
||||
{685271A8-6C69-46E4-9B11-89AF9761CE0A} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
|
||||
{EF46ABF9-0405-4EAA-BC1E-A2BC48DD1A69} = {763168FA-A590-482C-84D8-2922F7ADB1A2}
|
||||
{474C57B1-C9FC-4B71-A92B-B25BA27FAFA7} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
|
||||
{6230B915-B238-4E57-AAC4-06B4498F540F} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
|
||||
{1D1AD39B-EBCF-4960-930E-84246DEF6AAE} = {6230B915-B238-4E57-AAC4-06B4498F540F}
|
||||
{E8CD434A-306F-41D9-B67D-BFFF3287354D} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
|
||||
{6B51F54F-86E8-4FBC-8FDD-3C386E97D0E1} = {E8CD434A-306F-41D9-B67D-BFFF3287354D}
|
||||
{1F8ACA9B-7721-4D83-8545-6EE449B3A100} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
|
||||
{630CF216-BA97-4128-8563-214660B153DC} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
|
||||
{56230F58-02EF-4C88-9C28-BE37B8A4D074} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
|
||||
{6EB0FEBB-DB84-44C8-9C5B-FD3D3D187A4F} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
|
||||
{D9C6FA68-340F-4338-8158-3BCBF10F320D} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
|
||||
{A67CCC9B-3B0D-4E3D-A111-6D8A85FAA35B} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {7173C9F3-A7F9-496E-9078-9156E35D6E16}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<Project>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
|
||||
</Project>
|
|
@ -0,0 +1,34 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Version Condition=" '$(PackageVersion)' == '' ">4.0.0-local</Version>
|
||||
<Version Condition=" '$(PackageVersion)' != '' ">$(PackageVersion)</Version>
|
||||
<PackageVersion Condition=" '$(PackageVersion)' == '' ">4.0.0-local</PackageVersion>
|
||||
<PackageVersion Condition=" '$(PackageVersion)' != '' ">$(PackageVersion)</PackageVersion>
|
||||
<Configurations>Debug;Release;Debug - NuGet Packages</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<!-- The Twilio package isn't signed, so supress the warning. There seems to not be a way to supress this for ONLY Twilio. -->
|
||||
<NoWarn>$(NoWarn),CS8002</NoWarn>
|
||||
<DefineConstants></DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU' ">
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<DelaySign>true</DelaySign>
|
||||
<AssemblyOriginatorKeyFile>..\..\..\build\35MSSharedLib1024.snk</AssemblyOriginatorKeyFile>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<DocumentationFile>bin\$(Configuration)\netstandard2.0\Microsoft.Bot.Builder.Adapters.Twilio.xml</DocumentationFile>
|
||||
<DefineConstants>SIGNASSEMBLY</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Twilio" Version="5.31.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\integration\Microsoft.Bot.Builder.Integration.AspNet.Core\Microsoft.Bot.Builder.Integration.AspNet.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,209 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Bot.Schema;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Adapters.Twilio
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="BotAdapter"/> that can connect to Twilio's SMS service.
|
||||
/// </summary>
|
||||
public class TwilioAdapter : BotAdapter
|
||||
{
|
||||
private readonly TwilioAdapterOptions _options;
|
||||
|
||||
private readonly TwilioClientWrapper _twilioClient;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TwilioAdapter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="options">The options to use to authenticate the bot with the Twilio service.</param>
|
||||
/// <param name="twilioClient">The Twilio client to connect to.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="options"/> is <c>null</c>.</exception>
|
||||
public TwilioAdapter(TwilioAdapterOptions options, TwilioClientWrapper twilioClient)
|
||||
{
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(options.TwilioNumber))
|
||||
{
|
||||
throw new ArgumentException("TwilioNumber is a required part of the configuration.", nameof(options));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(options.AccountSid))
|
||||
{
|
||||
throw new ArgumentException("AccountSid is a required part of the configuration.", nameof(options));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(options.AuthToken))
|
||||
{
|
||||
throw new ArgumentException("AuthToken is a required part of the configuration.", nameof(options));
|
||||
}
|
||||
|
||||
_twilioClient = twilioClient ?? throw new ArgumentNullException(nameof(twilioClient));
|
||||
_options = options;
|
||||
|
||||
_twilioClient.LogIn(_options.AccountSid, _options.AuthToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends activities to the conversation.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for the turn.</param>
|
||||
/// <param name="activities">The activities to send.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>If the activities are successfully sent, the task result contains
|
||||
/// an array of <see cref="ResourceResponse"/> objects containing the SIDs that
|
||||
/// Twilio assigned to the activities.</remarks>
|
||||
/// <seealso cref="ITurnContext.OnSendActivities(SendActivitiesHandler)"/>
|
||||
public override async Task<ResourceResponse[]> SendActivitiesAsync(ITurnContext turnContext, Activity[] activities, CancellationToken cancellationToken)
|
||||
{
|
||||
var responses = new List<ResourceResponse>();
|
||||
foreach (var activity in activities)
|
||||
{
|
||||
if (activity.Type == ActivityTypes.Message)
|
||||
{
|
||||
var messageOptions = TwilioHelper.ActivityToTwilio(activity, _options.TwilioNumber);
|
||||
|
||||
var res = await _twilioClient.SendMessage(messageOptions).ConfigureAwait(false);
|
||||
|
||||
var response = new ResourceResponse()
|
||||
{
|
||||
Id = res,
|
||||
};
|
||||
|
||||
responses.Add(response);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Unknown message type of Activity.", nameof(activities));
|
||||
}
|
||||
}
|
||||
|
||||
return responses.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a turn context and runs the middleware pipeline for an incoming activity.
|
||||
/// </summary>
|
||||
/// <param name="httpRequest">The incoming HTTP request.</param>
|
||||
/// <param name="httpResponse">When this method completes, the HTTP response to send.</param>
|
||||
/// <param name="bot">The bot that will handle the incoming activity.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="httpRequest"/>,
|
||||
/// <paramref name="httpResponse"/>, or <paramref name="bot"/> is <c>null</c>.</exception>
|
||||
public async Task ProcessAsync(HttpRequest httpRequest, HttpResponse httpResponse, IBot bot, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (httpRequest == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(httpRequest));
|
||||
}
|
||||
|
||||
if (httpResponse == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(httpResponse));
|
||||
}
|
||||
|
||||
if (bot == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(bot));
|
||||
}
|
||||
|
||||
var activity = TwilioHelper.RequestToActivity(httpRequest, _options.ValidationUrl, _options.AuthToken);
|
||||
|
||||
// create a conversation reference
|
||||
using (var context = new TurnContext(this, activity))
|
||||
{
|
||||
context.TurnState.Add("httpStatus", HttpStatusCode.OK.ToString("D"));
|
||||
await RunPipelineAsync(context, bot.OnTurnAsync, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
httpResponse.StatusCode = Convert.ToInt32(context.TurnState.Get<string>("httpStatus"), CultureInfo.InvariantCulture);
|
||||
httpResponse.ContentType = "text/plain";
|
||||
var text = context.TurnState.Get<object>("httpBody") != null ? context.TurnState.Get<object>("httpBody").ToString() : string.Empty;
|
||||
|
||||
await httpResponse.WriteAsync(text, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces an existing activity in the conversation.
|
||||
/// Twilio SMS does not support this operation.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for the turn.</param>
|
||||
/// <param name="activity">New replacement activity.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>This method always returns a faulted task.</remarks>
|
||||
/// <seealso cref="ITurnContext.OnUpdateActivity(UpdateActivityHandler)"/>
|
||||
public override Task<ResourceResponse> UpdateActivityAsync(ITurnContext turnContext, Activity activity, CancellationToken cancellationToken)
|
||||
{
|
||||
// Twilio adapter does not support updateActivity.
|
||||
return Task.FromException<ResourceResponse>(new NotSupportedException("Twilio SMS does not support updating activities."));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes an existing activity in the conversation.
|
||||
/// Twilio SMS does not support this operation.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for the turn.</param>
|
||||
/// <param name="reference">Conversation reference for the activity to delete.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>This method always returns a faulted task.</remarks>
|
||||
/// <seealso cref="ITurnContext.OnDeleteActivity(DeleteActivityHandler)"/>
|
||||
public override Task DeleteActivityAsync(ITurnContext turnContext, ConversationReference reference, CancellationToken cancellationToken)
|
||||
{
|
||||
// Twilio adapter does not support deleteActivity.
|
||||
return Task.FromException<ResourceResponse>(new NotSupportedException("Twilio SMS does not support deleting activities."));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a proactive message to a conversation.
|
||||
/// </summary>
|
||||
/// <param name="reference">A reference to the conversation to continue.</param>
|
||||
/// <param name="logic">The method to call for the resulting bot turn.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>Call this method to proactively send a message to a conversation.
|
||||
/// Most channels require a user to initiate a conversation with a bot
|
||||
/// before the bot can send activities to the user.</remarks>
|
||||
/// <seealso cref="BotAdapter.RunPipelineAsync(ITurnContext, BotCallbackHandler, CancellationToken)"/>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="reference"/> or
|
||||
/// <paramref name="logic"/> is <c>null</c>.</exception>
|
||||
public async Task ContinueConversationAsync(ConversationReference reference, BotCallbackHandler logic, CancellationToken cancellationToken)
|
||||
{
|
||||
if (reference == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(reference));
|
||||
}
|
||||
|
||||
if (logic == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(logic));
|
||||
}
|
||||
|
||||
var request = reference.GetContinuationActivity().ApplyConversationReference(reference, true);
|
||||
|
||||
using (var context = new TurnContext(this, request))
|
||||
{
|
||||
await RunPipelineAsync(context, logic, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright(c) Microsoft Corporation.All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
namespace Microsoft.Bot.Builder.Adapters.Twilio
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines values that a <see cref="TwilioAdapter"/> can use to connect to Twilio's SMS service.
|
||||
/// </summary>
|
||||
public class TwilioAdapterOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TwilioAdapterOptions"/> class.
|
||||
/// </summary>
|
||||
/// <param name="twilioNumber">The twilio phone number.</param>
|
||||
/// <param name="accountSid">The account id.</param>
|
||||
/// <param name="authToken">The authentication token.</param>
|
||||
/// <param name="validationUrl">validation URL for incoming requests.</param>
|
||||
public TwilioAdapterOptions(string twilioNumber, string accountSid, string authToken, string validationUrl = null)
|
||||
{
|
||||
TwilioNumber = twilioNumber;
|
||||
AccountSid = accountSid;
|
||||
AuthToken = authToken;
|
||||
ValidationUrl = validationUrl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the phone number associated with this Twilio app.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The phone number, in the format 1XXXYYYZZZZ.
|
||||
/// </value>
|
||||
public string TwilioNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the account SID from the Twilio account.
|
||||
/// </summary>
|
||||
/// <value>The account SID.</value>
|
||||
public string AccountSid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the API auth token associated with the Twilio account.
|
||||
/// </summary>
|
||||
/// <value>The authentication token.</value>
|
||||
public string AuthToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an optional validation URL.
|
||||
/// </summary>
|
||||
/// <value>Optional validation URL to override the automatically generated URL signature used
|
||||
/// to validate incoming requests. See the Twilio security documentation on
|
||||
/// [validating requests](https://www.twilio.com/docs/usage/security#validating-requests).</value>
|
||||
public string ValidationUrl { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) Microsoft Corporation.All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Twilio;
|
||||
using Twilio.Rest.Api.V2010.Account;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Adapters.Twilio
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper class for the Twilio API.
|
||||
/// </summary>
|
||||
public class TwilioClientWrapper
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes the Twilio client with a user name and password.
|
||||
/// </summary>
|
||||
/// <param name="username">The user name for the Twilio API.</param>
|
||||
/// <param name="password">The password for the Twilio API.</param>
|
||||
public virtual void LogIn(string username, string password)
|
||||
{
|
||||
TwilioClient.Init(username, password);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a Twilio SMS message.
|
||||
/// </summary>
|
||||
/// <param name="messageOptions">An object containing the parameters for the message to send.</param>
|
||||
/// <returns>The SID of the Twilio message sent.</returns>
|
||||
public virtual async Task<string> SendMessage(CreateMessageOptions messageOptions)
|
||||
{
|
||||
var messageResource = await MessageResource.CreateAsync((CreateMessageOptions)messageOptions).ConfigureAwait(false);
|
||||
return messageResource.Sid;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Bot.Connector;
|
||||
using Microsoft.Bot.Schema;
|
||||
using Newtonsoft.Json;
|
||||
using Twilio.Exceptions;
|
||||
using Twilio.Rest.Api.V2010.Account;
|
||||
using Twilio.Security;
|
||||
|
||||
using AuthenticationException = System.Security.Authentication.AuthenticationException;
|
||||
|
||||
#if SIGNASSEMBLY
|
||||
[assembly: InternalsVisibleTo("Microsoft.Bot.Builder.Adapters.Twilio.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
|
||||
#else
|
||||
[assembly: InternalsVisibleTo("Microsoft.Bot.Builder.Adapters.Twilio.Tests")]
|
||||
#endif
|
||||
|
||||
namespace Microsoft.Bot.Builder.Adapters.Twilio
|
||||
{
|
||||
/// <summary>
|
||||
/// A helper class to create Activities and Twilio messages.
|
||||
/// </summary>
|
||||
internal static class TwilioHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates Twilio SMS message options object from a Bot Framework <see cref="Activity"/>.
|
||||
/// </summary>
|
||||
/// <param name="activity">The activity.</param>
|
||||
/// <param name="twilioNumber">The Twilio phone number assigned to the bot.</param>
|
||||
/// <returns>The Twilio message options object.</returns>
|
||||
/// <seealso cref="TwilioAdapter.SendActivitiesAsync(ITurnContext, Activity[], System.Threading.CancellationToken)"/>
|
||||
public static CreateMessageOptions ActivityToTwilio(Activity activity, string twilioNumber)
|
||||
{
|
||||
if (activity == null || string.IsNullOrWhiteSpace(twilioNumber))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var mediaUrls = new List<Uri>();
|
||||
if (activity.Attachments != null)
|
||||
{
|
||||
mediaUrls.AddRange(activity.Attachments.Select(attachment => new Uri(attachment.ContentUrl)));
|
||||
}
|
||||
|
||||
var messageOptions = new CreateMessageOptions(activity.Conversation.Id)
|
||||
{
|
||||
ApplicationSid = activity.Conversation.Id,
|
||||
From = twilioNumber,
|
||||
Body = activity.Text,
|
||||
MediaUrl = mediaUrls,
|
||||
};
|
||||
|
||||
return messageOptions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Bot Framework <see cref="Activity"/> from an HTTP request that contains a Twilio message.
|
||||
/// </summary>
|
||||
/// <param name="httpRequest">The HTTP request.</param>
|
||||
/// <param name="validationUrl">Optional validation URL to override the automatically
|
||||
/// generated URL signature used to validate incoming requests.</param>
|
||||
/// <param name="authToken">The authentication token for the Twilio app.</param>
|
||||
/// <returns>The activity object.</returns>
|
||||
/// <seealso cref="TwilioAdapter.ProcessAsync(HttpRequest, HttpResponse, IBot, System.Threading.CancellationToken)"/>
|
||||
/// <seealso cref="ITwilioAdapterOptions.ValidationUrl"/>
|
||||
public static Activity RequestToActivity(HttpRequest httpRequest, string validationUrl, string authToken)
|
||||
{
|
||||
if (httpRequest == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Dictionary<string, string> body;
|
||||
using (var bodyStream = new StreamReader(httpRequest.Body))
|
||||
{
|
||||
body = QueryStringToDictionary(bodyStream.ReadToEnd());
|
||||
}
|
||||
|
||||
ValidateRequest(httpRequest, body, validationUrl, authToken);
|
||||
|
||||
var twilioMessage = JsonConvert.DeserializeObject<TwilioMessage>(JsonConvert.SerializeObject(body));
|
||||
|
||||
return new Activity()
|
||||
{
|
||||
Id = twilioMessage.MessageSid,
|
||||
Timestamp = DateTime.UtcNow,
|
||||
ChannelId = Channels.Twilio,
|
||||
Conversation = new ConversationAccount()
|
||||
{
|
||||
Id = twilioMessage.From ?? twilioMessage.Author,
|
||||
},
|
||||
From = new ChannelAccount()
|
||||
{
|
||||
Id = twilioMessage.From ?? twilioMessage.Author,
|
||||
},
|
||||
Recipient = new ChannelAccount()
|
||||
{
|
||||
Id = twilioMessage.To,
|
||||
},
|
||||
Text = twilioMessage.Body,
|
||||
ChannelData = twilioMessage,
|
||||
Type = ActivityTypes.Message,
|
||||
Attachments = int.TryParse(twilioMessage.NumMedia, out var numMediaResult) && numMediaResult > 0 ? GetMessageAttachments(numMediaResult, body) : null,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates an HTTP request as coming from Twilio.
|
||||
/// </summary>
|
||||
/// <param name="httpRequest">The request to validate.</param>
|
||||
/// <param name="body">The request payload, as key-value pairs.</param>
|
||||
/// <param name="validationUrl">Optional validation URL to override the automatically
|
||||
/// generated URL signature used to validate incoming requests.</param>
|
||||
/// <param name="authToken">The authentication token for the Twilio app.</param>
|
||||
/// <exception cref="AuthenticationException">Validation failed.</exception>
|
||||
private static void ValidateRequest(HttpRequest httpRequest, Dictionary<string, string> body, string validationUrl, string authToken)
|
||||
{
|
||||
var twilioSignature = httpRequest.Headers["x-twilio-signature"];
|
||||
validationUrl = validationUrl ?? (httpRequest.Headers["x-forwarded-proto"][0] ?? httpRequest.Protocol + "://" + httpRequest.Host + httpRequest.Path);
|
||||
var requestValidator = new RequestValidator(authToken);
|
||||
if (!requestValidator.Validate(validationUrl, body, twilioSignature))
|
||||
{
|
||||
throw new AuthenticationException("Request does not match provided signature");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets attachments from a Twilio message.
|
||||
/// </summary>
|
||||
/// <param name="numMedia">The number of media items to pull from the message body.</param>
|
||||
/// <param name="message">A dictionary containing the Twilio message elements.</param>
|
||||
/// <returns>An Attachments array with the converted attachments.</returns>
|
||||
private static List<Attachment> GetMessageAttachments(int numMedia, Dictionary<string, string> message)
|
||||
{
|
||||
var attachments = new List<Attachment>();
|
||||
for (var i = 0; i < numMedia; i++)
|
||||
{
|
||||
// Ensure MediaContentType and MediaUrl are present before adding the attachment
|
||||
if (message.ContainsKey($"MediaContentType{i}") && message.ContainsKey($"MediaUrl{i}"))
|
||||
{
|
||||
var attachment = new Attachment()
|
||||
{
|
||||
ContentType = message[$"MediaContentType{i}"],
|
||||
ContentUrl = message[$"MediaUrl{i}"],
|
||||
};
|
||||
attachments.Add(attachment);
|
||||
}
|
||||
}
|
||||
|
||||
return attachments;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a query string to a dictionary with key-value pairs.
|
||||
/// </summary>
|
||||
/// <param name="query">The query string to convert.</param>
|
||||
/// <returns>A dictionary with the query values.</returns>
|
||||
private static Dictionary<string, string> QueryStringToDictionary(string query)
|
||||
{
|
||||
var values = new Dictionary<string, string>();
|
||||
if (string.IsNullOrWhiteSpace(query))
|
||||
{
|
||||
return values;
|
||||
}
|
||||
|
||||
var pairs = query.Replace("+", "%20").Split('&');
|
||||
|
||||
foreach (var p in pairs)
|
||||
{
|
||||
var pair = p.Split('=');
|
||||
var key = pair[0];
|
||||
var value = Uri.UnescapeDataString(pair[1]);
|
||||
|
||||
values.Add(key, value);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
// Copyright(c) Microsoft Corporation.All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Adapters.Twilio
|
||||
{
|
||||
/// <summary>
|
||||
/// A class wrapping Twilio request parameters.
|
||||
/// </summary>
|
||||
/// <remarks>These parameters can be included in an HTTP request that contains a Twilio message.</remarks>
|
||||
public class TwilioMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the Author of the message.
|
||||
/// </summary>
|
||||
/// <value>The Author of the message.</value>
|
||||
public string Author { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the receiver's country.
|
||||
/// </summary>
|
||||
/// <value>The receiver's country, such as "US".</value>
|
||||
public string ToCountry { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sender's country.
|
||||
/// </summary>
|
||||
/// <value>The sender's country, such as "US".</value>
|
||||
public string FromCountry { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the receiver's state or province.
|
||||
/// </summary>
|
||||
/// <value>The receiver's state or province, such as "NY".</value>
|
||||
public string ToState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the `sms_id` found in the response of a phone verification start.
|
||||
/// </summary>
|
||||
/// <value>The`sms_id` found in the response of a phone verification start.</value>
|
||||
public string SmsMessageSid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of media files associated with the message.
|
||||
/// </summary>
|
||||
/// <value>The number of media files associated with the message.</value>
|
||||
/// <remarks>A message can include up to 10 media files.</remarks>
|
||||
public string NumMedia { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the URLs referencing the media content included with the message, if any.
|
||||
/// </summary>
|
||||
/// <value>URLs referencing the media content included with the message.</value>
|
||||
public List<Uri> MediaUrls { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content types for the media included with the message, if any.
|
||||
/// </summary>
|
||||
/// <value>The content types for the media included with the message.</value>
|
||||
public List<string> MediaContentTypes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the receiver's city.
|
||||
/// </summary>
|
||||
/// <value>The receiver's city, such as "FARMINGDALE".</value>
|
||||
public string ToCity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sender's postal code.
|
||||
/// </summary>
|
||||
/// <value>The sender's postal code. </value>
|
||||
public string FromZip { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the SMS security identifier.
|
||||
/// </summary>
|
||||
/// <value>The SMS message security identifier.</value>
|
||||
/// <remarks>Same as the <see cref="MessageSid"/>.</remarks>
|
||||
public string SmsSid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sender's state or province.
|
||||
/// </summary>
|
||||
/// <value>The sender's state or province, such as "NY".</value>
|
||||
public string FromState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the status of the message.
|
||||
/// </summary>
|
||||
/// <value>The status of the message, such as "received".</value>
|
||||
/// <remarks>See [message status values](https://aka.ms/twilio-message-status-values)
|
||||
/// for a list of the possible values.</remarks>
|
||||
public string SmsStatus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sender's city.
|
||||
/// </summary>
|
||||
/// <value>The sender's city, such as "FARMINGDALE".</value>
|
||||
public string FromCity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the message text.
|
||||
/// </summary>
|
||||
/// <value>The message text. Can be up to 1,600 characters long.</value>
|
||||
public string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the phone number in E.164 format that received the message.
|
||||
/// </summary>
|
||||
/// <value>The phone number in E.164 format that received the message.</value>
|
||||
public string To { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the recipient's postal code.
|
||||
/// </summary>
|
||||
/// <value>The recipient's postal code.</value>
|
||||
public string ToZip { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of segments that make up the complete message.
|
||||
/// </summary>
|
||||
/// <value>The number of segments that make up the complete message.</value>
|
||||
public string NumSegments { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the security identifier of the message.
|
||||
/// </summary>
|
||||
/// <value>The security identifier of the message.</value>
|
||||
/// <remarks>For more information, see [Security Identifier (SID)](https://aka.ms/twilio-sid).
|
||||
/// </remarks>
|
||||
public string MessageSid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Sid of the Account that sent the message that created the resource.
|
||||
/// </summary>
|
||||
/// <value>The security identifier of the Account that sent the message.</value>
|
||||
public string AccountSid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sender phone number.
|
||||
/// </summary>
|
||||
/// <value>The phone number (in E.164 format), alphanumeric sender ID, or Wireless SIM that initiated the message.</value>
|
||||
public string From { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the API version used to process the message.
|
||||
/// </summary>
|
||||
/// <value>The API version used to process the message.</value>
|
||||
public string ApiVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the event type for using with Twilio Conversation API.
|
||||
/// </summary>
|
||||
/// <value>The type of event, e.g. "onMessageAdd", "onMessageAdded", "onConversationAdd".</value>
|
||||
public string EventType { get; set; }
|
||||
}
|
||||
}
|
|
@ -50,7 +50,7 @@ namespace Microsoft.Bot.Builder.AI.Luis
|
|||
/// <item>datetime -- combination of date and time like "march 23 2pm".</item>
|
||||
/// <item>timerange -- a range of time like "2pm to 4pm".</item>
|
||||
/// <item>daterange -- a range of dates like "march 23rd to 24th".</item>
|
||||
/// <item>datetimerang -- a range of dates and times like "july 3rd 2pm to 5th 4pm".</item>
|
||||
/// <item>datetimerange -- a range of dates and times like "july 3rd 2pm to 5th 4pm".</item>
|
||||
/// <item>set -- a recurrence like "every monday".</item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
|
|
|
@ -32,9 +32,9 @@ namespace Microsoft.Bot.Builder.AI.Luis
|
|||
/// <param name="telemetryMetrics">Additional metrics to be logged to telemetry with the LuisResult event.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
|
||||
/// <returns>The LUIS results of the analysis of the current message text in the current turn's context activity.</returns>
|
||||
Task<RecognizerResult> RecognizeAsync(ITurnContext turnContext, Dictionary<string, string> telemetryProperties, Dictionary<string, double> telemetryMetrics, CancellationToken cancellationToken = default(CancellationToken));
|
||||
Task<RecognizerResult> RecognizeAsync(ITurnContext turnContext, Dictionary<string, string> telemetryProperties, Dictionary<string, double> telemetryMetrics, CancellationToken cancellationToken = default);
|
||||
|
||||
Task<T> RecognizeAsync<T>(ITurnContext turnContext, Dictionary<string, string> telemetryProperties, Dictionary<string, double> telemetryMetrics, CancellationToken cancellationToken = default(CancellationToken))
|
||||
Task<T> RecognizeAsync<T>(ITurnContext turnContext, Dictionary<string, string> telemetryProperties, Dictionary<string, double> telemetryMetrics, CancellationToken cancellationToken = default)
|
||||
where T : IRecognizerConvert, new();
|
||||
|
||||
/// <summary>
|
||||
|
@ -44,7 +44,7 @@ namespace Microsoft.Bot.Builder.AI.Luis
|
|||
/// <param name="turnContext">Turn context.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>Analysis of utterance.</returns>
|
||||
new Task<T> RecognizeAsync<T>(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
|
||||
new Task<T> RecognizeAsync<T>(ITurnContext turnContext, CancellationToken cancellationToken = default)
|
||||
where T : IRecognizerConvert, new();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ namespace Microsoft.Bot.Builder.AI.Luis
|
|||
{
|
||||
var (applicationId, endpointKey, endpoint) = props;
|
||||
|
||||
if (!Guid.TryParse(applicationId, out var appGuid))
|
||||
if (!Guid.TryParse(applicationId, out var _))
|
||||
{
|
||||
throw new ArgumentException($"\"{applicationId}\" is not a valid LUIS application id.");
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace Microsoft.Bot.Builder.AI.Luis
|
|||
{
|
||||
internal class LuisDelegatingHandler : DelegatingHandler
|
||||
{
|
||||
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
// Bot Builder Package name and version.
|
||||
var assemblyName = this.GetType().Assembly.GetName();
|
||||
|
|
|
@ -360,7 +360,7 @@ namespace Microsoft.Bot.Builder.AI.Luis
|
|||
Text = utterance,
|
||||
AlteredText = luisResult.AlteredQuery,
|
||||
Intents = LuisUtil.GetIntents(luisResult),
|
||||
Entities = LuisUtil.ExtractEntitiesAndMetadata(luisResult.Entities, luisResult.CompositeEntities, luisPredictionOptions.IncludeInstanceData ?? true),
|
||||
Entities = LuisUtil.ExtractEntitiesAndMetadata(luisResult.Entities, luisResult.CompositeEntities, luisPredictionOptions.IncludeInstanceData ?? true, utterance),
|
||||
};
|
||||
LuisUtil.AddProperties(luisResult, recognizerResult);
|
||||
if (_includeApiResults)
|
||||
|
@ -428,13 +428,6 @@ namespace Microsoft.Bot.Builder.AI.Luis
|
|||
return currentHandler;
|
||||
}
|
||||
|
||||
private HttpClientHandler CreateRootHandler() =>
|
||||
|
||||
// Create our root handler
|
||||
#if FullNetFx
|
||||
return new WebRequestHandler();
|
||||
#else
|
||||
new HttpClientHandler();
|
||||
#endif
|
||||
private HttpClientHandler CreateRootHandler() => new HttpClientHandler();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace Microsoft.Bot.Builder.AI.Luis
|
|||
}
|
||||
}
|
||||
|
||||
internal static JObject ExtractEntitiesAndMetadata(IList<EntityModel> entities, IList<CompositeEntityModel> compositeEntities, bool verbose)
|
||||
internal static JObject ExtractEntitiesAndMetadata(IList<EntityModel> entities, IList<CompositeEntityModel> compositeEntities, bool verbose, string utterance)
|
||||
{
|
||||
var entitiesAndMetadata = new JObject();
|
||||
if (verbose)
|
||||
|
@ -49,7 +49,7 @@ namespace Microsoft.Bot.Builder.AI.Luis
|
|||
if (compositeEntities != null && compositeEntities.Any())
|
||||
{
|
||||
compositeEntityTypes = new HashSet<string>(compositeEntities.Select(ce => ce.ParentType));
|
||||
entities = compositeEntities.Aggregate(entities, (current, compositeEntity) => PopulateCompositeEntityModel(compositeEntity, current, entitiesAndMetadata, verbose));
|
||||
entities = compositeEntities.Aggregate(entities, (current, compositeEntity) => PopulateCompositeEntityModel(compositeEntity, current, entitiesAndMetadata, verbose, utterance));
|
||||
}
|
||||
|
||||
foreach (var entity in entities)
|
||||
|
@ -64,7 +64,7 @@ namespace Microsoft.Bot.Builder.AI.Luis
|
|||
|
||||
if (verbose)
|
||||
{
|
||||
AddProperty((JObject)entitiesAndMetadata[_metadataKey], ExtractNormalizedEntityName(entity), ExtractEntityMetadata(entity));
|
||||
AddProperty((JObject)entitiesAndMetadata[_metadataKey], ExtractNormalizedEntityName(entity), ExtractEntityMetadata(entity, utterance));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,15 +162,18 @@ namespace Microsoft.Bot.Builder.AI.Luis
|
|||
}
|
||||
}
|
||||
|
||||
internal static JObject ExtractEntityMetadata(EntityModel entity)
|
||||
internal static JObject ExtractEntityMetadata(EntityModel entity, string utterance)
|
||||
{
|
||||
var start = (int)entity.StartIndex;
|
||||
var end = (int)entity.EndIndex + 1;
|
||||
dynamic obj = JObject.FromObject(new
|
||||
{
|
||||
startIndex = (int)entity.StartIndex,
|
||||
endIndex = (int)entity.EndIndex + 1,
|
||||
text = entity.Entity,
|
||||
startIndex = start,
|
||||
endIndex = end,
|
||||
text = entity.Entity.Length == end - start ? entity.Entity : utterance.Substring(start, end - start),
|
||||
type = entity.Type,
|
||||
});
|
||||
|
||||
if (entity.AdditionalProperties != null)
|
||||
{
|
||||
if (entity.AdditionalProperties.TryGetValue("score", out var score))
|
||||
|
@ -223,7 +226,7 @@ namespace Microsoft.Bot.Builder.AI.Luis
|
|||
return type.Replace('.', '_').Replace(' ', '_');
|
||||
}
|
||||
|
||||
internal static IList<EntityModel> PopulateCompositeEntityModel(CompositeEntityModel compositeEntity, IList<EntityModel> entities, JObject entitiesAndMetadata, bool verbose)
|
||||
internal static IList<EntityModel> PopulateCompositeEntityModel(CompositeEntityModel compositeEntity, IList<EntityModel> entities, JObject entitiesAndMetadata, bool verbose, string utterance)
|
||||
{
|
||||
var childrenEntites = new JObject();
|
||||
var childrenEntitiesMetadata = new JObject();
|
||||
|
@ -243,7 +246,7 @@ namespace Microsoft.Bot.Builder.AI.Luis
|
|||
|
||||
if (verbose)
|
||||
{
|
||||
childrenEntitiesMetadata = ExtractEntityMetadata(compositeEntityMetadata);
|
||||
childrenEntitiesMetadata = ExtractEntityMetadata(compositeEntityMetadata, utterance);
|
||||
childrenEntites[_metadataKey] = new JObject();
|
||||
}
|
||||
|
||||
|
@ -259,7 +262,7 @@ namespace Microsoft.Bot.Builder.AI.Luis
|
|||
}
|
||||
|
||||
// This entity doesn't belong to this composite entity
|
||||
if (child.Type != entity.Type || !CompositeContainsEntity(compositeEntityMetadata, entity))
|
||||
if (child.Type != entity.Type || !CompositeContainsEntity(compositeEntityMetadata, entity) || child.Value != entity.Entity)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -270,8 +273,10 @@ namespace Microsoft.Bot.Builder.AI.Luis
|
|||
|
||||
if (verbose)
|
||||
{
|
||||
AddProperty((JObject)childrenEntites[_metadataKey], ExtractNormalizedEntityName(entity), ExtractEntityMetadata(entity));
|
||||
AddProperty((JObject)childrenEntites[_metadataKey], ExtractNormalizedEntityName(entity), ExtractEntityMetadata(entity, utterance));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,5 +42,6 @@
|
|||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.Bot.Builder\Microsoft.Bot.Builder.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.Bot.Configuration\Microsoft.Bot.Configuration.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.Bot.Schema\Microsoft.Bot.Schema.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.Luis
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an extension for a list entity.
|
||||
/// </summary>
|
||||
public class DynamicList
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DynamicList"/> class.
|
||||
/// </summary>
|
||||
public DynamicList()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DynamicList"/> class.
|
||||
/// </summary>
|
||||
/// <param name="entity">The name of the list entity to extend.</param>
|
||||
/// <param name="requestLists">The lists to append on the extended list entity.</param>
|
||||
public DynamicList(string entity, IList<ListElement> requestLists)
|
||||
{
|
||||
Entity = entity;
|
||||
List = requestLists;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the list entity to extend.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name of the list entity to extend.
|
||||
/// </value>
|
||||
[JsonProperty(PropertyName = "listEntityName")]
|
||||
public string Entity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the lists to append on the extended list entity.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The lists to append on the extended list entity.
|
||||
/// </value>
|
||||
[JsonProperty(PropertyName = "requestLists")]
|
||||
public IList<ListElement> List { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Validate the object.
|
||||
/// </summary>
|
||||
/// <exception cref="Microsoft.Rest.ValidationException">
|
||||
/// Thrown if validation fails.
|
||||
/// </exception>
|
||||
public virtual void Validate()
|
||||
{
|
||||
// Required: ListEntityName, RequestLists
|
||||
if (Entity == null || List == null)
|
||||
{
|
||||
throw new Microsoft.Rest.ValidationException($"DynamicList requires Entity and List to be defined.");
|
||||
}
|
||||
|
||||
foreach (var elt in List)
|
||||
{
|
||||
elt.Validate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.Luis
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a user predicted entity that extends an already existing one.
|
||||
/// </summary>
|
||||
public class ExternalEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExternalEntity"/> class.
|
||||
/// </summary>
|
||||
public ExternalEntity()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExternalEntity"/> class.
|
||||
/// </summary>
|
||||
/// <param name="entity">The name of the entity to extend.</param>
|
||||
/// <param name="start">The start character index of the predicted entity.</param>
|
||||
/// <param name="length">The length of the predicted entity.</param>
|
||||
/// <param name="resolution">A user supplied custom resolution to return as the entity's prediction.</param>
|
||||
public ExternalEntity(string entity, int start, int length, object resolution = null)
|
||||
{
|
||||
Entity = entity;
|
||||
Start = start;
|
||||
Length = length;
|
||||
Resolution = resolution;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the entity to extend.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name of the entity to extend.
|
||||
/// </value>
|
||||
[JsonProperty(PropertyName = "entityName")]
|
||||
public string Entity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start character index of the predicted entity.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The start character index of the predicted entity.
|
||||
/// </value>
|
||||
[JsonProperty(PropertyName = "startIndex")]
|
||||
public int Start { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the length of the predicted entity.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The length of the predicted entity.
|
||||
/// </value>
|
||||
[JsonProperty(PropertyName = "entityLength")]
|
||||
public int Length { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a user supplied custom resolution to return as the entity's prediction.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A user supplied custom resolution to return as the entity's prediction.
|
||||
/// </value>
|
||||
[JsonProperty(PropertyName = "resolution")]
|
||||
public object Resolution { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Validate the object.
|
||||
/// </summary>
|
||||
/// <exception cref="Microsoft.Rest.ValidationException">
|
||||
/// Thrown if validation fails.
|
||||
/// </exception>
|
||||
public virtual void Validate()
|
||||
{
|
||||
if (Entity == null || Length == 0)
|
||||
{
|
||||
throw new Microsoft.Rest.ValidationException($"ExternalEntity requires an EntityName and EntityLength > 0");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.Luis
|
||||
{
|
||||
/// <summary>
|
||||
/// Recognizer with Telemetry support.
|
||||
/// </summary>
|
||||
public interface ITelemetryRecognizer : IRecognizer
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether determines whether to log personal information that came from the user.
|
||||
/// </summary>
|
||||
/// <value>If true, will log personal information into the IBotTelemetryClient.TrackEvent method; otherwise the properties will be filtered.</value>
|
||||
bool LogPersonalInformation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the currently configured <see cref="IBotTelemetryClient"/> that logs the LuisResult event.
|
||||
/// </summary>
|
||||
/// <value>The <see cref="IBotTelemetryClient"/> being used to log events.</value>
|
||||
IBotTelemetryClient TelemetryClient { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Return results of the analysis (suggested intents and entities) using the turn context.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">Context object containing information for a single turn of conversation with a user.</param>
|
||||
/// <param name="telemetryProperties">Additional properties to be logged to telemetry with the LuisResult event.</param>
|
||||
/// <param name="telemetryMetrics">Additional metrics to be logged to telemetry with the LuisResult event.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
|
||||
/// <returns>The LUIS results of the analysis of the current message text in the current turn's context activity.</returns>
|
||||
Task<RecognizerResult> RecognizeAsync(ITurnContext turnContext, Dictionary<string, string> telemetryProperties, Dictionary<string, double> telemetryMetrics, CancellationToken cancellationToken = default);
|
||||
|
||||
Task<T> RecognizeAsync<T>(ITurnContext turnContext, Dictionary<string, string> telemetryProperties, Dictionary<string, double> telemetryMetrics, CancellationToken cancellationToken = default)
|
||||
where T : IRecognizerConvert, new();
|
||||
|
||||
/// <summary>
|
||||
/// Runs an utterance through a recognizer and returns a strongly-typed recognizer result.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The recognition result type.</typeparam>
|
||||
/// <param name="turnContext">Turn context.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>Analysis of utterance.</returns>
|
||||
new Task<T> RecognizeAsync<T>(ITurnContext turnContext, CancellationToken cancellationToken = default)
|
||||
where T : IRecognizerConvert, new();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.Luis
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a sub-list to append to an existing list entity.
|
||||
/// </summary>
|
||||
public class ListElement
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ListElement"/> class.
|
||||
/// </summary>
|
||||
public ListElement()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ListElement"/> class.
|
||||
/// </summary>
|
||||
/// <param name="canonicalForm">The canonical form of the sub-list.</param>
|
||||
/// <param name="synonyms">The synonyms of the canonical form.</param>
|
||||
public ListElement(string canonicalForm, IList<string> synonyms = null)
|
||||
{
|
||||
CanonicalForm = canonicalForm;
|
||||
Synonyms = synonyms;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the canonical form of the sub-list.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The canonical form of the sub-list.
|
||||
/// </value>
|
||||
[JsonProperty(PropertyName = "canonicalForm")]
|
||||
public string CanonicalForm { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the synonyms of the canonical form.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The synonyms of the canonical form.
|
||||
/// </value>
|
||||
[JsonProperty(PropertyName = "synonyms")]
|
||||
public IList<string> Synonyms { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Validate the object.
|
||||
/// </summary>
|
||||
/// <exception cref="Microsoft.Rest.ValidationException">
|
||||
/// Thrown if parameters are invalid.
|
||||
/// </exception>
|
||||
public virtual void Validate()
|
||||
{
|
||||
if (CanonicalForm == null)
|
||||
{
|
||||
throw new Microsoft.Rest.ValidationException($"RequestList requires CanonicalForm to be defined.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.Luis
|
||||
{
|
||||
/// <summary>
|
||||
/// Data describing a LUIS application.
|
||||
/// </summary>
|
||||
public class LuisApplication
|
||||
{
|
||||
public LuisApplication()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LuisApplication"/> class.
|
||||
/// </summary>
|
||||
/// <param name="applicationId">LUIS application ID.</param>
|
||||
/// <param name="endpointKey">LUIS subscription or endpoint key.</param>
|
||||
/// <param name="endpoint">LUIS endpoint to use like https://westus.api.cognitive.microsoft.com.</param>
|
||||
public LuisApplication(string applicationId, string endpointKey, string endpoint)
|
||||
: this((applicationId, endpointKey, endpoint))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LuisApplication"/> class.
|
||||
/// </summary>
|
||||
/// <param name="applicationEndpoint">LUIS application endpoint.</param>
|
||||
public LuisApplication(string applicationEndpoint)
|
||||
: this(Parse(applicationEndpoint))
|
||||
{
|
||||
}
|
||||
|
||||
private LuisApplication(ValueTuple<string, string, string> props)
|
||||
{
|
||||
var (applicationId, endpointKey, endpoint) = props;
|
||||
|
||||
if (!Guid.TryParse(applicationId, out var _))
|
||||
{
|
||||
throw new ArgumentException($"\"{applicationId}\" is not a valid LUIS application id.");
|
||||
}
|
||||
|
||||
if (!Guid.TryParse(endpointKey, out var subscriptionGuid))
|
||||
{
|
||||
throw new ArgumentException($"\"{subscriptionGuid}\" is not a valid LUIS subscription key.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(endpoint))
|
||||
{
|
||||
endpoint = "https://westus.api.cognitive.microsoft.com";
|
||||
}
|
||||
|
||||
if (!Uri.IsWellFormedUriString(endpoint, UriKind.Absolute))
|
||||
{
|
||||
throw new ArgumentException($"\"{endpoint}\" is not a valid LUIS endpoint.");
|
||||
}
|
||||
|
||||
ApplicationId = applicationId;
|
||||
EndpointKey = endpointKey;
|
||||
Endpoint = endpoint;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets lUIS application ID.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// LUIS application ID.
|
||||
/// </value>
|
||||
public string ApplicationId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets lUIS subscription or endpoint key.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// LUIS subscription or endpoint key.
|
||||
/// </value>
|
||||
public string EndpointKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets lUIS endpoint like https://westus.api.cognitive.microsoft.com.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// LUIS endpoint where application is hosted.
|
||||
/// </value>
|
||||
public string Endpoint { get; set; }
|
||||
|
||||
private static (string applicationId, string endpointKey, string endpoint) Parse(string applicationEndpoint)
|
||||
{
|
||||
if (!Uri.TryCreate(applicationEndpoint, UriKind.Absolute, out var uri))
|
||||
{
|
||||
throw new ArgumentException(nameof(applicationEndpoint));
|
||||
}
|
||||
|
||||
string applicationId = null;
|
||||
var foundApps = false;
|
||||
foreach (var segment in uri.Segments)
|
||||
{
|
||||
if (foundApps)
|
||||
{
|
||||
applicationId = segment.TrimEnd('/');
|
||||
break;
|
||||
}
|
||||
|
||||
if (segment == "apps/")
|
||||
{
|
||||
foundApps = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (applicationId == null)
|
||||
{
|
||||
throw new ArgumentException($"Could not find application Id in {applicationEndpoint}");
|
||||
}
|
||||
|
||||
var endpointKey = HttpUtility.ParseQueryString(uri.Query).Get("subscription-key");
|
||||
var endpoint = uri.GetLeftPart(UriPartial.Authority);
|
||||
return (applicationId, endpointKey, endpoint);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.Luis
|
||||
{
|
||||
internal class LuisDelegatingHandler : DelegatingHandler
|
||||
{
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
// Bot Builder Package name and version.
|
||||
var assemblyName = this.GetType().Assembly.GetName();
|
||||
request.Headers.UserAgent.Add(new ProductInfoHeaderValue(assemblyName.Name, assemblyName.Version.ToString()));
|
||||
|
||||
// Platform information: OS and language runtime.
|
||||
var framework = Assembly
|
||||
.GetEntryAssembly()?
|
||||
.GetCustomAttribute<TargetFrameworkAttribute>()?
|
||||
.FrameworkName;
|
||||
var comment = $"({Environment.OSVersion.VersionString};{framework})";
|
||||
request.Headers.UserAgent.Add(new ProductInfoHeaderValue(comment));
|
||||
|
||||
// Forward the call.
|
||||
return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Bot.Builder.AI.LuisV3;
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.Luis
|
||||
{
|
||||
/// <summary>
|
||||
/// Optional parameters for a LUIS prediction request.
|
||||
/// </summary>
|
||||
public class LuisPredictionOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether all intents come back or only the top one.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// True for returning all intents.
|
||||
/// </value>
|
||||
public bool IncludeAllIntents { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not instance data should be included in response.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// A value indicating whether or not instance data should be included in response.
|
||||
/// </value>
|
||||
public bool IncludeInstanceData { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether API results should be included.
|
||||
/// </summary>
|
||||
/// <value>True to include API results.</value>
|
||||
/// <remarks>This is mainly useful for testing or getting access to LUIS features not yet in the SDK.</remarks>
|
||||
public bool IncludeAPIResults { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether queries should be logged in LUIS.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// If queries should be logged in LUIS in order to help build better models through active learning.
|
||||
/// </value>
|
||||
/// <remarks>The default is to log queries to LUIS in order to support active learning. To default to the Luis setting set to null.</remarks>
|
||||
public bool? Log { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets dynamic lists used to recognize entities for a particular query.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Dynamic lists of things like contact names to recognize at query time.
|
||||
/// </value>
|
||||
public IList<DynamicList> DynamicLists { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets external entities recognized in the query.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// External entities recognized in query.
|
||||
/// </value>
|
||||
public IList<ExternalEntity> ExternalEntities { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether external entities should override other means of recognizing entities.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Boolean for if external entities should be preferred to the results from LUIS models.
|
||||
/// </value>
|
||||
public bool PreferExternalEntities { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the LUIS slot to use for the application.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The LUIS slot to use for the application.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// By default this uses the production slot. You can find other standard slots in <see cref="LuisSlot"/>.
|
||||
/// If you specify a Version, then a private version of the application is used instead of a slot.
|
||||
/// </remarks>
|
||||
public string Slot { get; set; } = LuisSlot.Production;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the specific version of the application to access.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Version to access.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// LUIS supports versions and this is the version to use instead of a slot.
|
||||
/// If this is specified, then the <see cref="Slot"/> is ignored.
|
||||
/// </remarks>
|
||||
public string Version { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,443 @@
|
|||
// 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.TraceExtensions;
|
||||
using Microsoft.Bot.Schema;
|
||||
using Microsoft.Rest;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.Luis
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// A LUIS based implementation of <see cref="ITelemetryRecognizer"/> for the V3 endpoint.
|
||||
/// </summary>
|
||||
public class LuisRecognizer : ITelemetryRecognizer
|
||||
{
|
||||
/// <summary>
|
||||
/// The value type for a LUIS trace activity.
|
||||
/// </summary>
|
||||
public const string LuisTraceType = "https://www.luis.ai/schemas/trace";
|
||||
|
||||
/// <summary>
|
||||
/// The context label for a LUIS trace activity.
|
||||
/// </summary>
|
||||
public const string LuisTraceLabel = "LuisV3 Trace";
|
||||
|
||||
private readonly LuisApplication _application;
|
||||
private readonly LuisPredictionOptions _predictionOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LuisRecognizer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="application">The LUIS application to use to recognize text.</param>
|
||||
/// <param name="recognizerOptions">(Optional) Options for the created recognizer.</param>
|
||||
/// <param name="predictionOptions">(Optional) The default LUIS prediction options to use.</param>
|
||||
public LuisRecognizer(LuisApplication application, LuisRecognizerOptions recognizerOptions = null, LuisPredictionOptions predictionOptions = null)
|
||||
{
|
||||
recognizerOptions = recognizerOptions ?? new LuisRecognizerOptions();
|
||||
_application = application ?? throw new ArgumentNullException(nameof(application));
|
||||
_predictionOptions = predictionOptions ?? new LuisPredictionOptions();
|
||||
|
||||
TelemetryClient = recognizerOptions.TelemetryClient;
|
||||
LogPersonalInformation = recognizerOptions.LogPersonalInformation;
|
||||
|
||||
var delegatingHandler = new LuisDelegatingHandler();
|
||||
var httpClientHandler = recognizerOptions.HttpClient ?? CreateRootHandler();
|
||||
var currentHandler = CreateHttpHandlerPipeline(httpClientHandler, delegatingHandler);
|
||||
|
||||
DefaultHttpClient = new HttpClient(currentHandler, false)
|
||||
{
|
||||
Timeout = recognizerOptions.Timeout,
|
||||
};
|
||||
|
||||
DefaultHttpClient.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", _application.EndpointKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets client to use for http.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Client to use for http.
|
||||
/// </value>
|
||||
public static HttpClient DefaultHttpClient { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to log personal information that came from the user to telemetry.
|
||||
/// </summary>
|
||||
/// <value>If true, personal information is logged to Telemetry; otherwise the properties will be filtered.</value>
|
||||
public bool LogPersonalInformation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the currently configured <see cref="IBotTelemetryClient"/> that logs the LuisResult event.
|
||||
/// </summary>
|
||||
/// <value>The <see cref="IBotTelemetryClient"/> being used to log events.</value>
|
||||
public IBotTelemetryClient TelemetryClient { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the name of the top scoring intent from a set of LUIS results.
|
||||
/// </summary>
|
||||
/// <param name="results">Result set to be searched.</param>
|
||||
/// <param name="defaultIntent">(Optional) Intent name to return should a top intent be found. Defaults to a value of "None".</param>
|
||||
/// <param name="minScore">(Optional) Minimum score needed for an intent to be considered as a top intent. If all intents in the set are below this threshold then the `defaultIntent` will be returned. Defaults to a value of `0.0`.</param>
|
||||
/// <returns>The top scoring intent name.</returns>
|
||||
public static string TopIntent(RecognizerResult results, string defaultIntent = "None", double minScore = 0.0)
|
||||
{
|
||||
if (results == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(results));
|
||||
}
|
||||
|
||||
string topIntent = null;
|
||||
var topScore = -1.0;
|
||||
foreach (var intent in results.Intents)
|
||||
{
|
||||
var score = (double)intent.Value.Score;
|
||||
if (score > topScore && score >= minScore)
|
||||
{
|
||||
topIntent = intent.Key;
|
||||
topScore = score;
|
||||
}
|
||||
}
|
||||
|
||||
return !string.IsNullOrEmpty(topIntent) ? topIntent : defaultIntent;
|
||||
}
|
||||
|
||||
/* IRecognizer */
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual async Task<RecognizerResult> RecognizeAsync(ITurnContext turnContext, CancellationToken cancellationToken)
|
||||
=> await RecognizeInternalAsync(turnContext, null, null, null, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
/// <summary>
|
||||
/// Runs an utterance through a recognizer and returns a generic recognizer result.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">Turn context.</param>
|
||||
/// <param name="predictionOptions">A <see cref="LuisPredictionOptions"/> instance to be used by the call.
|
||||
/// This parameter gets merged with the default <see cref="LuisPredictionOptions"/> passed in the constructor.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>Analysis of utterance.</returns>
|
||||
public virtual async Task<RecognizerResult> RecognizeAsync(ITurnContext turnContext, LuisPredictionOptions predictionOptions, CancellationToken cancellationToken)
|
||||
=> await RecognizeInternalAsync(turnContext, predictionOptions, null, null, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual async Task<T> RecognizeAsync<T>(ITurnContext turnContext, CancellationToken cancellationToken)
|
||||
where T : IRecognizerConvert, new()
|
||||
{
|
||||
var result = new T();
|
||||
result.Convert(await RecognizeInternalAsync(turnContext, null, null, null, cancellationToken).ConfigureAwait(false));
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ITelemetryRecognizer */
|
||||
|
||||
/// <summary>
|
||||
/// Return results of the analysis (Suggested actions and intents).
|
||||
/// </summary>
|
||||
/// <param name="turnContext">Context object containing information for a single turn of conversation with a user.</param>
|
||||
/// <param name="telemetryProperties">Additional properties to be logged to telemetry with the LuisResult event.</param>
|
||||
/// <param name="telemetryMetrics">Additional metrics to be logged to telemetry with the LuisResult event.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
|
||||
/// <returns>The LUIS results of the analysis of the current message text in the current turn's context activity.</returns>
|
||||
public virtual async Task<RecognizerResult> RecognizeAsync(ITurnContext turnContext, Dictionary<string, string> telemetryProperties, Dictionary<string, double> telemetryMetrics = null, CancellationToken cancellationToken = default)
|
||||
=> await RecognizeInternalAsync(turnContext, null, telemetryProperties, telemetryMetrics, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
/// <summary>
|
||||
/// Return results of the analysis (Suggested actions and intents).
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The recognition result type.</typeparam>
|
||||
/// <param name="turnContext">Context object containing information for a single turn of conversation with a user.</param>
|
||||
/// <param name="telemetryProperties">Additional properties to be logged to telemetry with the LuisResult event.</param>
|
||||
/// <param name="telemetryMetrics">Additional metrics to be logged to telemetry with the LuisResult event.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
|
||||
/// <returns>The LUIS results of the analysis of the current message text in the current turn's context activity.</returns>
|
||||
public virtual async Task<T> RecognizeAsync<T>(ITurnContext turnContext, Dictionary<string, string> telemetryProperties, Dictionary<string, double> telemetryMetrics = null, CancellationToken cancellationToken = default)
|
||||
where T : IRecognizerConvert, new()
|
||||
{
|
||||
var result = new T();
|
||||
result.Convert(await RecognizeInternalAsync(turnContext, null, telemetryProperties, telemetryMetrics, cancellationToken).ConfigureAwait(false));
|
||||
return result;
|
||||
}
|
||||
|
||||
/* LUIS specific methods */
|
||||
|
||||
/// <summary>
|
||||
/// Return results of the analysis (Suggested actions and intents).
|
||||
/// </summary>
|
||||
/// <param name="turnContext">Context object containing information for a single turn of conversation with a user.</param>
|
||||
/// <param name="predictionOptions">A <see cref="LuisPredictionOptions"/> instance to be used by the call.
|
||||
/// This parameter gets merged with the default <see cref="LuisPredictionOptions"/> passed in the constructor.</param>
|
||||
/// <param name="telemetryProperties">Additional properties to be logged to telemetry with the LuisResult event.</param>
|
||||
/// <param name="telemetryMetrics">Additional metrics to be logged to telemetry with the LuisResult event.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
|
||||
/// <returns>The LUIS results of the analysis of the current message text in the current turn's context activity.</returns>
|
||||
public virtual async Task<RecognizerResult> RecognizeAsync(
|
||||
ITurnContext turnContext,
|
||||
LuisPredictionOptions predictionOptions = null,
|
||||
Dictionary<string, string> telemetryProperties = null,
|
||||
Dictionary<string, double> telemetryMetrics = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
=> await RecognizeInternalAsync(turnContext, predictionOptions, telemetryProperties, telemetryMetrics, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
/// <summary>
|
||||
/// Return results of the analysis (Suggested actions and intents).
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The recognition result type.</typeparam>
|
||||
/// <param name="turnContext">Context object containing information for a single turn of conversation with a user.</param>
|
||||
/// <param name="predictionOptions">A <see cref="LuisPredictionOptions"/> instance to be used by the call.
|
||||
/// This parameter gets merged with the default <see cref="LuisPredictionOptions"/> passed in the constructor.</param>
|
||||
/// <param name="telemetryProperties">Additional properties to be logged to telemetry with the LuisResult event.</param>
|
||||
/// <param name="telemetryMetrics">Additional metrics to be logged to telemetry with the LuisResult event.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
|
||||
/// <returns>The LUIS results of the analysis of the current message text in the current turn's context activity.</returns>
|
||||
public virtual async Task<T> RecognizeAsync<T>(
|
||||
ITurnContext turnContext,
|
||||
LuisPredictionOptions predictionOptions = null,
|
||||
Dictionary<string, string> telemetryProperties = null,
|
||||
Dictionary<string, double> telemetryMetrics = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
where T : IRecognizerConvert, new()
|
||||
{
|
||||
var result = new T();
|
||||
result.Convert(await RecognizeInternalAsync(turnContext, predictionOptions, telemetryProperties, telemetryMetrics, cancellationToken).ConfigureAwait(false));
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked prior to a LuisResult being logged.
|
||||
/// </summary>
|
||||
/// <param name="recognizerResult">The Luis Results for the call.</param>
|
||||
/// <param name="turnContext">Context object containing information for a single turn of conversation with a user.</param>
|
||||
/// <param name="telemetryProperties">Additional properties to be logged to telemetry with the LuisResult event.</param>
|
||||
/// <param name="telemetryMetrics">Additional metrics to be logged to telemetry with the LuisResult event.</param>
|
||||
protected virtual void OnRecognizerResult(RecognizerResult recognizerResult, ITurnContext turnContext, Dictionary<string, string> telemetryProperties = null, Dictionary<string, double> telemetryMetrics = null)
|
||||
{
|
||||
var properties = FillLuisEventProperties(recognizerResult, turnContext, telemetryProperties);
|
||||
|
||||
// Track the event
|
||||
TelemetryClient.TrackEvent(LuisTelemetryConstants.LuisResult, properties, telemetryMetrics);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills the event properties for LuisResult event for telemetry.
|
||||
/// These properties are logged when the recognizer is called.
|
||||
/// </summary>
|
||||
/// <param name="recognizerResult">Last activity sent from user.</param>
|
||||
/// <param name="turnContext">Context object containing information for a single turn of conversation with a user.</param>
|
||||
/// <param name="telemetryProperties">Additional properties to be logged to telemetry with the LuisResult event.</param>
|
||||
/// <returns>A dictionary that is sent as "Properties" to IBotTelemetryClient.TrackEvent method for the BotMessageSend event.</returns>
|
||||
protected Dictionary<string, string> FillLuisEventProperties(RecognizerResult recognizerResult, ITurnContext turnContext, Dictionary<string, string> telemetryProperties = null)
|
||||
{
|
||||
var topTwoIntents = (recognizerResult.Intents.Count > 0) ? recognizerResult.Intents.OrderByDescending(x => x.Value.Score).Take(2).ToArray() : null;
|
||||
|
||||
// Add the intent score and conversation id properties
|
||||
var properties = new Dictionary<string, string>()
|
||||
{
|
||||
{ LuisTelemetryConstants.ApplicationIdProperty, _application.ApplicationId },
|
||||
{ LuisTelemetryConstants.IntentProperty, topTwoIntents?[0].Key ?? string.Empty },
|
||||
{ LuisTelemetryConstants.IntentScoreProperty, topTwoIntents?[0].Value.Score?.ToString("N2") ?? "0.00" },
|
||||
{ LuisTelemetryConstants.Intent2Property, (topTwoIntents?.Count() > 1) ? topTwoIntents?[1].Key ?? string.Empty : string.Empty },
|
||||
{ LuisTelemetryConstants.IntentScore2Property, (topTwoIntents?.Count() > 1) ? topTwoIntents?[1].Value.Score?.ToString("N2") ?? "0.00" : "0.00" },
|
||||
{ LuisTelemetryConstants.FromIdProperty, turnContext.Activity.From.Id },
|
||||
};
|
||||
|
||||
if (recognizerResult.Properties.TryGetValue("sentiment", out var sentiment) && sentiment is JObject)
|
||||
{
|
||||
if (((JObject)sentiment).TryGetValue("label", out var label))
|
||||
{
|
||||
properties.Add(LuisTelemetryConstants.SentimentLabelProperty, label.Value<string>());
|
||||
}
|
||||
|
||||
if (((JObject)sentiment).TryGetValue("score", out var score))
|
||||
{
|
||||
properties.Add(LuisTelemetryConstants.SentimentScoreProperty, score.Value<string>());
|
||||
}
|
||||
}
|
||||
|
||||
var entities = recognizerResult.Entities?.ToString();
|
||||
properties.Add(LuisTelemetryConstants.EntitiesProperty, entities);
|
||||
|
||||
// Use the LogPersonalInformation flag to toggle logging PII data, text is a common example
|
||||
if (LogPersonalInformation && !string.IsNullOrEmpty(turnContext.Activity.Text))
|
||||
{
|
||||
properties.Add(LuisTelemetryConstants.QuestionProperty, turnContext.Activity.Text);
|
||||
}
|
||||
|
||||
// Additional Properties can override "stock" properties.
|
||||
if (telemetryProperties != null)
|
||||
{
|
||||
properties = telemetryProperties.Concat(properties)
|
||||
.GroupBy(kv => kv.Key)
|
||||
.ToDictionary(g => g.Key, g => g.First().Value);
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
private string AddParam(string query, string prop, bool? val)
|
||||
{
|
||||
var result = query;
|
||||
if (val.HasValue)
|
||||
{
|
||||
if (query == null)
|
||||
{
|
||||
result = $"{prop}={val.Value}";
|
||||
}
|
||||
else
|
||||
{
|
||||
result += $"&{prop}={val.Value}";
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<RecognizerResult> RecognizeInternalAsync(ITurnContext turnContext, LuisPredictionOptions predictionOptions, Dictionary<string, string> telemetryProperties, Dictionary<string, double> telemetryMetrics, CancellationToken cancellationToken)
|
||||
{
|
||||
BotAssert.ContextNotNull(turnContext);
|
||||
if (turnContext.Activity.Type != ActivityTypes.Message)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var options = predictionOptions ?? _predictionOptions;
|
||||
var utterance = turnContext.Activity?.AsMessageActivity()?.Text;
|
||||
RecognizerResult recognizerResult;
|
||||
JObject luisResponse = null;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(utterance))
|
||||
{
|
||||
recognizerResult = new RecognizerResult
|
||||
{
|
||||
Text = utterance,
|
||||
Intents = new Dictionary<string, IntentScore>() { { string.Empty, new IntentScore() { Score = 1.0 } } },
|
||||
Entities = new JObject(),
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
var uri = new UriBuilder(_application.Endpoint);
|
||||
|
||||
// TODO: When the endpoint GAs, we will need to change this. I could make it an option, but other code is likely to need to change.
|
||||
uri.Path += $"luis/v3.0-preview/apps/{_application.ApplicationId}";
|
||||
|
||||
var query = AddParam(null, "verbose", options.IncludeInstanceData);
|
||||
query = AddParam(query, "log", options.Log);
|
||||
query = AddParam(query, "show-all-intents", options.IncludeAllIntents);
|
||||
uri.Query = query;
|
||||
|
||||
var content = new JObject
|
||||
{
|
||||
{ "query", utterance },
|
||||
};
|
||||
var queryOptions = new JObject
|
||||
{
|
||||
{ "overridePredictions", options.PreferExternalEntities },
|
||||
};
|
||||
content.Add("options", queryOptions);
|
||||
|
||||
var settings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };
|
||||
if (options.DynamicLists != null)
|
||||
{
|
||||
foreach (var list in options.DynamicLists)
|
||||
{
|
||||
list.Validate();
|
||||
}
|
||||
|
||||
content.Add("dynamicLists", (JArray)JsonConvert.DeserializeObject(JsonConvert.SerializeObject(options.DynamicLists, settings)));
|
||||
}
|
||||
|
||||
if (options.ExternalEntities != null)
|
||||
{
|
||||
foreach (var entity in options.ExternalEntities)
|
||||
{
|
||||
entity.Validate();
|
||||
}
|
||||
|
||||
content.Add("externalEntities", (JArray)JsonConvert.DeserializeObject(JsonConvert.SerializeObject(options.ExternalEntities, settings)));
|
||||
}
|
||||
|
||||
if (options.Version == null)
|
||||
{
|
||||
uri.Path += $"/slots/{options.Slot}/predict";
|
||||
}
|
||||
else
|
||||
{
|
||||
uri.Path += $"/versions/{options.Version}/predict";
|
||||
}
|
||||
|
||||
var response = await DefaultHttpClient.PostAsync(uri.Uri, new StringContent(content.ToString(), System.Text.Encoding.UTF8, "application/json")).ConfigureAwait(false);
|
||||
response.EnsureSuccessStatusCode();
|
||||
luisResponse = (JObject)JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync().ConfigureAwait(false));
|
||||
var prediction = (JObject)luisResponse["prediction"];
|
||||
recognizerResult = new RecognizerResult
|
||||
{
|
||||
Text = utterance,
|
||||
AlteredText = prediction["alteredQuery"]?.Value<string>(),
|
||||
Intents = LuisUtil.GetIntents(prediction),
|
||||
Entities = LuisUtil.ExtractEntitiesAndMetadata(prediction),
|
||||
};
|
||||
LuisUtil.AddProperties(prediction, recognizerResult);
|
||||
if (options.IncludeAPIResults)
|
||||
{
|
||||
recognizerResult.Properties.Add("luisResult", luisResponse);
|
||||
}
|
||||
}
|
||||
|
||||
// Log telemetry
|
||||
OnRecognizerResult(recognizerResult, turnContext, telemetryProperties, telemetryMetrics);
|
||||
|
||||
var traceInfo = JObject.FromObject(
|
||||
new
|
||||
{
|
||||
recognizerResult,
|
||||
luisModel = new
|
||||
{
|
||||
ModelID = _application.ApplicationId,
|
||||
},
|
||||
luisOptions = options,
|
||||
luisResult = luisResponse,
|
||||
});
|
||||
|
||||
await turnContext.TraceActivityAsync("LuisRecognizer", traceInfo, LuisTraceType, LuisTraceLabel, cancellationToken).ConfigureAwait(false);
|
||||
return recognizerResult;
|
||||
}
|
||||
|
||||
private DelegatingHandler CreateHttpHandlerPipeline(HttpClientHandler httpClientHandler, params DelegatingHandler[] handlers)
|
||||
{
|
||||
// Now, the RetryAfterDelegatingHandler should be the absolute outermost handler
|
||||
// because it's extremely lightweight and non-interfering
|
||||
DelegatingHandler currentHandler =
|
||||
new RetryDelegatingHandler(new RetryAfterDelegatingHandler { InnerHandler = httpClientHandler });
|
||||
|
||||
if (handlers != null)
|
||||
{
|
||||
for (var i = handlers.Length - 1; i >= 0; --i)
|
||||
{
|
||||
var handler = handlers[i];
|
||||
|
||||
// Non-delegating handlers are ignored since we always
|
||||
// have RetryDelegatingHandler as the outer-most handler
|
||||
while (handler.InnerHandler is DelegatingHandler)
|
||||
{
|
||||
handler = handler.InnerHandler as DelegatingHandler;
|
||||
}
|
||||
|
||||
handler.InnerHandler = currentHandler;
|
||||
currentHandler = handlers[i];
|
||||
}
|
||||
}
|
||||
|
||||
return currentHandler;
|
||||
}
|
||||
|
||||
private HttpClientHandler CreateRootHandler() => new HttpClientHandler();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.Luis
|
||||
{
|
||||
/// <summary>
|
||||
/// Optional parameters for a LUIS recognizer.
|
||||
/// </summary>
|
||||
public class LuisRecognizerOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the time span to wait before the request times out.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The time span to wait before the request times out. Default is 2 seconds.
|
||||
/// </value>
|
||||
public TimeSpan Timeout { get; set; } = new TimeSpan(0, 2, 0);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the IBotTelemetryClient used to log the LuisResult event.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The client used to log telemetry events.
|
||||
/// </value>
|
||||
[JsonIgnore]
|
||||
public IBotTelemetryClient TelemetryClient { get; set; } = new NullBotTelemetryClient();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to log personal information that came from the user to telemetry.
|
||||
/// </summary>
|
||||
/// <value>If true, personal information is logged to Telemetry; otherwise the properties will be filtered.</value>
|
||||
public bool LogPersonalInformation { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the handler for sending http calls.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Handler for intercepting http calls for logging or testing.
|
||||
/// </value>
|
||||
public HttpClientHandler HttpClient { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.LuisV3
|
||||
{
|
||||
/// <summary>
|
||||
/// Default LUIS slot names.
|
||||
/// </summary>
|
||||
public static class LuisSlot
|
||||
{
|
||||
/// <summary>
|
||||
/// Production slot on LUIS.
|
||||
/// </summary>
|
||||
public static readonly string Production = "production";
|
||||
|
||||
/// <summary>
|
||||
/// Staging slot on LUIS.
|
||||
/// </summary>
|
||||
public static readonly string Staging = "staging";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.Luis
|
||||
{
|
||||
/// <summary>
|
||||
/// The IBotTelemetryClient event and property names that logged by default.
|
||||
/// </summary>
|
||||
public static class LuisTelemetryConstants
|
||||
{
|
||||
public static readonly string LuisResult = "LuisResult"; // Event name
|
||||
public static readonly string ApplicationIdProperty = "applicationId";
|
||||
public static readonly string IntentProperty = "intent";
|
||||
public static readonly string IntentScoreProperty = "intentScore";
|
||||
public static readonly string Intent2Property = "intent2";
|
||||
public static readonly string IntentScore2Property = "intentScore2";
|
||||
public static readonly string EntitiesProperty = "entities";
|
||||
public static readonly string QuestionProperty = "question";
|
||||
public static readonly string ActivityIdProperty = "activityId";
|
||||
public static readonly string SentimentLabelProperty = "sentimentLabel";
|
||||
public static readonly string SentimentScoreProperty = "sentimentScore";
|
||||
public static readonly string FromIdProperty = "fromId";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.Luis
|
||||
{
|
||||
// Utility functions used to extract and transform data from Luis SDK
|
||||
internal static class LuisUtil
|
||||
{
|
||||
internal const string MetadataKey = "$instance";
|
||||
internal const string GeoV2 = "builtin.geographyV2.";
|
||||
internal static readonly HashSet<string> _dateSubtypes = new HashSet<string> { "date", "daterange", "datetime", "datetimerange", "duration", "set", "time", "timerange" };
|
||||
|
||||
internal static string NormalizedIntent(string intent) => intent.Replace('.', '_').Replace(' ', '_');
|
||||
|
||||
internal static IDictionary<string, IntentScore> GetIntents(JObject luisResult)
|
||||
{
|
||||
var result = new Dictionary<string, IntentScore>();
|
||||
var intents = (JObject)luisResult["intents"];
|
||||
if (intents != null)
|
||||
{
|
||||
foreach (var intent in intents)
|
||||
{
|
||||
result.Add(NormalizedIntent(intent.Key), new IntentScore { Score = intent.Value["score"] == null ? 0.0 : intent.Value["score"].Value<double>() });
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Remove role and ensure that dot and space are not a part of entity names since we want to do JSON paths.
|
||||
internal static string NormalizeEntity(string entity)
|
||||
{
|
||||
// Type::Role -> Role
|
||||
var type = entity.Split(':').Last();
|
||||
return type.Replace('.', '_').Replace(' ', '_');
|
||||
}
|
||||
|
||||
// TODO: This code should be removed once V3.1 returns a geography object.
|
||||
// It exists to find the type field in $instance if present in order to create
|
||||
// a geography object with type and value.
|
||||
internal static void FindGeographyTypes(JToken source, Dictionary<string, string> geoTypes)
|
||||
{
|
||||
if (source != null)
|
||||
{
|
||||
if (source is JObject obj)
|
||||
{
|
||||
if (obj.TryGetValue("type", out var type) && type.Type == JTokenType.String && type.Value<string>().StartsWith(GeoV2))
|
||||
{
|
||||
var path = type.Path.Replace(MetadataKey + ".", string.Empty);
|
||||
path = path.Substring(0, path.LastIndexOf('.'));
|
||||
geoTypes.Add(path, type.Value<string>().Substring(GeoV2.Length));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var property in obj.Properties())
|
||||
{
|
||||
FindGeographyTypes(property.Value, geoTypes);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (source is JArray arr)
|
||||
{
|
||||
foreach (var elt in arr)
|
||||
{
|
||||
FindGeographyTypes(elt, geoTypes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static JToken MapProperties(JToken source, bool inInstance, Dictionary<string, string> geoTypes)
|
||||
{
|
||||
var result = source;
|
||||
if (source is JObject obj)
|
||||
{
|
||||
var nobj = new JObject();
|
||||
|
||||
// Fix datetime by reverting to simple timex
|
||||
if (!inInstance && obj.TryGetValue("type", out var type) && type.Type == JTokenType.String && _dateSubtypes.Contains(type.Value<string>()))
|
||||
{
|
||||
var timexs = obj["values"];
|
||||
var arr = new JArray();
|
||||
if (timexs != null)
|
||||
{
|
||||
var unique = new HashSet<string>();
|
||||
foreach (var elt in timexs)
|
||||
{
|
||||
unique.Add(elt["timex"]?.Value<string>());
|
||||
}
|
||||
|
||||
foreach (var timex in unique)
|
||||
{
|
||||
arr.Add(timex);
|
||||
}
|
||||
|
||||
nobj["timex"] = arr;
|
||||
}
|
||||
|
||||
nobj["type"] = type;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Map or remove properties
|
||||
foreach (var property in obj.Properties())
|
||||
{
|
||||
var name = NormalizeEntity(property.Name);
|
||||
var isObj = property.Value.Type == JTokenType.Object;
|
||||
var isArr = property.Value.Type == JTokenType.Array;
|
||||
var isStr = property.Value.Type == JTokenType.String;
|
||||
var isInt = property.Value.Type == JTokenType.Integer;
|
||||
var val = MapProperties(property.Value, inInstance || property.Name == MetadataKey, geoTypes);
|
||||
if (name == "datetime" && isArr)
|
||||
{
|
||||
nobj.Add("datetimeV1", val);
|
||||
}
|
||||
else if (name == "datetimeV2" && isArr)
|
||||
{
|
||||
nobj.Add("datetime", val);
|
||||
}
|
||||
else if (inInstance)
|
||||
{
|
||||
// Correct $instance issues
|
||||
if (name == "length" && isInt)
|
||||
{
|
||||
nobj.Add("endIndex", property.Value.Value<int>() + property.Parent["startIndex"].Value<int>());
|
||||
}
|
||||
else if (!((isInt && name == "modelTypeId") ||
|
||||
(isStr && name == "role")))
|
||||
{
|
||||
nobj.Add(name, val);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Correct non-$instance values
|
||||
if (name == "unit" && isStr)
|
||||
{
|
||||
nobj.Add("units", val);
|
||||
}
|
||||
else
|
||||
{
|
||||
nobj.Add(name, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = nobj;
|
||||
}
|
||||
else if (source is JArray arr)
|
||||
{
|
||||
var narr = new JArray();
|
||||
foreach (var elt in arr)
|
||||
{
|
||||
if (!inInstance && geoTypes.TryGetValue(elt.Path, out var geoType))
|
||||
{
|
||||
narr.Add(new JObject(new JProperty("location", elt.Value<string>()), new JProperty("type", geoType)));
|
||||
}
|
||||
else
|
||||
{
|
||||
narr.Add(MapProperties(elt, inInstance, geoTypes));
|
||||
}
|
||||
}
|
||||
|
||||
result = narr;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static JObject ExtractEntitiesAndMetadata(JObject prediction)
|
||||
{
|
||||
var entities = (JObject)JObject.FromObject(prediction["entities"]);
|
||||
var geoTypes = new Dictionary<string, string>();
|
||||
FindGeographyTypes(entities, geoTypes);
|
||||
return (JObject)MapProperties(entities, false, geoTypes);
|
||||
}
|
||||
|
||||
internal static void AddProperties(JObject luis, RecognizerResult result)
|
||||
{
|
||||
var sentiment = luis["sentiment"];
|
||||
if (luis["sentiment"] != null)
|
||||
{
|
||||
result.Properties.Add("sentiment", new JObject(
|
||||
new JProperty("label", sentiment["label"]),
|
||||
new JProperty("score", sentiment["score"])));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Version>4.0.0-local</Version>
|
||||
<Description>LUIS V3 endpoint Middleware and Recognizers for the Microsoft Bot Builder SDK</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Bot.Builder" Version="4.5.1" />
|
||||
<PackageReference Include="Microsoft.Bot.Builder.ApplicationInsights" Version="4.5.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.Luis
|
||||
{
|
||||
/// <summary>
|
||||
/// Strongly typed LUIS builtin_age.
|
||||
/// </summary>
|
||||
public class Age : NumberWithUnits
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Age"/> class.
|
||||
/// </summary>
|
||||
/// <param name="age">Age.</param>
|
||||
/// <param name="units">Units for age.</param>
|
||||
public Age(double age, string units)
|
||||
: base(age, units)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() => $"Age({Number} {Units})";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.Luis
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the built-in LUIS date-time type.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// LUIS recognizes time expressions like "next monday" and converts those to a type and set of timex expressions.
|
||||
/// More information on timex can be found here: http://www.timeml.org/publications/timeMLdocs/timeml_1.2.1.html#timex3.
|
||||
/// More information on the library which does the recognition can be found here: https://github.com/Microsoft/Recognizers-Text.
|
||||
/// </remarks>
|
||||
public class DateTimeSpec
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DateTimeSpec"/> class.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of TIMEX expression.</param>
|
||||
/// <param name="expressions">The TIMEX expression.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="type"/> is null or contains only white space,
|
||||
/// or <paramref name="expressions"/> is null.</exception>
|
||||
public DateTimeSpec(string type, IEnumerable<string> expressions)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(type))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
}
|
||||
|
||||
if (expressions == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(expressions));
|
||||
}
|
||||
|
||||
Type = type;
|
||||
Expressions = expressions.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets type of expression.
|
||||
/// </summary>
|
||||
/// <remarks>Example types include:
|
||||
/// <list type="*">
|
||||
/// <item>time -- simple time expression like "3pm".</item>
|
||||
/// <item>date -- simple date like "july 3rd".</item>
|
||||
/// <item>datetime -- combination of date and time like "march 23 2pm".</item>
|
||||
/// <item>timerange -- a range of time like "2pm to 4pm".</item>
|
||||
/// <item>daterange -- a range of dates like "march 23rd to 24th".</item>
|
||||
/// <item>datetimerange -- a range of dates and times like "july 3rd 2pm to 5th 4pm".</item>
|
||||
/// <item>set -- a recurrence like "every monday".</item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
/// <value>
|
||||
/// The type of expression.
|
||||
/// </value>
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets Timex expressions.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Timex expressions.
|
||||
/// </value>
|
||||
[JsonProperty("timex")]
|
||||
public IReadOnlyList<string> Expressions { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() => $"DateTimeSpec({Type}, [{string.Join(", ", Expressions)}]";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.Luis
|
||||
{
|
||||
/// <summary>
|
||||
/// Strongly typed LUIS builtin_dimension.
|
||||
/// </summary>
|
||||
public class Dimension : NumberWithUnits
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Dimension"/> class.
|
||||
/// </summary>
|
||||
/// <param name="number">Number.</param>
|
||||
/// <param name="units">Units for number.</param>
|
||||
public Dimension(double number, string units)
|
||||
: base(number, units)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() => $"Dimension({Number} {Units})";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.Luis
|
||||
{
|
||||
/// <summary>
|
||||
/// Strongly typed LUIS builtin GeographyV2.
|
||||
/// </summary>
|
||||
public class GeographyV2
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GeographyV2"/> class.
|
||||
/// </summary>
|
||||
/// <param name="type">Type of geographic location from <see cref="Types"/>.</param>
|
||||
/// <param name="location">Geographic location.</param>
|
||||
public GeographyV2(string type, string location)
|
||||
{
|
||||
Type = type;
|
||||
Location = location;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets type of geographic location.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Type of geographic location from <see cref="Types"/>.
|
||||
/// </value>
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets geographic location.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Geographic location.
|
||||
/// </value>
|
||||
[JsonProperty("location")]
|
||||
public string Location { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() => $"GeographyV2({Type}, {Location})";
|
||||
|
||||
/// <summary>
|
||||
/// Different types of geographic locations.
|
||||
/// </summary>
|
||||
public static class Types
|
||||
{
|
||||
public const string POI = "poi";
|
||||
public const string City = "city";
|
||||
public const string CountryRegion = "countryRegion";
|
||||
public const string Continent = "continent";
|
||||
public const string State = "state";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.Luis
|
||||
{
|
||||
/// <summary>
|
||||
/// Strongly typed information corresponding to LUIS $instance value.
|
||||
/// </summary>
|
||||
public class InstanceData
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets 0-based index in the analyzed text for where entity starts.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// 0-based index in the analyzed text for where entity starts.
|
||||
/// </value>
|
||||
[JsonProperty("startIndex")]
|
||||
public int StartIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets 0-based index of the first character beyond the recognized entity.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// 0-based index of the first character beyond the recognized entity.
|
||||
/// </value>
|
||||
[JsonProperty("endIndex")]
|
||||
public int EndIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets word broken and normalized text for the entity.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Word broken and normalized text for the entity.
|
||||
/// </value>
|
||||
[JsonProperty("text")]
|
||||
public string Text { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets optional confidence in the recognition.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Optional confidence in the recognition.
|
||||
/// </value>
|
||||
[JsonProperty("score")]
|
||||
public double? Score { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets optional type for the entity.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Optional entity type.
|
||||
/// </value>
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets optional subtype for the entity.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Optional entity subtype.
|
||||
/// </value>
|
||||
[JsonProperty("subtype")]
|
||||
public string Subtype { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets any extra properties.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Any extra properties.
|
||||
/// </value>
|
||||
[JsonExtensionData(ReadData = true, WriteData = true)]
|
||||
public IDictionary<string, object> Properties { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.Luis
|
||||
{
|
||||
/// <summary>
|
||||
/// Strongly typed LUIS builtin_money.
|
||||
/// </summary>
|
||||
public class Money : NumberWithUnits
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Money"/> class.
|
||||
/// </summary>
|
||||
/// <param name="money">Money amount.</param>
|
||||
/// <param name="units">Currency units.</param>
|
||||
public Money(double money, string units)
|
||||
: base(money, units)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() => $"Currency({Number} {Units})";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.Luis
|
||||
{
|
||||
/// <summary>
|
||||
/// Strongly typed class for LUIS number and units entity recognition.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Specific subtypes of this class are generated to match the builtin age, currency, dimension and temperature entities.
|
||||
/// </remarks>
|
||||
public class NumberWithUnits
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NumberWithUnits"/> class.
|
||||
/// </summary>
|
||||
/// <param name="number">Number.</param>
|
||||
/// <param name="units">Units for number.</param>
|
||||
public NumberWithUnits(double? number, string units)
|
||||
{
|
||||
Number = number;
|
||||
Units = units;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets recognized number, or null if unit only.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Recognized number, or null if unit only.
|
||||
/// </value>
|
||||
[JsonProperty("number")]
|
||||
public double? Number { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets normalized recognized unit.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Normalized recognized unit.
|
||||
/// </value>
|
||||
[JsonProperty("units")]
|
||||
public string Units { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.Luis
|
||||
{
|
||||
/// <summary>
|
||||
/// Strongly typed LUIS builtin OrdinalV2.
|
||||
/// </summary>
|
||||
public class OrdinalV2
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OrdinalV2"/> class.
|
||||
/// </summary>
|
||||
/// <param name="offset">Offset from <see cref="RelativeTo"/>.</param>
|
||||
/// <param name="relativeTo">Position that anchors offset.</param>
|
||||
public OrdinalV2(string relativeTo, long offset)
|
||||
{
|
||||
RelativeTo = relativeTo;
|
||||
Offset = offset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the anchor for the offset.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The base position in a sequence one of <see cref="Anchor"/>.
|
||||
/// </value>
|
||||
[JsonProperty("relativeTo")]
|
||||
public string RelativeTo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the offset in the sequence with respect to <see cref="RelativeTo"/>.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Offset in sequence relative to <see cref="RelativeTo"/>.
|
||||
/// </value>
|
||||
[JsonProperty("offset")]
|
||||
public long Offset { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() => $"OrdinalV2({RelativeTo}, {Offset})";
|
||||
|
||||
/// <summary>
|
||||
/// Possible anchors for offsets.
|
||||
/// </summary>
|
||||
public static class Anchor
|
||||
{
|
||||
public const string Current = "current";
|
||||
public const string End = "end";
|
||||
public const string Start = "start";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.Luis
|
||||
{
|
||||
/// <summary>
|
||||
/// Strongly typed LUIS builtin_temperature.
|
||||
/// </summary>
|
||||
public class Temperature : NumberWithUnits
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Temperature"/> class.
|
||||
/// </summary>
|
||||
/// <param name="temperature">Temperature.</param>
|
||||
/// <param name="units">Units.</param>
|
||||
public Temperature(double temperature, string units)
|
||||
: base(temperature, units)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() => $"Temperature({Number} {Units})";
|
||||
}
|
||||
}
|
|
@ -11,29 +11,23 @@ namespace Microsoft.Bot.Builder.AI.QnA
|
|||
public class FeedbackRecord
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets user id.
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// User id.
|
||||
/// </value>
|
||||
/// <value>the user id.</value>
|
||||
[JsonProperty("userId")]
|
||||
public string UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets user question.
|
||||
/// Gets or sets the user question.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// User question.
|
||||
/// </value>
|
||||
/// <value>the user question.</value>
|
||||
[JsonProperty("userQuestion")]
|
||||
public string UserQuestion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets qnA Id.
|
||||
/// Gets or sets the QnA Id.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// QnA Id.
|
||||
/// </value>
|
||||
/// <value>the qnaMaker id.</value>
|
||||
[JsonProperty("qnaId")]
|
||||
public int QnaId { get; set; }
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.QnA
|
||||
{
|
||||
/// <summary>
|
||||
/// Prompt Object.
|
||||
/// </summary>
|
||||
public class Prompt
|
||||
{
|
||||
private const int DefaultDisplayOrder = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets displayOrder - index of the prompt - used in ordering of the prompts.
|
||||
/// </summary>
|
||||
/// <value>Display order.</value>
|
||||
[JsonProperty("displayOrder")]
|
||||
public int DisplayOrder { get; set; } = DefaultDisplayOrder;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets qna id corresponding to the prompt - if QnaId is present, QnADTO object is ignored.
|
||||
/// </summary>
|
||||
/// <value>QnA Id.</value>
|
||||
[JsonProperty("qnaId")]
|
||||
public int QnaId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets displayText - Text displayed to represent a follow up question prompt.
|
||||
/// </summary>
|
||||
/// <value>Display test.</value>
|
||||
[JsonProperty("displayText")]
|
||||
public string DisplayText { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the QnADTO returned from the API.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The QnA DTO.
|
||||
/// </value>
|
||||
[JsonProperty("qna")]
|
||||
public object Qna { get; set; }
|
||||
}
|
||||
}
|
|
@ -75,5 +75,23 @@ namespace Microsoft.Bot.Builder.AI.QnA
|
|||
/// </value>
|
||||
[JsonProperty("metadataBoost")]
|
||||
public Metadata[] MetadataBoost { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets context for multi-turn responses.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The context from which the QnA was extracted.
|
||||
/// </value>
|
||||
[JsonProperty("context")]
|
||||
public QnARequestContext Context { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets QnA Id of the current question asked.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Id of the current question asked.
|
||||
/// </value>
|
||||
[JsonProperty("qnaId")]
|
||||
public int QnAId { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.QnA
|
||||
{
|
||||
/// <summary>
|
||||
/// The context associated with QnA. Used to mark if the current prompt is relevant with a previous question or not.
|
||||
/// </summary>
|
||||
public class QnARequestContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the previous QnA Id that was returned.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The previous QnA Id.
|
||||
/// </value>
|
||||
[JsonProperty(PropertyName = "previousQnAId")]
|
||||
public int PreviousQnAId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the previous user query/question.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The previous user query.
|
||||
/// </value>
|
||||
[JsonProperty(PropertyName = "previousUserQuery")]
|
||||
public string PreviousUserQuery { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.Bot.Builder.AI.QnA
|
||||
{
|
||||
/// <summary>
|
||||
/// The context associated with QnA. Used to mark if the qna response has related prompts to display.
|
||||
/// </summary>
|
||||
public class QnAResponseContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the prompts collection of related prompts.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The QnA prompts array.
|
||||
/// </value>
|
||||
[JsonProperty(PropertyName = "prompts")]
|
||||
public Prompt[] Prompts { get; set; }
|
||||
}
|
||||
}
|
|
@ -67,5 +67,14 @@ namespace Microsoft.Bot.Builder.AI.QnA
|
|||
/// </value>
|
||||
[JsonProperty(PropertyName = "id")]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets context for multi-turn responses.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The context from which the QnA was extracted.
|
||||
/// </value>
|
||||
[JsonProperty(PropertyName = "context")]
|
||||
public QnAResponseContext Context { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,22 @@ namespace Microsoft.Bot.Builder.AI.QnA
|
|||
[JsonProperty("top")]
|
||||
public int Top { get; set; }
|
||||
|
||||
[JsonProperty("strictFilters")]
|
||||
/// <summary>
|
||||
/// Gets or sets context of the previous turn.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The context of previous turn.
|
||||
/// </value>
|
||||
public QnARequestContext Context { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets QnA Id of the current question asked (if avaliable).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Id of the current question asked.
|
||||
/// </value>
|
||||
public int QnAId { get; set; }
|
||||
|
||||
public Metadata[] StrictFilters { get; set; }
|
||||
|
||||
[JsonProperty("metadataBoost")]
|
||||
|
|
|
@ -39,9 +39,7 @@ namespace Microsoft.Bot.Builder.AI.QnA
|
|||
/// <summary>
|
||||
/// Gets or sets qnA Maker options.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// QnA Maker options.
|
||||
/// </value>
|
||||
/// <value>The options for QnAMaker.</value>
|
||||
public QnAMakerOptions Options { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -162,6 +160,9 @@ namespace Microsoft.Bot.Builder.AI.QnA
|
|||
{
|
||||
hydratedOptions.MetadataBoost = queryOptions.MetadataBoost;
|
||||
}
|
||||
|
||||
hydratedOptions.Context = queryOptions.Context;
|
||||
hydratedOptions.QnAId = queryOptions.QnAId;
|
||||
}
|
||||
|
||||
return hydratedOptions;
|
||||
|
@ -178,6 +179,8 @@ namespace Microsoft.Bot.Builder.AI.QnA
|
|||
strictFilters = options.StrictFilters,
|
||||
metadataBoost = options.MetadataBoost,
|
||||
scoreThreshold = options.ScoreThreshold,
|
||||
context = options.Context,
|
||||
qnaId = options.QnAId,
|
||||
}, Formatting.None);
|
||||
|
||||
var httpRequestHelper = new HttpRequestUtils(httpClient);
|
||||
|
@ -199,6 +202,8 @@ namespace Microsoft.Bot.Builder.AI.QnA
|
|||
Top = options.Top,
|
||||
StrictFilters = options.StrictFilters,
|
||||
MetadataBoost = options.MetadataBoost,
|
||||
Context = options.Context,
|
||||
QnAId = options.QnAId,
|
||||
};
|
||||
var traceActivity = Activity.CreateTraceActivity(QnAMaker.QnAMakerName, QnAMaker.QnAMakerTraceType, traceInfo, QnAMaker.QnAMakerTraceLabel);
|
||||
await turnContext.SendActivityAsync(traceActivity).ConfigureAwait(false);
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
using Microsoft.Bot.Builder.AI.Luis;
|
||||
using Microsoft.Bot.Builder.Dialogs.Adaptive.Actions;
|
||||
using Microsoft.Bot.Builder.Dialogs.Adaptive.TriggerHandlers;
|
||||
using Microsoft.Bot.Builder.Dialogs.Adaptive.Input;
|
||||
using Microsoft.Bot.Builder.Dialogs.Adaptive.Recognizers;
|
||||
using Microsoft.Bot.Builder.Dialogs.Adaptive.TriggerHandlers;
|
||||
using Microsoft.Bot.Builder.Dialogs.Debugging;
|
||||
using Microsoft.Bot.Builder.Dialogs.Declarative;
|
||||
using Microsoft.Bot.Builder.Dialogs.Declarative.Converters;
|
||||
|
|
|
@ -8,8 +8,8 @@ using System.Linq;
|
|||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Bot.Builder.Dialogs.Adaptive.TriggerHandlers;
|
||||
using Microsoft.Bot.Builder.Dialogs.Adaptive.Selectors;
|
||||
using Microsoft.Bot.Builder.Dialogs.Adaptive.TriggerHandlers;
|
||||
using Microsoft.Bot.Builder.Dialogs.Debugging;
|
||||
using Microsoft.Bot.Builder.LanguageGeneration;
|
||||
using Microsoft.Bot.Schema;
|
||||
|
@ -83,6 +83,11 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive
|
|||
/// </value>
|
||||
public string DefaultResultProperty { get; set; } = "dialog.result";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the dialogs which make up the AdaptiveDialog
|
||||
/// </summary>
|
||||
public DialogSet Dialogs => this._dialogs;
|
||||
|
||||
public override IBotTelemetryClient TelemetryClient
|
||||
{
|
||||
get
|
||||
|
|
|
@ -60,11 +60,11 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Recognizers.Text" Version="1.1.3" />
|
||||
<PackageReference Include="Microsoft.Recognizers.Text.Choice" Version="1.1.3" />
|
||||
<PackageReference Include="Microsoft.Recognizers.Text.DateTime" Version="1.1.3" />
|
||||
<PackageReference Include="Microsoft.Recognizers.Text.Number" Version="1.1.3" />
|
||||
<PackageReference Include="Microsoft.Recognizers.Text.Sequence" Version="1.1.3" />
|
||||
<PackageReference Include="Microsoft.Recognizers.Text" Version="1.2.6.1" />
|
||||
<PackageReference Include="Microsoft.Recognizers.Text.Choice" Version="1.2.6.1" />
|
||||
<PackageReference Include="Microsoft.Recognizers.Text.DateTime" Version="1.2.6.1" />
|
||||
<PackageReference Include="Microsoft.Recognizers.Text.Number" Version="1.2.6.1" />
|
||||
<PackageReference Include="Microsoft.Recognizers.Text.Sequence" Version="1.2.6.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ProjectExtensions>
|
||||
|
|
|
@ -106,7 +106,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Choices
|
|||
|
||||
private static List<ModelResult<FoundChoice>> RecognizeOrdinal(string utterance, string culture)
|
||||
{
|
||||
var model = new NumberRecognizer(culture).GetOrdinalModel(culture);
|
||||
var model = new NumberRecognizer(culture, NumberOptions.SuppressExtendedTypes).GetOrdinalModel(culture);
|
||||
var result = model.Parse(utterance);
|
||||
return result.Select(r =>
|
||||
new ModelResult<FoundChoice>
|
||||
|
@ -123,7 +123,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Choices
|
|||
|
||||
private static List<ModelResult<FoundChoice>> RecognizeNumber(string utterance, string culture)
|
||||
{
|
||||
var model = new NumberRecognizer(culture).GetNumberModel(culture);
|
||||
var model = new NumberRecognizer(culture, NumberOptions.SuppressExtendedTypes).GetNumberModel(culture);
|
||||
var result = model.Parse(utterance);
|
||||
return result.Select(r =>
|
||||
new ModelResult<FoundChoice>
|
||||
|
|
|
@ -9,12 +9,21 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace Microsoft.Bot.Builder.Dialogs
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="Dialog"/> that is composed of other dialogs.
|
||||
/// </summary>
|
||||
/// <remarks>A component dialog has an inner <see cref="DialogSet"/> and <see cref="DialogContext"/>,
|
||||
/// which provides an inner dialog stack that is hidden from the parent dialog.</remarks>
|
||||
public class ComponentDialog : DialogContainer
|
||||
{
|
||||
public const string PersistedDialogState = "dialogs";
|
||||
|
||||
private bool initialized = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ComponentDialog"/> class.
|
||||
/// </summary>
|
||||
/// <param name="dialogId">The ID to assign to the new dialog within the parent dialog set.</param>
|
||||
public ComponentDialog(string dialogId = null)
|
||||
: base(dialogId)
|
||||
{
|
||||
|
@ -23,10 +32,12 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
public string InitialDialogId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets or set the <see cref="IBotTelemetryClient"/> to use.
|
||||
/// When setting this property, all the contained dialogs TelemetryClient properties are also set.
|
||||
/// Gets or sets the <see cref="IBotTelemetryClient"/> to use for logging.
|
||||
/// When setting this property, all of the contained dialogs' <see cref="Dialog.TelemetryClient"/>
|
||||
/// properties are also set.
|
||||
/// </summary>
|
||||
/// <value>The <see cref="IBotTelemetryClient"/> to use when logging.</value>
|
||||
/// <seealso cref="DialogSet.TelemetryClient"/>
|
||||
public new IBotTelemetryClient TelemetryClient
|
||||
{
|
||||
get
|
||||
|
@ -41,6 +52,18 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the dialog is started and pushed onto the parent's dialog stack.
|
||||
/// </summary>
|
||||
/// <param name="outerDc">The parent <see cref="DialogContext"/> for the current turn of conversation.</param>
|
||||
/// <param name="options">Optional, initial information to pass to the dialog.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
/// <remarks>If the task is successful, the result indicates whether the dialog is still
|
||||
/// active after the turn has been processed by the dialog.</remarks>
|
||||
/// <seealso cref="OnBeginDialogAsync(DialogContext, object, CancellationToken)"/>
|
||||
/// <seealso cref="DialogContext.BeginDialogAsync(string, object, CancellationToken)"/>
|
||||
public override async Task<DialogTurnResult> BeginDialogAsync(DialogContext outerDc, object options = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (options is CancellationToken)
|
||||
|
@ -61,12 +84,6 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
// Check for end of inner dialog
|
||||
if (turnResult.Status != DialogTurnStatus.Waiting)
|
||||
{
|
||||
if (turnResult.Status == DialogTurnStatus.Cancelled)
|
||||
{
|
||||
await EndComponentAsync(outerDc, turnResult.Result, cancellationToken).ConfigureAwait(false);
|
||||
return new DialogTurnResult(DialogTurnStatus.Cancelled, turnResult.Result);
|
||||
}
|
||||
|
||||
// Return result to calling dialog
|
||||
return await EndComponentAsync(outerDc, turnResult.Result, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
@ -75,6 +92,26 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
return Dialog.EndOfTurn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the dialog is _continued_, where it is the active dialog and the
|
||||
/// user replies with a new activity.
|
||||
/// </summary>
|
||||
/// <param name="outerDc">The parent <see cref="DialogContext"/> for the current turn of conversation.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
/// <remarks>If the task is successful, the result indicates whether the dialog is still
|
||||
/// active after the turn has been processed by the dialog. The result may also contain a
|
||||
/// return value.
|
||||
///
|
||||
/// If this method is *not* overridden, the component dialog calls the
|
||||
/// <see cref="DialogContext.ContinueDialogAsync(CancellationToken)"/> method on its inner
|
||||
/// dialog context. If the inner dialog stack is empty, the component dialog ends, and if
|
||||
/// a <see cref="DialogTurnResult.Result"/> is available, the component dialog uses that as
|
||||
/// its return value.
|
||||
/// </remarks>
|
||||
/// <seealso cref="OnContinueDialogAsync(DialogContext, CancellationToken)"/>
|
||||
/// <seealso cref="DialogContext.ContinueDialogAsync(CancellationToken)"/>
|
||||
public override async Task<DialogTurnResult> ContinueDialogAsync(DialogContext outerDc, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
// Continue execution of inner dialog
|
||||
|
@ -100,6 +137,31 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a child dialog on the parent's dialog stack completed this turn, returning
|
||||
/// control to this dialog component.
|
||||
/// </summary>
|
||||
/// <param name="outerDc">The <see cref="DialogContext"/> for the current turn of conversation.</param>
|
||||
/// <param name="reason">Reason why the dialog resumed.</param>
|
||||
/// <param name="result">Optional, value returned from the dialog that was called. The type
|
||||
/// of the value returned is dependent on the child dialog.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
/// <remarks>If the task is successful, the result indicates whether this dialog is still
|
||||
/// active after this dialog turn has been processed.
|
||||
///
|
||||
/// Generally, the child dialog was started with a call to
|
||||
/// <see cref="BeginDialogAsync(DialogContext, object, CancellationToken)"/> in the parent's
|
||||
/// context. However, if the
|
||||
/// <see cref="DialogContext.ReplaceDialogAsync(string, object, CancellationToken)"/> method
|
||||
/// is called, the logical child dialog may be different than the original.
|
||||
///
|
||||
/// If this method is *not* overridden, the dialog automatically calls its
|
||||
/// <see cref="RepromptDialogAsync(ITurnContext, DialogInstance, CancellationToken)"/> when
|
||||
/// the user replies.
|
||||
/// </remarks>
|
||||
/// <seealso cref="RepromptDialogAsync(ITurnContext, DialogInstance, CancellationToken)"/>
|
||||
public override async Task<DialogTurnResult> ResumeDialogAsync(DialogContext outerDc, DialogReason reason, object result = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (result is CancellationToken)
|
||||
|
@ -118,6 +180,16 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
return Dialog.EndOfTurn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the dialog should re-prompt the user for input.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="instance">State information for this dialog.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
/// <seealso cref="OnRepromptDialogAsync(ITurnContext, DialogInstance, CancellationToken)"/>
|
||||
/// <seealso cref="DialogContext.RepromptDialogAsync(CancellationToken)"/>
|
||||
public override async Task RepromptDialogAsync(ITurnContext turnContext, DialogInstance instance, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
// Delegate to inner dialog.
|
||||
|
@ -128,6 +200,20 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
await OnRepromptDialogAsync(turnContext, instance, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the dialog is ending.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="instance">State information associated with the instance of this component
|
||||
/// dialog on its parent's dialog stack.</param>
|
||||
/// <param name="reason">Reason why the dialog ended.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
/// <remarks>When this method is called from the parent dialog's context, the component dialog
|
||||
/// cancels all of the dialogs on its inner dialog stack before ending.</remarks>
|
||||
/// <seealso cref="OnEndDialogAsync(ITurnContext, DialogInstance, DialogReason, CancellationToken)"/>
|
||||
/// <seealso cref="DialogContext.EndDialogAsync(object, CancellationToken)"/>
|
||||
public override async Task EndDialogAsync(ITurnContext turnContext, DialogInstance instance, DialogReason reason, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
// Forward cancel to inner dialogs
|
||||
|
@ -141,14 +227,15 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a dialog to the component dialog.
|
||||
/// Adds a new <see cref="Dialog"/> to the component dialog and returns the updated component.
|
||||
/// </summary>
|
||||
/// <param name="dialog">The dialog to add.</param>
|
||||
/// <returns>The updated <see cref="ComponentDialog"/>.</returns>
|
||||
/// <remarks>Adding a new dialog will inherit the <see cref="IBotTelemetryClient"/> of the ComponentDialog.</remarks>
|
||||
public override Dialog AddDialog(Dialog dialog)
|
||||
/// <returns>The <see cref="ComponentDialog"/> after the operation is complete.</returns>
|
||||
/// <remarks>The added dialog's <see cref="Dialog.TelemetryClient"/> is set to the
|
||||
/// <see cref="TelemetryClient"/> of the component dialog.</remarks>
|
||||
public virtual ComponentDialog AddDialog(Dialog dialog)
|
||||
{
|
||||
base.AddDialog(dialog);
|
||||
this._dialogs.Add(dialog);
|
||||
|
||||
if (this.InitialDialogId == null)
|
||||
{
|
||||
|
@ -158,16 +245,6 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a dialog by ID (ONLY in this ComponentDialog, use DialogContext.FindDialog to get scoped dialogs).
|
||||
/// </summary>
|
||||
/// <param name="dialogId">The ID of the dialog to find.</param>
|
||||
/// <returns>The dialog; or <c>null</c> if there is not a match for the ID.</returns>
|
||||
public new Dialog FindDialog(string dialogId)
|
||||
{
|
||||
return _dialogs.Find(dialogId);
|
||||
}
|
||||
|
||||
public override DialogContext CreateChildContext(DialogContext dc)
|
||||
{
|
||||
var childDc = this.CreateInnerDc(dc.Context, dc.ActiveDialog);
|
||||
|
@ -194,26 +271,108 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the dialog is started and pushed onto the parent's dialog stack.
|
||||
/// </summary>
|
||||
/// <param name="innerDc">The inner <see cref="DialogContext"/> for the current turn of conversation.</param>
|
||||
/// <param name="options">Optional, initial information to pass to the dialog.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
/// <remarks>If the task is successful, the result indicates whether the dialog is still
|
||||
/// active after the turn has been processed by the dialog.
|
||||
///
|
||||
/// By default, this calls the
|
||||
/// <see cref="Dialog.BeginDialogAsync(DialogContext, object, CancellationToken)"/> method
|
||||
/// of the component dialog's initial dialog, as defined by <see cref="InitialDialogId"/>.
|
||||
///
|
||||
/// Override this method in a derived class to implement interrupt logic.</remarks>
|
||||
/// <seealso cref="BeginDialogAsync(DialogContext, object, CancellationToken)"/>
|
||||
protected virtual Task<DialogTurnResult> OnBeginDialogAsync(DialogContext innerDc, object options, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return innerDc.BeginDialogAsync(InitialDialogId, options, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the dialog is _continued_, where it is the active dialog and the
|
||||
/// user replies with a new activity.
|
||||
/// </summary>
|
||||
/// <param name="innerDc">The inner <see cref="DialogContext"/> for the current turn of conversation.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
/// <remarks>If the task is successful, the result indicates whether the dialog is still
|
||||
/// active after the turn has been processed by the dialog. The result may also contain a
|
||||
/// return value.
|
||||
///
|
||||
/// By default, this calls the currently active inner dialog's
|
||||
/// <see cref="Dialog.ContinueDialogAsync(DialogContext, CancellationToken)"/> method.
|
||||
///
|
||||
/// Override this method in a derived class to implement interrupt logic.</remarks>
|
||||
/// <seealso cref=" ContinueDialogAsync(DialogContext, CancellationToken)"/>
|
||||
protected virtual Task<DialogTurnResult> OnContinueDialogAsync(DialogContext innerDc, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return innerDc.ContinueDialogAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the dialog is ending.
|
||||
/// </summary>
|
||||
/// <param name="context">The context object for this turn.</param>
|
||||
/// <param name="instance">State information associated with the inner dialog stack of this
|
||||
/// component dialog.</param>
|
||||
/// <param name="reason">Reason why the dialog ended.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
/// <remarks>Override this method in a derived class to implement any additional logic that
|
||||
/// should happen at the component level, after all inner dialogs have been canceled.</remarks>
|
||||
/// <seealso cref="EndDialogAsync(ITurnContext, DialogInstance, DialogReason, CancellationToken)"/>
|
||||
protected virtual Task OnEndDialogAsync(ITurnContext context, DialogInstance instance, DialogReason reason, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the dialog should re-prompt the user for input.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="instance">State information associated with the inner dialog stack of this
|
||||
/// component dialog.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
/// <remarks>Override this method in a derived class to implement any additional logic that
|
||||
/// should happen at the component level, after the re-prompt operation completes for the inner
|
||||
/// dialog.</remarks>
|
||||
/// <seealso cref="RepromptDialogAsync(ITurnContext, DialogInstance, CancellationToken)"/>
|
||||
protected virtual Task OnRepromptDialogAsync(ITurnContext turnContext, DialogInstance instance, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends the component dialog in its parent's context.
|
||||
/// </summary>
|
||||
/// <param name="outerDc">The parent <see cref="DialogContext"/> for the current turn of conversation.</param>
|
||||
/// <param name="result">Optional, value to return from the dialog component to the parent context.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>If the task is successful, the result indicates that the dialog ended after the
|
||||
/// turn was processed by the dialog.
|
||||
///
|
||||
/// In general, the parent context is the dialog or bot turn handler that started the dialog.
|
||||
/// If the parent is a dialog, the stack calls the parent's
|
||||
/// <see cref="Dialog.ResumeDialogAsync(DialogContext, DialogReason, object, CancellationToken)"/>
|
||||
/// method to return a result to the parent dialog. If the parent dialog does not implement
|
||||
/// `ResumeDialogAsync`, then the parent will end, too, and the result is passed to the next
|
||||
/// parent context, if one exists.
|
||||
///
|
||||
/// The returned <see cref="DialogTurnResult"/> contains the return value in its
|
||||
/// <see cref="DialogTurnResult.Result"/> property.</remarks>
|
||||
/// <seealso cref="BeginDialogAsync(DialogContext, object, CancellationToken)"/>
|
||||
/// <seealso cref="ContinueDialogAsync(DialogContext, CancellationToken)"/>
|
||||
protected virtual Task<DialogTurnResult> EndComponentAsync(DialogContext outerDc, object result, CancellationToken cancellationToken)
|
||||
{
|
||||
return outerDc.EndDialogAsync(result, cancellationToken);
|
||||
|
|
|
@ -16,6 +16,10 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
[DebuggerDisplay("{Id}")]
|
||||
public abstract class Dialog
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="DialogTurnResult"/> that indicates that the current dialog is still
|
||||
/// active and waiting for input from the user next turn.
|
||||
/// </summary>
|
||||
public static readonly DialogTurnResult EndOfTurn = new DialogTurnResult(DialogTurnStatus.Waiting);
|
||||
|
||||
private IBotTelemetryClient _telemetryClient;
|
||||
|
@ -62,9 +66,10 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
public List<string> Tags { get; private set; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the telemetry client for logging events.
|
||||
/// Gets or sets the <see cref="IBotTelemetryClient"/> to use for logging.
|
||||
/// </summary>
|
||||
/// <value>The Telemetry Client logger.</value>
|
||||
/// <value>The <see cref="IBotTelemetryClient"/> to use for logging.</value>
|
||||
/// <seealso cref="DialogSet.TelemetryClient"/>
|
||||
public virtual IBotTelemetryClient TelemetryClient
|
||||
{
|
||||
get
|
||||
|
@ -79,29 +84,33 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method called when a new dialog has been pushed onto the stack and is being activated.
|
||||
/// Called when the dialog is started and pushed onto the dialog stack.
|
||||
/// </summary>
|
||||
/// <param name="dc">The dialog context for the current turn of conversation.</param>
|
||||
/// <param name="options">(Optional) additional argument(s) to pass to the dialog being started.</param>
|
||||
/// <param name="dc">The <see cref="DialogContext"/> for the current turn of conversation.</param>
|
||||
/// <param name="options">Optional, initial information to pass to the dialog.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
/// <remarks>If the task is successful, the result indicates whether the dialog is still
|
||||
/// active after the turn has been processed by the dialog.</remarks>
|
||||
/// <seealso cref="DialogContext.BeginDialogAsync(string, object, CancellationToken)"/>
|
||||
public abstract Task<DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Method called when an instance of the dialog is the "current" dialog and the
|
||||
/// user replies with a new activity. The dialog will generally continue to receive the user's
|
||||
/// replies until it calls either `EndDialogAsync()` or `BeginDialogAsync()`.
|
||||
/// If this method is NOT implemented then the dialog will automatically be ended when the user replies.
|
||||
/// Called when the dialog is _continued_, where it is the active dialog and the
|
||||
/// user replies with a new activity.
|
||||
/// </summary>
|
||||
/// <param name="dc">The dialog context for the current turn of conversation.</param>
|
||||
/// <param name="dc">The <see cref="DialogContext"/> for the current turn of conversation.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
/// <remarks>If the task is successful, the result indicates whether the dialog is still
|
||||
/// active after the turn has been processed by the dialog.</remarks>
|
||||
/// active after the turn has been processed by the dialog. The result may also contain a
|
||||
/// return value.
|
||||
///
|
||||
/// If this method is *not* overridden, the dialog automatically ends when the user replies.
|
||||
/// </remarks>
|
||||
/// <seealso cref="DialogContext.ContinueDialogAsync(CancellationToken)"/>
|
||||
public virtual async Task<DialogTurnResult> ContinueDialogAsync(DialogContext dc, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
// By default just end the current dialog.
|
||||
|
@ -109,20 +118,26 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method called when an instance of the dialog is being returned to from another
|
||||
/// dialog that was started by the current instance using `BeginDialogAsync()`.
|
||||
/// If this method is NOT implemented then the dialog will be automatically ended with a call
|
||||
/// to `EndDialogAsync()`. Any result passed from the called dialog will be passed
|
||||
/// to the current dialog's parent.
|
||||
/// Called when a child dialog completed this turn, returning control to this dialog.
|
||||
/// </summary>
|
||||
/// <param name="dc">The dialog context for the current turn of the conversation.</param>
|
||||
/// <param name="reason">An enum indicating why the dialog resumed.</param>
|
||||
/// <param name="result">(Optional) value returned from the dialog that was called. The type of the value returned is dependent on the dialog that was called.</param>
|
||||
/// <param name="reason">Reason why the dialog resumed.</param>
|
||||
/// <param name="result">Optional, value returned from the dialog that was called. The type
|
||||
/// of the value returned is dependent on the child dialog.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
/// <remarks>If the task is successful, the result indicates whether the dialog is still
|
||||
/// active after the turn has been processed by the dialog.</remarks>
|
||||
/// <remarks>If the task is successful, the result indicates whether this dialog is still
|
||||
/// active after this dialog turn has been processed.
|
||||
///
|
||||
/// Generally, the child dialog was started with a call to
|
||||
/// <see cref="BeginDialogAsync(DialogContext, object, CancellationToken)"/>. However, if the
|
||||
/// <see cref="DialogContext.ReplaceDialogAsync(string, object, CancellationToken)"/> method
|
||||
/// is called, the logical child dialog may be different than the original.
|
||||
///
|
||||
/// If this method is *not* overridden, the dialog automatically ends when the user replies.
|
||||
/// </remarks>
|
||||
/// <seealso cref="DialogContext.EndDialogAsync(object, CancellationToken)"/>
|
||||
public virtual async Task<DialogTurnResult> ResumeDialogAsync(DialogContext dc, DialogReason reason, object result = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (result is CancellationToken)
|
||||
|
@ -135,19 +150,29 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method called when the dialog has been requested to re-prompt the user for input.
|
||||
/// Called when the dialog should re-prompt the user for input.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">Context for the current turn of conversation with the user.</param>
|
||||
/// <param name="instance">The instance of the dialog on the stack.</param>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="instance">State information for this dialog.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
/// <seealso cref="DialogContext.RepromptDialogAsync(CancellationToken)"/>
|
||||
public virtual Task RepromptDialogAsync(ITurnContext turnContext, DialogInstance instance, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
// No-op by default
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the dialog is ending.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="instance">State information associated with the instance of this dialog on the dialog stack.</param>
|
||||
/// <param name="reason">Reason why the dialog ended.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
public virtual Task EndDialogAsync(ITurnContext turnContext, DialogInstance instance, DialogReason reason, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
// No-op by default
|
||||
|
|
|
@ -16,13 +16,7 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
|
||||
public abstract DialogContext CreateChildContext(DialogContext dc);
|
||||
|
||||
public virtual Dialog AddDialog(Dialog dialog)
|
||||
{
|
||||
this._dialogs.Add(dialog);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Dialog FindDialog(string dialogId)
|
||||
public virtual Dialog FindDialog(string dialogId)
|
||||
{
|
||||
return this._dialogs.Find(dialogId);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,11 @@ using static Microsoft.Bot.Builder.Dialogs.Debugging.DebugSupport;
|
|||
|
||||
namespace Microsoft.Bot.Builder.Dialogs
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides context for the current state of the dialog stack.
|
||||
/// </summary>
|
||||
/// <remarks>The <see cref="Context"/> property contains the <see cref="ITurnContext"/>
|
||||
/// for the current turn.</remarks>
|
||||
[System.Diagnostics.DebuggerDisplay("{GetType().Name}[{ActiveDialog?.Id}]")]
|
||||
public class DialogContext
|
||||
{
|
||||
|
@ -54,11 +59,17 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
/// <summary>
|
||||
/// Gets the context for the current turn of conversation.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The context for the current turn of conversation.
|
||||
/// </value>
|
||||
public ITurnContext Context { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current dialog stack.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The current dialog stack.
|
||||
/// </value>
|
||||
public List<DialogInstance> Stack { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -67,7 +78,7 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
public DialogStateManager State { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets parent context.
|
||||
/// Gets or sets the parent <see cref="DialogContext"/>, if any. Used when searching for the ID of a dialog to start.
|
||||
/// </summary>
|
||||
public DialogContext Parent { get; set; }
|
||||
|
||||
|
@ -114,12 +125,18 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes a new dialog onto the dialog stack.
|
||||
/// Starts a new dialog and pushes it onto the dialog stack.
|
||||
/// </summary>
|
||||
/// <param name="dialogId">ID of the dialog to start.</param>
|
||||
/// <param name="options">(Optional) additional argument(s) to pass to the dialog being started.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
/// <param name="options">Optional, information to pass to the dialog being started.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>If the task is successful, the result indicates whether the dialog is still
|
||||
/// active after the turn has been processed by the dialog.</remarks>
|
||||
/// <seealso cref="EndDialogAsync(object, CancellationToken)"/>
|
||||
/// <seealso cref="PromptAsync(string, PromptOptions, CancellationToken)"/>
|
||||
/// <seealso cref="Dialog.BeginDialogAsync(DialogContext, object, CancellationToken)"/>
|
||||
public async Task<DialogTurnResult> BeginDialogAsync(string dialogId, object options = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (string.IsNullOrEmpty(dialogId))
|
||||
|
@ -155,10 +172,15 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
/// Helper function to simplify formatting the options for calling a prompt dialog. This helper will
|
||||
/// take a `PromptOptions` argument and then call[begin(context, dialogId, options)](#begin).
|
||||
/// </summary>
|
||||
/// <param name="dialogId">ID of the prompt to start.</param>
|
||||
/// <param name="options">Contains a Prompt, potentially a RetryPrompt and if using ChoicePrompt, Choices.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
/// <param name="dialogId">ID of the prompt dialog to start.</param>
|
||||
/// <param name="options">Information to pass to the prompt dialog being started.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>If the task is successful, the result indicates whether the dialog is still
|
||||
/// active after the turn has been processed by the dialog.</remarks>
|
||||
/// <seealso cref="BeginDialogAsync(string, object, CancellationToken)"/>
|
||||
/// <seealso cref="Prompt{T}.BeginDialogAsync(DialogContext, object, CancellationToken)"/>
|
||||
public async Task<DialogTurnResult> PromptAsync(string dialogId, PromptOptions options, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (string.IsNullOrEmpty(dialogId))
|
||||
|
@ -175,12 +197,20 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Continues execution of the active dialog, if there is one, by passing the context object to
|
||||
/// its `Dialog.ContinueDialogAsync()` method. You can check `context.responded` after the call completes
|
||||
/// to determine if a dialog was run and a reply was sent to the user.
|
||||
/// Continues execution of the active dialog, if there is one, by passing the current
|
||||
/// <see cref="DialogContext"/> to the active dialog's
|
||||
/// <see cref="Dialog.ContinueDialogAsync(DialogContext, CancellationToken)"/> method.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>If the task is successful, the result indicates whether the dialog is still
|
||||
/// active after the turn has been processed by the dialog.
|
||||
///
|
||||
/// Check the <see cref="TurnContext.Responded"/> property after the call completes
|
||||
/// to determine if the active dialog sent a reply message to the user.
|
||||
/// </remarks>
|
||||
/// <seealso cref="Dialog.ContinueDialogAsync(DialogContext, CancellationToken)"/>
|
||||
public async Task<DialogTurnResult> ContinueDialogAsync(CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
// if we are continuing and haven't emitted the activityReceived event, emit it
|
||||
|
@ -222,9 +252,25 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
/// automatically ended as well and the result passed to its parent. If there are no more
|
||||
/// parent dialogs on the stack then processing of the turn will end.
|
||||
/// </summary>
|
||||
/// <param name="result">(Optional) result to pass to the parent dialogs.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
/// <param name="result">Optional, result to pass to the parent context.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>If the task is successful, the result indicates that the dialog ended after the
|
||||
/// turn was processed by the dialog.
|
||||
///
|
||||
/// In general, the parent context is the dialog or bot turn handler that started the dialog.
|
||||
/// If the parent is a dialog, the stack calls the parent's
|
||||
/// <see cref="Dialog.ResumeDialogAsync(DialogContext, DialogReason, object, CancellationToken)"/> method to
|
||||
/// return a result to the parent dialog. If the parent dialog does not implement `ResumeDialogAsyn`,
|
||||
/// then the parent will end, too, and the result passed to the next parent context.
|
||||
///
|
||||
/// The returned <see cref="DialogTurnResult"/> contains the return value in its
|
||||
/// <see cref="DialogTurnResult.Result"/> property.</remarks>
|
||||
/// <seealso cref="BeginDialogAsync(string, object, CancellationToken)"/>
|
||||
/// <seealso cref="PromptAsync(string, PromptOptions, CancellationToken)"/>
|
||||
/// <seealso cref="ReplaceDialogAsync(string, object, CancellationToken)"/>
|
||||
/// <seealso cref="Dialog.EndDialogAsync(ITurnContext, DialogInstance, DialogReason, CancellationToken)"/>
|
||||
public async Task<DialogTurnResult> EndDialogAsync(object result = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (result is CancellationToken token)
|
||||
|
@ -256,7 +302,7 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels all dialogs on the dialog stack.
|
||||
/// Deletes any existing dialog stack thus cancelling all dialogs on the stack.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
|
@ -281,8 +327,19 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
/// </summary>
|
||||
/// <param name="eventName">The event.</param>
|
||||
/// <param name="eventValue">The event value.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The dialog context.</returns>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>If the task is successful, the result indicates that dialogs were canceled after the
|
||||
/// turn was processed by the dialog or that the stack was already empty.
|
||||
///
|
||||
/// In general, the parent context is the dialog or bot turn handler that started the dialog.
|
||||
/// If the parent is a dialog, the stack calls the parent's
|
||||
/// <see cref="Dialog.ResumeDialogAsync(DialogContext, DialogReason, object, CancellationToken)"/>
|
||||
/// method to return a result to the parent dialog. If the parent dialog does not implement
|
||||
/// `ResumeDialogAsync`, then the parent will end, too, and the result is passed to the next
|
||||
/// parent context.</remarks>
|
||||
/// <seealso cref="EndDialogAsync(object, CancellationToken)"/>
|
||||
public async Task<DialogTurnResult> CancelAllDialogsAsync(string eventName, object eventValue = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (eventValue is CancellationToken)
|
||||
|
@ -334,13 +391,18 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends the active dialog and starts a new dialog in its place. This is particularly useful
|
||||
/// for creating loops or redirecting to another dialog.
|
||||
/// Starts a new dialog and replaces on the stack the currently active dialog with the new one.
|
||||
/// This is particularly useful for creating loops or redirecting to another dialog.
|
||||
/// </summary>
|
||||
/// <param name="dialogId">ID of the new dialog to start.</param>
|
||||
/// <param name="options">(Optional) additional argument(s) to pass to the new dialog.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
/// <param name="options">Optional, information to pass to the dialog being started.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>If the task is successful, the result indicates whether the dialog is still
|
||||
/// active after the turn has been processed by the dialog.</remarks>
|
||||
/// <seealso cref="BeginDialogAsync(string, object, CancellationToken)"/>
|
||||
/// <seealso cref="EndDialogAsync(object, CancellationToken)"/>
|
||||
public async Task<DialogTurnResult> ReplaceDialogAsync(string dialogId, object options = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (options is CancellationToken)
|
||||
|
@ -358,10 +420,14 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls reprompt on the currently active dialog, if there is one. Used with Prompts that have a reprompt behavior.
|
||||
/// Calls the currently active dialog's
|
||||
/// <see cref="Dialog.RepromptDialogAsync(ITurnContext, DialogInstance, CancellationToken)"/>
|
||||
/// method. Used with dialogs that implement a re-prompt behavior.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <seealso cref="Dialog.RepromptDialogAsync(ITurnContext, DialogInstance, CancellationToken)"/>
|
||||
public async Task RepromptDialogAsync(CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
// Emit 'RepromptDialog' event
|
||||
|
|
|
@ -9,16 +9,16 @@ using Newtonsoft.Json.Serialization;
|
|||
namespace Microsoft.Bot.Builder.Dialogs
|
||||
{
|
||||
/// <summary>
|
||||
/// Tracking information for a dialog on the stack.
|
||||
/// Contains state information associated with a <see cref="Dialog"/> on a dialog stack.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{Id}")]
|
||||
public class DialogInstance
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the ID of the dialog this instance is for.
|
||||
/// Gets or sets the ID of the dialog.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// ID of the dialog this instance is for.
|
||||
/// The ID of the dialog.
|
||||
/// </value>
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
|
|
@ -3,36 +3,46 @@
|
|||
|
||||
namespace Microsoft.Bot.Builder.Dialogs
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates in which a dialog-related method is being called.
|
||||
/// </summary>
|
||||
public enum DialogReason
|
||||
{
|
||||
/// <summary>
|
||||
/// A dialog is being started through a call to `DialogContext.BeginAsync()`.
|
||||
/// A dialog was started.
|
||||
/// </summary>
|
||||
/// <seealso cref="DialogContext.BeginDialogAsync(string, object, System.Threading.CancellationToken)"/>
|
||||
/// <seealso cref="DialogContext.PromptAsync(string, PromptOptions, System.Threading.CancellationToken)"/>
|
||||
BeginCalled,
|
||||
|
||||
/// <summary>
|
||||
/// A dialog is being continued through a call to `DialogContext.ContinueDialogAsync()`.
|
||||
/// A dialog was continued.
|
||||
/// </summary>
|
||||
/// <seealso cref="DialogContext.ContinueDialogAsync(System.Threading.CancellationToken)"/>
|
||||
ContinueCalled,
|
||||
|
||||
/// <summary>
|
||||
/// A dialog ended normally through a call to `DialogContext.EndDialogAsync()`.
|
||||
/// A dialog was ended normally.
|
||||
/// </summary>
|
||||
/// <seealso cref="DialogContext.EndDialogAsync(object, System.Threading.CancellationToken)"/>
|
||||
EndCalled,
|
||||
|
||||
/// <summary>
|
||||
/// A dialog is ending because it's being replaced through a call to `DialogContext.ReplaceDialogAsync()`.
|
||||
/// A dialog was ending because it was replaced.
|
||||
/// </summary>
|
||||
/// <seealso cref="DialogContext.ReplaceDialogAsync(string, object, System.Threading.CancellationToken)"/>
|
||||
ReplaceCalled,
|
||||
|
||||
/// <summary>
|
||||
/// A dialog was cancelled as part of a call to `DialogContext.CancelAllDialogsAsync()`.
|
||||
/// A dialog was canceled.
|
||||
/// </summary>
|
||||
/// <seealso cref="DialogContext.CancelAllDialogsAsync(System.Threading.CancellationToken)"/>
|
||||
CancelCalled,
|
||||
|
||||
/// <summary>
|
||||
/// A step was advanced through a call to `WaterfallStepContext.NextAsync()`.
|
||||
/// A preceding step of the dialog was skipped.
|
||||
/// </summary>
|
||||
/// <seealso cref="WaterfallStepContext.NextAsync(object, System.Threading.CancellationToken)"/>
|
||||
NextCalled,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ using System.Threading.Tasks;
|
|||
namespace Microsoft.Bot.Builder.Dialogs
|
||||
{
|
||||
/// <summary>
|
||||
/// A related set of dialogs that can all call each other.
|
||||
/// A collection of <see cref="Dialog"/> objects that can all call each other.
|
||||
/// </summary>
|
||||
public class DialogSet
|
||||
{
|
||||
|
@ -18,6 +18,15 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
|
||||
private IBotTelemetryClient _telemetryClient;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DialogSet"/> class.
|
||||
/// </summary>
|
||||
/// <param name="dialogState">The state property accessor with which to manage the stack for
|
||||
/// this dialog set.</param>
|
||||
/// <remarks>To start and control the dialogs in this dialog set, create a <see cref="DialogContext"/>
|
||||
/// and use its methods to start, continue, or end dialogs. To create a dialog context,
|
||||
/// call <see cref="CreateContextAsync(ITurnContext, CancellationToken)"/>.
|
||||
/// </remarks>
|
||||
public DialogSet(IStatePropertyAccessor<DialogState> dialogState)
|
||||
{
|
||||
_dialogState = dialogState ?? throw new ArgumentNullException(nameof(dialogState));
|
||||
|
@ -31,10 +40,11 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IBotTelemetryClient"/> to use.
|
||||
/// When setting this property, all of the contained dialogs' TelemetryClient properties are also set.
|
||||
/// Gets or sets the <see cref="IBotTelemetryClient"/> to use for logging.
|
||||
/// </summary>
|
||||
/// <value>The <see cref="IBotTelemetryClient"/> to use when logging.</value>
|
||||
/// <value>The <see cref="IBotTelemetryClient"/> to use for logging.</value>
|
||||
/// <remarks>When this property is set, it sets the <see cref="Dialog.TelemetryClient"/> of each
|
||||
/// dialog in the set to the new value.</remarks>
|
||||
public IBotTelemetryClient TelemetryClient
|
||||
{
|
||||
get
|
||||
|
@ -60,8 +70,9 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
/// of "duplicate2".
|
||||
/// </summary>
|
||||
/// <param name="dialog">The dialog to add.</param>
|
||||
/// <returns>The DialogSet for fluent calls to Add().</returns>
|
||||
/// <remarks>Adding a new dialog will inherit the <see cref="IBotTelemetryClient"/> of the DialogSet.</remarks>
|
||||
/// <returns>The dialog set after the operation is complete.</returns>
|
||||
/// <remarks>The added dialog's <see cref="Dialog.TelemetryClient"/> is set to the
|
||||
/// <see cref="TelemetryClient"/> of the dialog set.</remarks>
|
||||
public DialogSet Add(Dialog dialog)
|
||||
{
|
||||
if (dialog == null)
|
||||
|
@ -104,12 +115,17 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
return this;
|
||||
}
|
||||
|
||||
public Task<DialogContext> CreateContextAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return CreateContextAsync(turnContext, null, null, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<DialogContext> CreateContextAsync(ITurnContext turnContext, Dictionary<string, object> conversationState, Dictionary<string, object> userState, CancellationToken cancellationToken = default(CancellationToken))
|
||||
/// <summary>
|
||||
/// Creates a <see cref="DialogContext"/> which can be used to work with the dialogs in the
|
||||
/// <see cref="DialogSet"/>.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">Context for the current turn of conversation with the user.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
/// <remarks>If the task is successful, the result contains the created <see cref="DialogContext"/>.
|
||||
/// </remarks>
|
||||
public async Task<DialogContext> CreateContextAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
BotAssert.ContextNotNull(turnContext);
|
||||
|
||||
|
@ -128,10 +144,10 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a dialog that was previously added to the set using <see cref="Add(Dialog)"/>.
|
||||
/// Searches the current <see cref="DialogSet"/> for a <see cref="Dialog"/> by its ID.
|
||||
/// </summary>
|
||||
/// <param name="dialogId">ID of the dialog/prompt to look up.</param>
|
||||
/// <returns>The dialog if found, otherwise null.</returns>
|
||||
/// <param name="dialogId">ID of the dialog to search for.</param>
|
||||
/// <returns>The dialog if found; otherwise <c>null</c>.</returns>
|
||||
public Dialog Find(string dialogId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(dialogId))
|
||||
|
|
|
@ -7,18 +7,37 @@ using Newtonsoft.Json.Serialization;
|
|||
|
||||
namespace Microsoft.Bot.Builder.Dialogs
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains state information for the dialog stack.
|
||||
/// </summary>
|
||||
public class DialogState
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DialogState"/> class.
|
||||
/// </summary>
|
||||
/// <remarks>The new instance is created with an empty dialog stack.</remarks>
|
||||
/// <seealso cref="DialogContext.Stack"/>
|
||||
/// <seealso cref="DialogSet(IStatePropertyAccessor{DialogState})"/>
|
||||
public DialogState()
|
||||
: this(null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DialogState"/> class.
|
||||
/// </summary>
|
||||
/// <param name="stack">The state information to initialize the stack with.</param>
|
||||
/// <remarks>The new instance has a dialog stack that is populated using the information
|
||||
/// in <paramref name="stack"/>.</remarks>
|
||||
public DialogState(List<DialogInstance> stack)
|
||||
{
|
||||
DialogStack = stack ?? new List<DialogInstance>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the state information for a dialog stack.
|
||||
/// </summary>
|
||||
/// <value>State information for a dialog stack.</value>
|
||||
[JsonProperty("dialogStack")]
|
||||
public List<DialogInstance> DialogStack { get; set; } = new List<DialogInstance>();
|
||||
}
|
||||
|
|
|
@ -4,9 +4,12 @@
|
|||
namespace Microsoft.Bot.Builder.Dialogs
|
||||
{
|
||||
/// <summary>
|
||||
/// Result returned to the caller of one of the various stack manipulation methods,
|
||||
/// and used to return the result from a final call to `DialogContext.EndDialogAsync()` to the bot's logic.
|
||||
/// Result returned to the caller of one of the various stack manipulation methods.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Use <see cref="DialogContext.EndDialogAsync(object, System.Threading.CancellationToken)"/>
|
||||
/// to end a <see cref="Dialog"/> and return a result to the calling context.
|
||||
/// </remarks>
|
||||
public class DialogTurnResult
|
||||
{
|
||||
public DialogTurnResult(DialogTurnStatus status, object result = null)
|
||||
|
|
|
@ -16,12 +16,14 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
Waiting,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the dialog completed successfully, the result is available, and the stack is empty.
|
||||
/// Indicates that a dialog completed successfully, the result is available, and no child
|
||||
/// dialogs to the current context are on the dialog stack.
|
||||
/// </summary>
|
||||
Complete,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the dialog was cancelled and the stack is empty.
|
||||
/// Indicates that the dialog was canceled, and no child
|
||||
/// dialogs to the current context are on the dialog stack.
|
||||
/// </summary>
|
||||
Cancelled,
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Memory.PathResolvers
|
|||
{
|
||||
return base.TransformPath(path);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,40 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Description>This library implements .NET Simple Dialog classes </Description>
|
||||
<Description>
|
||||
This library implements .NET Simple Dialog classes
|
||||
Library for building bots using Microsoft Bot Framework Connector
|
||||
|
||||
Recognizers-Text Version Upgrade: from v1.1.3 to v1.2.6
|
||||
|
||||
DateTime - Recognize
|
||||
New recognized inputs:
|
||||
- ash wednesday
|
||||
- halloween
|
||||
|
||||
Changed results:
|
||||
- black friday
|
||||
- easter
|
||||
- maundy thursday
|
||||
- palm sunday
|
||||
|
||||
DateTime - Prompt
|
||||
New recognized inputs:
|
||||
- untill friday
|
||||
- monday untill friday
|
||||
- this past friday
|
||||
- past friday
|
||||
|
||||
Ordinal
|
||||
Removed inputs:
|
||||
- the second to last
|
||||
|
||||
Number
|
||||
Changed inputs:
|
||||
- half
|
||||
- half nelson
|
||||
- half seas over
|
||||
</Description>
|
||||
<Summary>This library implements .NET Simple Dialog classes </Summary>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -44,8 +77,8 @@
|
|||
<PackageReference Include="AsyncUsageAnalyzers" Version="1.0.0-alpha003" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.Bot.Builder" Condition=" '$(PackageVersion)' == '' " Version="4.5.0-local" />
|
||||
<PackageReference Include="Microsoft.Bot.Builder" Condition=" '$(PackageVersion)' != '' " Version="$(PackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Recognizers.Text.Choice" Version="1.1.3" />
|
||||
<PackageReference Include="Microsoft.Recognizers.Text.DateTime" Version="1.1.3" />
|
||||
<PackageReference Include="Microsoft.Recognizers.Text.Choice" Version="1.2.6.1" />
|
||||
<PackageReference Include="Microsoft.Recognizers.Text.DateTime" Version="1.2.6.1" />
|
||||
<PackageReference Include="SourceLink.Create.CommandLine" Version="2.8.3" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Bot.Schema;
|
||||
|
@ -101,14 +102,14 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
{
|
||||
var message = turnContext.Activity.AsMessageActivity();
|
||||
var culture = turnContext.Activity.Locale ?? DefaultLocale ?? English;
|
||||
var results = NumberRecognizer.RecognizeNumber(message.Text, culture);
|
||||
var results = NumberRecognizer.RecognizeNumber(message.Text, culture, NumberOptions.SuppressExtendedTypes);
|
||||
if (results.Count > 0)
|
||||
{
|
||||
// Try to parse value based on type
|
||||
var text = results[0].Resolution["value"].ToString();
|
||||
if (typeof(T) == typeof(float))
|
||||
{
|
||||
if (float.TryParse(text, out var value))
|
||||
if (float.TryParse(text, NumberStyles.Any, new CultureInfo(culture), out var value))
|
||||
{
|
||||
result.Succeeded = true;
|
||||
result.Value = (T)(object)value;
|
||||
|
@ -116,7 +117,7 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
}
|
||||
else if (typeof(T) == typeof(int))
|
||||
{
|
||||
if (int.TryParse(text, out var value))
|
||||
if (int.TryParse(text, NumberStyles.Any, new CultureInfo(culture), out var value))
|
||||
{
|
||||
result.Succeeded = true;
|
||||
result.Value = (T)(object)value;
|
||||
|
@ -124,7 +125,7 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
}
|
||||
else if (typeof(T) == typeof(long))
|
||||
{
|
||||
if (long.TryParse(text, out var value))
|
||||
if (long.TryParse(text, NumberStyles.Any, new CultureInfo(culture), out var value))
|
||||
{
|
||||
result.Succeeded = true;
|
||||
result.Value = (T)(object)value;
|
||||
|
@ -132,7 +133,7 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
}
|
||||
else if (typeof(T) == typeof(double))
|
||||
{
|
||||
if (double.TryParse(text, out var value))
|
||||
if (double.TryParse(text, NumberStyles.Any, new CultureInfo(culture), out var value))
|
||||
{
|
||||
result.Succeeded = true;
|
||||
result.Value = (T)(object)value;
|
||||
|
@ -140,16 +141,12 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
}
|
||||
else if (typeof(T) == typeof(decimal))
|
||||
{
|
||||
if (decimal.TryParse(text, out var value))
|
||||
if (decimal.TryParse(text, NumberStyles.Any, new CultureInfo(culture), out var value))
|
||||
{
|
||||
result.Succeeded = true;
|
||||
result.Value = (T)(object)value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException($"NumberPrompt: type argument T of type 'typeof(T)' is not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
///
|
||||
/// - The automatic signin flow where once the user signs in and the SSO service will forward the bot
|
||||
/// the users access token using either an `event` or `invoke` activity.
|
||||
/// - The "magic code" flow where where once the user signs in they will be prompted by the SSO
|
||||
/// - The "magic code" flow where once the user signs in they will be prompted by the SSO
|
||||
/// service to send the bot a six digit code confirming their identity. This code will be sent as a
|
||||
/// standard `message` activity.
|
||||
///
|
||||
|
|
|
@ -8,6 +8,11 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace Microsoft.Bot.Builder.Dialogs
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides context for a step in a <see cref="WaterfallDialog"/>.
|
||||
/// </summary>
|
||||
/// <remarks>The <see cref="DialogContext.Context"/> property contains the <see cref="ITurnContext"/>
|
||||
/// for the current turn.</remarks>
|
||||
public class WaterfallStepContext : DialogContext
|
||||
{
|
||||
private readonly WaterfallDialog _parentWaterfall;
|
||||
|
@ -15,7 +20,6 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WaterfallStepContext"/> class.
|
||||
/// Provides context for a turn of a waterfall dialog. Contains ITurnContext as property 'Context'.
|
||||
/// </summary>
|
||||
/// <param name= "parentWaterfall">The parent of the waterfall dialog.</param>
|
||||
/// <param name= "dc">The dialog's context.</param>
|
||||
|
@ -72,11 +76,13 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
public DialogReason Reason { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets results returned by a dialog called in the previous waterfall step.
|
||||
/// Gets the result from the previous waterfall step.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Results returned by a dialog called in the previous waterfall step.
|
||||
/// The result from the previous waterfall step.
|
||||
/// </value>
|
||||
/// <remarks>The result is often the return value of a child dialog that was started in
|
||||
/// the previous step of the waterfall.</remarks>
|
||||
public object Result { get; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -88,9 +94,9 @@ namespace Microsoft.Bot.Builder.Dialogs
|
|||
public IDictionary<string, object> Values { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Used to skip to the next waterfall step.
|
||||
/// Skips to the next step of the waterfall.
|
||||
/// </summary>
|
||||
/// <param name="result">(Optional) result to pass to the next step of the current waterfall dialog.</param>
|
||||
/// <param name="result">Optional, result to pass to the next step of the current waterfall dialog.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
|
|
|
@ -57,6 +57,11 @@
|
|||
<Content Include="**/*.lu" />
|
||||
<Content Include="**/*.schema" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Bot.Builder.Dialogs" Condition=" '$(PackageVersion)' != '' " Version="$(PackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Bot.Builder.LanguageGeneration" Condition=" '$(PackageVersion)' != '' " Version="$(PackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Bot.Builder.Dialogs.Declarative" Condition=" '$(PackageVersion)' != '' " Version="$(PackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.Bot.Builder.Dialogs.Declarative\Microsoft.Bot.Builder.Dialogs.Declarative.csproj" />
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
namespace Microsoft.Bot.Builder.StreamingExtensions
|
||||
|
@ -14,12 +15,13 @@ namespace Microsoft.Bot.Builder.StreamingExtensions
|
|||
{
|
||||
/// <summary>
|
||||
/// Maps various endpoint handlers for the registered bot into the request execution pipeline using the V4 protocol.
|
||||
/// Throws <see cref="ArgumentNullException"/> if application is null.
|
||||
/// </summary>
|
||||
/// <param name="applicationBuilder">The application builder that defines the bot's pipeline.<see cref="IApplicationBuilder"/>.</param>
|
||||
/// <param name="middlewareSet">The set of middleware the bot executes on each turn. <see cref="MiddlewareSet"/>.</param>
|
||||
/// <param name="applicationBuilder">The application builder that defines the bot's pipeline.</param>
|
||||
/// <param name="middlewareSet">The set of middleware the bot executes on each turn.</param>
|
||||
/// <param name="onTurnError">A callback method to call when an error occurs while executing the pipeline.</param>
|
||||
/// <returns>A reference to this instance after the operation has completed.</returns>
|
||||
public static IApplicationBuilder UseBotFrameworkNamedPipe(this IApplicationBuilder applicationBuilder, IList<IMiddleware> middlewareSet = null)
|
||||
/// <exception cref="ArgumentNullException"><paramref name="applicationBuilder"/> is <c>null</c>.</exception>
|
||||
public static IApplicationBuilder UseBotFrameworkNamedPipe(this IApplicationBuilder applicationBuilder, IList<IMiddleware> middlewareSet = null, Func<ITurnContext, Exception, Task> onTurnError = null)
|
||||
{
|
||||
if (applicationBuilder == null)
|
||||
{
|
||||
|
@ -27,7 +29,7 @@ namespace Microsoft.Bot.Builder.StreamingExtensions
|
|||
}
|
||||
|
||||
var connector = new NamedPipeConnector();
|
||||
connector.InitializeNamedPipeServer(applicationBuilder.ApplicationServices, middlewareSet);
|
||||
connector.InitializeNamedPipeServer(applicationBuilder.ApplicationServices, middlewareSet, onTurnError);
|
||||
|
||||
return applicationBuilder;
|
||||
}
|
||||
|
|
|
@ -24,11 +24,15 @@
|
|||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Net.Http.Headers" Version="2.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
<PackageReference Include="Microsoft.Bot.Builder" Version="4.5.0" />
|
||||
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Version="4.5.0" />
|
||||
<PackageReference Include="Microsoft.Bot.Builder" Condition=" '$(PackageVersion)' == '' " Version="4.0.0-local" />
|
||||
<PackageReference Include="Microsoft.Bot.Builder" Condition=" '$(PackageVersion)' != '' " Version="$(PackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Condition=" '$(PackageVersion)' == '' " Version="4.0.0-local" />
|
||||
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Condition=" '$(PackageVersion)' != '' " Version="$(PackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.Bot.Builder\Microsoft.Bot.Builder.csproj" />
|
||||
<ProjectReference Include="..\integration\Microsoft.Bot.Builder.Integration.AspNet.Core\Microsoft.Bot.Builder.Integration.AspNet.Core.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.Bot.StreamingExtensions\Microsoft.Bot.StreamingExtensions.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -12,7 +12,7 @@ namespace Microsoft.Bot.Builder.StreamingExtensions
|
|||
public class NamedPipeConnector
|
||||
{
|
||||
/* The default named pipe all instances of DL ASE listen on is named bfv4.pipes
|
||||
Unfortunately this name is no longer very discriptive, but for the time being
|
||||
Unfortunately this name is no longer very descriptive, but for the time being
|
||||
we're unable to change it without coordinated updates to DL ASE, which we
|
||||
currently are unable to perform.
|
||||
*/
|
||||
|
|
|
@ -189,9 +189,16 @@ namespace Microsoft.Bot.Builder.StreamingExtensions
|
|||
try
|
||||
{
|
||||
var adapter = new BotFrameworkStreamingExtensionsAdapter(_transportServer, _middlewareSet, logger);
|
||||
IBot bot = null;
|
||||
|
||||
// First check if an IBot type definition is available from the service provider.
|
||||
var bot = _services?.GetService<IBot>();
|
||||
if (_services != null)
|
||||
{
|
||||
/* Creating a new scope for each request allows us to support
|
||||
* bots that inject scoped services as dependencies.
|
||||
*/
|
||||
bot = _services.CreateScope().ServiceProvider.GetService<IBot>();
|
||||
}
|
||||
|
||||
// If no bot has been set, check if a singleton bot is associated with this request handler.
|
||||
if (bot == null)
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Version Condition=" '$(PackageVersion)' == '' ">4.0.0-local</Version>
|
||||
<Version Condition=" '$(PackageVersion)' != '' ">$(PackageVersion)</Version>
|
||||
<PackageVersion Condition=" '$(PackageVersion)' == '' ">4.0.0-local</PackageVersion>
|
||||
<PackageVersion Condition=" '$(PackageVersion)' != '' ">$(PackageVersion)</PackageVersion>
|
||||
<Configurations>Debug;Release;Debug - NuGet Packages</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<DelaySign>true</DelaySign>
|
||||
<AssemblyOriginatorKeyFile>..\..\build\35MSSharedLib1024.snk</AssemblyOriginatorKeyFile>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<PackageId>Microsoft.Bot.Builder.Teams</PackageId>
|
||||
<Description>Library for building bots using Microsoft Bot Framework</Description>
|
||||
<Summary>Library for building bots using Microsoft Bot Framework</Summary>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AdaptiveCards" Version="1.0.0" />
|
||||
<PackageReference Include="Microsoft.Bot.Builder" Condition=" '$(PackageVersion)' == '' " Version="4.0.0-local" />
|
||||
<PackageReference Include="Microsoft.Bot.Builder" Condition=" '$(PackageVersion)' != '' " Version="$(PackageVersion)" />
|
||||
|
||||
<!-- This may move to the root level dir.props file at some point. -->
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.Bot.Connector.Teams\Microsoft.Bot.Connector.Teams.csproj" Exclude="Runtime" DevelopmentDependency="true" />
|
||||
<ProjectReference Include="..\Microsoft.Bot.Schema.Teams\Microsoft.Bot.Schema.Teams.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.Bot.Builder\Microsoft.Bot.Builder.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,304 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Bot.Schema;
|
||||
using Microsoft.Bot.Schema.Teams;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Teams
|
||||
{
|
||||
public class TeamsActivityHandler : ActivityHandler
|
||||
{
|
||||
public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (turnContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(turnContext));
|
||||
}
|
||||
|
||||
if (turnContext.Activity == null)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(turnContext)} must have non-null Activity.");
|
||||
}
|
||||
|
||||
if (turnContext.Activity.Type == null)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(turnContext)}.Activity must have non-null Type.");
|
||||
}
|
||||
|
||||
switch (turnContext.Activity.Type)
|
||||
{
|
||||
case ActivityTypes.Invoke:
|
||||
var invokeResponse = await OnInvokeActivityAsync(new DelegatingTurnContext<IInvokeActivity>(turnContext), cancellationToken).ConfigureAwait(false);
|
||||
if (invokeResponse != null)
|
||||
{
|
||||
await turnContext.SendActivityAsync(new Activity { Value = invokeResponse, Type = ActivityTypesEx.InvokeResponse }).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
await base.OnTurnAsync(turnContext, cancellationToken).ConfigureAwait(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual async Task<InvokeResponse> OnInvokeActivityAsync(ITurnContext<IInvokeActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
if (turnContext.Activity.Name == null)
|
||||
{
|
||||
return await OnTeamsCardActionInvokeAsync(turnContext, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (turnContext.Activity.Name)
|
||||
{
|
||||
case "signin/verifyState":
|
||||
return await OnTeamsSigninVerifyStateAsync(turnContext, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
case "fileConsent/invoke":
|
||||
return await OnTeamsFileConsentAsync(turnContext, SafeCast<FileConsentCardResponse>(turnContext.Activity.Value), cancellationToken).ConfigureAwait(false);
|
||||
|
||||
case "actionableMessage/executeAction":
|
||||
await OnTeamsO365ConnectorCardActionAsync(turnContext, SafeCast<O365ConnectorCardActionQuery>(turnContext.Activity.Value), cancellationToken).ConfigureAwait(false);
|
||||
return CreateInvokeResponse();
|
||||
|
||||
case "composeExtension/query":
|
||||
return CreateInvokeResponse(await OnTeamsMessagingExtensionQueryAsync(turnContext, SafeCast<MessagingExtensionQuery>(turnContext.Activity.Value), cancellationToken).ConfigureAwait(false));
|
||||
|
||||
case "composeExtension/queryLink":
|
||||
return CreateInvokeResponse(await OnTeamsAppBasedLinkQueryAsync(turnContext, SafeCast<AppBasedLinkQuery>(turnContext.Activity.Value), cancellationToken).ConfigureAwait(false));
|
||||
|
||||
case "composeExtension/selectItem":
|
||||
return CreateInvokeResponse(await OnTeamsMessagingExtensionSelectItemAsync(turnContext, turnContext.Activity.Value as JObject, cancellationToken).ConfigureAwait(false));
|
||||
|
||||
case "composeExtension/submitAction":
|
||||
return CreateInvokeResponse(await OnTeamsMessagingExtensionSubmitActionDispatchAsync(turnContext, SafeCast<MessagingExtensionAction>(turnContext.Activity.Value), cancellationToken).ConfigureAwait(false));
|
||||
|
||||
case "composeExtension/fetchTask":
|
||||
return CreateInvokeResponse(await OnTeamsMessagingExtensionFetchTaskAsync(turnContext, cancellationToken).ConfigureAwait(false));
|
||||
|
||||
case "composeExtension/onquerySettingsUrl":
|
||||
return CreateInvokeResponse(await OnTeamsMessagingExtensionConfigurationSettingsUrlAsync(turnContext, cancellationToken).ConfigureAwait(false));
|
||||
|
||||
case "composeExtension/setting":
|
||||
return CreateInvokeResponse(await OnTeamsMessagingExtensionConfigurationSettingsAsync(turnContext, cancellationToken).ConfigureAwait(false));
|
||||
|
||||
case "task/fetch":
|
||||
return CreateInvokeResponse(await OnTeamsTaskModuleFetchAsync(turnContext, cancellationToken).ConfigureAwait(false));
|
||||
|
||||
case "task/submit":
|
||||
return CreateInvokeResponse(await OnTeamsTaskModuleSubmitAsync(turnContext, cancellationToken).ConfigureAwait(false));
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Task<InvokeResponse> OnTeamsCardActionInvokeAsync(ITurnContext<IInvokeActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult<InvokeResponse>(null);
|
||||
}
|
||||
|
||||
protected virtual Task<InvokeResponse> OnTeamsSigninVerifyStateAsync(ITurnContext<IInvokeActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult<InvokeResponse>(null);
|
||||
}
|
||||
|
||||
protected virtual async Task<InvokeResponse> OnTeamsFileConsentAsync(ITurnContext<IInvokeActivity> turnContext, FileConsentCardResponse fileConsentCardResponse, CancellationToken cancellationToken)
|
||||
{
|
||||
switch (fileConsentCardResponse.Action)
|
||||
{
|
||||
case "accept":
|
||||
await OnTeamsFileConsentAcceptAsync(turnContext, fileConsentCardResponse, cancellationToken).ConfigureAwait(false);
|
||||
return CreateInvokeResponse();
|
||||
|
||||
case "decline":
|
||||
await OnTeamsFileConsentDeclineAsync(turnContext, fileConsentCardResponse, cancellationToken).ConfigureAwait(false);
|
||||
return CreateInvokeResponse();
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Task OnTeamsFileConsentAcceptAsync(ITurnContext<IInvokeActivity> turnContext, FileConsentCardResponse fileConsentCardResponse, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected virtual Task OnTeamsFileConsentDeclineAsync(ITurnContext<IInvokeActivity> turnContext, FileConsentCardResponse fileConsentCardResponse, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected virtual Task<MessagingExtensionResponse> OnTeamsMessagingExtensionQueryAsync(ITurnContext<IInvokeActivity> turnContext, MessagingExtensionQuery query, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult<MessagingExtensionResponse>(null);
|
||||
}
|
||||
|
||||
protected virtual Task OnTeamsO365ConnectorCardActionAsync(ITurnContext<IInvokeActivity> turnContext, O365ConnectorCardActionQuery query, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected virtual Task<MessagingExtensionResponse> OnTeamsAppBasedLinkQueryAsync(ITurnContext<IInvokeActivity> turnContext, AppBasedLinkQuery query, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult<MessagingExtensionResponse>(null);
|
||||
}
|
||||
|
||||
protected virtual Task<MessagingExtensionResponse> OnTeamsMessagingExtensionSelectItemAsync(ITurnContext<IInvokeActivity> turnContext, JObject query, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult<MessagingExtensionResponse>(null);
|
||||
}
|
||||
|
||||
protected virtual Task<MessagingExtensionActionResponse> OnTeamsMessagingExtensionFetchTaskAsync(ITurnContext<IInvokeActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult<MessagingExtensionActionResponse>(null);
|
||||
}
|
||||
|
||||
protected virtual async Task<MessagingExtensionActionResponse> OnTeamsMessagingExtensionSubmitActionDispatchAsync(ITurnContext<IInvokeActivity> turnContext, MessagingExtensionAction query, CancellationToken cancellationToken)
|
||||
{
|
||||
var value = turnContext.Activity.Value as MessagingExtensionAction;
|
||||
if (value?.BotMessagePreviewAction != null)
|
||||
{
|
||||
switch (value.BotMessagePreviewAction)
|
||||
{
|
||||
case "edit":
|
||||
return await OnTeamsMessagingExtensionBotMessagePreviewEditAsync(turnContext, query, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
case "submit":
|
||||
return await OnTeamsMessagingExtensionBotMessagePreviewSendAsync(turnContext, query, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return await OnTeamsMessagingExtensionSubmitActionAsync(turnContext, query, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Task<MessagingExtensionActionResponse> OnTeamsMessagingExtensionSubmitActionAsync(ITurnContext<IInvokeActivity> turnContext, MessagingExtensionAction query, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult<MessagingExtensionActionResponse>(null);
|
||||
}
|
||||
|
||||
protected virtual Task<MessagingExtensionActionResponse> OnTeamsMessagingExtensionBotMessagePreviewEditAsync(ITurnContext<IInvokeActivity> turnContext, MessagingExtensionAction query, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult<MessagingExtensionActionResponse>(null);
|
||||
}
|
||||
|
||||
protected virtual Task<MessagingExtensionActionResponse> OnTeamsMessagingExtensionBotMessagePreviewSendAsync(ITurnContext<IInvokeActivity> turnContext, MessagingExtensionAction query, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult<MessagingExtensionActionResponse>(null);
|
||||
}
|
||||
|
||||
protected virtual Task<MessagingExtensionResponse> OnTeamsMessagingExtensionConfigurationSettingsUrlAsync(ITurnContext<IInvokeActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult<MessagingExtensionResponse>(null);
|
||||
}
|
||||
|
||||
protected virtual Task<MessagingExtensionResponse> OnTeamsMessagingExtensionConfigurationSettingsAsync(ITurnContext<IInvokeActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult<MessagingExtensionResponse>(null);
|
||||
}
|
||||
|
||||
protected virtual Task<TaskModuleResponse> OnTeamsTaskModuleFetchAsync(ITurnContext<IInvokeActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult<TaskModuleResponse>(null);
|
||||
}
|
||||
|
||||
protected virtual Task<TaskModuleResponse> OnTeamsTaskModuleSubmitAsync(ITurnContext<IInvokeActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult<TaskModuleResponse>(null);
|
||||
}
|
||||
|
||||
protected override Task OnConversationUpdateActivityAsync(ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
var channelData = turnContext.Activity.GetChannelData<TeamsChannelData>();
|
||||
|
||||
if (string.IsNullOrEmpty(channelData?.EventType))
|
||||
{
|
||||
return base.OnConversationUpdateActivityAsync(turnContext, cancellationToken);
|
||||
}
|
||||
|
||||
switch (channelData.EventType)
|
||||
{
|
||||
case "teamMemberAdded":
|
||||
return OnTeamsMembersAddedAsync(turnContext.Activity.MembersAdded, channelData.Team, turnContext, cancellationToken);
|
||||
|
||||
case "teamMemberRemoved":
|
||||
return OnTeamsMembersRemovedAsync(turnContext.Activity.MembersRemoved, channelData.Team, turnContext, cancellationToken);
|
||||
|
||||
case "channelCreated":
|
||||
return OnTeamsChannelCreatedAsync(channelData.Channel, channelData.Team, turnContext, cancellationToken);
|
||||
|
||||
case "channelDeleted":
|
||||
return OnTeamsChannelDeletedAsync(channelData.Channel, channelData.Team, turnContext, cancellationToken);
|
||||
|
||||
case "channelRenamed":
|
||||
return OnTeamsChannelRenamedAsync(channelData.Channel, channelData.Team, turnContext, cancellationToken);
|
||||
|
||||
case "teamRenamed":
|
||||
return OnTeamsTeamRenamedAsync(channelData.Team, turnContext, cancellationToken);
|
||||
|
||||
default:
|
||||
return base.OnConversationUpdateActivityAsync(turnContext, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Task OnTeamsMembersAddedAsync(IList<ChannelAccount> membersAdded, TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return OnMembersAddedAsync(membersAdded, turnContext, cancellationToken);
|
||||
}
|
||||
|
||||
protected virtual Task OnTeamsMembersRemovedAsync(IList<ChannelAccount> membersRemoved, TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return OnMembersRemovedAsync(membersRemoved, turnContext, cancellationToken);
|
||||
}
|
||||
|
||||
protected virtual Task OnTeamsChannelCreatedAsync(ChannelInfo channelInfo, TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected virtual Task OnTeamsChannelDeletedAsync(ChannelInfo channelInfo, TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected virtual Task OnTeamsChannelRenamedAsync(ChannelInfo channelInfo, TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected virtual Task OnTeamsTeamRenamedAsync(TeamInfo teamInfo, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static InvokeResponse CreateInvokeResponse(object body = null)
|
||||
{
|
||||
return new InvokeResponse { Status = (int)HttpStatusCode.OK, Body = body };
|
||||
}
|
||||
|
||||
private static T SafeCast<T>(object value)
|
||||
{
|
||||
var obj = value as JObject;
|
||||
if (obj == null)
|
||||
{
|
||||
throw new Exception($"expected type '{value.GetType().Name}'");
|
||||
}
|
||||
|
||||
return obj.ToObject<T>();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Bot.Connector;
|
||||
using Microsoft.Bot.Schema.Teams;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Teams
|
||||
{
|
||||
/// <summary>
|
||||
/// Filters request based on provided tenant list.
|
||||
/// </summary>
|
||||
/// <seealso cref="IMiddleware" />
|
||||
public class TeamsTenantFilteringMiddleware : IMiddleware
|
||||
{
|
||||
/// <summary>
|
||||
/// The tenant map.
|
||||
/// </summary>
|
||||
private readonly HashSet<string> tenantMap;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TeamsTenantFilteringMiddleware"/> class.
|
||||
/// </summary>
|
||||
/// <param name="allowedTenantIds">The list of allowed tenants.</param>
|
||||
public TeamsTenantFilteringMiddleware(IEnumerable<string> allowedTenantIds)
|
||||
{
|
||||
if (allowedTenantIds == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(allowedTenantIds));
|
||||
}
|
||||
|
||||
this.tenantMap = new HashSet<string>(allowedTenantIds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When implemented in middleware, processess an incoming activity.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="next">The delegate to call to continue the bot middleware pipeline.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the work queued to execute.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Middleware calls the <paramref name="next" /> delegate to pass control to
|
||||
/// the next middleware in the pipeline. If middleware doesn’t call the next delegate,
|
||||
/// the adapter does not call any of the subsequent middleware’s request handlers or the
|
||||
/// bot’s receive handler, and the pipeline short circuits.
|
||||
/// <para>The <paramref name="turnContext" /> provides information about the
|
||||
/// incoming activity, and other data needed to process the activity.</para>
|
||||
/// </remarks>
|
||||
/// <seealso cref="ITurnContext" />
|
||||
/// <seealso cref="Schema.IActivity" />
|
||||
public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (!turnContext.Activity.ChannelId.Equals(Channels.Msteams, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await next(cancellationToken).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
TeamsChannelData teamsChannelData = turnContext.Activity.GetChannelData<TeamsChannelData>();
|
||||
string tenantId = teamsChannelData?.Tenant?.Id;
|
||||
|
||||
if (string.IsNullOrEmpty(tenantId))
|
||||
{
|
||||
throw new UnauthorizedAccessException("Tenant Id is missing.");
|
||||
}
|
||||
|
||||
if (!this.tenantMap.Contains(tenantId))
|
||||
{
|
||||
throw new UnauthorizedAccessException("Tenant Id '" + tenantId + "' is not allowed access.");
|
||||
}
|
||||
|
||||
await next(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,21 +11,40 @@ using Microsoft.Bot.Schema;
|
|||
namespace Microsoft.Bot.Builder
|
||||
{
|
||||
/// <summary>
|
||||
/// An implementation of the IBot interface intended for further subclassing.
|
||||
/// Derive from this class to plug in code to handle particular Activity types.
|
||||
/// Pre and post processing of Activities can be plugged in by deriving and calling
|
||||
/// the base class implementation.
|
||||
/// An implementation of the <see cref="IBot"/> interface, intended for further subclassing.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Derive from this class to plug in code to handle particular activity types.
|
||||
/// Pre- and post-processing of <see cref="Activity"/> objects can be added by calling
|
||||
/// the base class implementation from the derived class.
|
||||
/// </remarks>
|
||||
public class ActivityHandler : IBot
|
||||
{
|
||||
/// <summary>
|
||||
/// The OnTurnAsync function is called by the Adapter (for example, the <see cref="BotFrameworkAdapter"/>)
|
||||
/// at runtime in order to process an inbound Activity.
|
||||
/// Called by the adapter (for example, a <see cref="BotFrameworkAdapter"/>)
|
||||
/// at runtime in order to process an inbound <see cref="Activity"/>.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>
|
||||
/// This method calls other methods in this class based on the type of the activity to
|
||||
/// process, which allows a derived class to provide type-specific logic in a controlled way.
|
||||
///
|
||||
/// In a derived class, override this method to add logic that applies to all activity types.
|
||||
/// Add logic to apply before the type-specific logic before the call to the base class
|
||||
/// <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/> method.
|
||||
/// Add logic to apply after the type-specific logic after the call to the base class
|
||||
/// <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/> method.
|
||||
/// </remarks>
|
||||
/// <seealso cref="OnMessageActivityAsync(ITurnContext{IMessageActivity}, CancellationToken)"/>
|
||||
/// <seealso cref="OnConversationUpdateActivityAsync(ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>
|
||||
/// <seealso cref="OnMessageReactionActivityAsync(ITurnContext{IMessageReactionActivity}, CancellationToken)"/>
|
||||
/// <seealso cref="OnEventActivityAsync(ITurnContext{IEventActivity}, CancellationToken)"/>
|
||||
/// <seealso cref="OnUnrecognizedActivityTypeAsync(ITurnContext, CancellationToken)"/>
|
||||
/// <seealso cref="Activity.Type"/>
|
||||
/// <seealso cref="ActivityTypes"/>
|
||||
public virtual Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (turnContext == null)
|
||||
|
@ -63,15 +82,18 @@ namespace Microsoft.Bot.Builder
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a message activity is received from the user when the base behavior of
|
||||
/// <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/> is used.
|
||||
/// If overridden, this could potentially contain conversational logic.
|
||||
/// By default, this method does nothing.
|
||||
/// Override this in a derived class to provide logic specific to
|
||||
/// <see cref="ActivityTypes.Message"/> activities, such as the conversational logic.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>
|
||||
/// When the <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
||||
/// method receives a message activity, it calls this method.
|
||||
/// </remarks>
|
||||
/// <seealso cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
||||
protected virtual Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
|
@ -86,10 +108,27 @@ namespace Microsoft.Bot.Builder
|
|||
/// if any users have been added or <see cref="OnMembersRemovedAsync(IList{ChannelAccount}, ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>
|
||||
/// if any users have been removed. The method checks the member ID so that it only responds to updates regarding members other than the bot itself.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>
|
||||
/// When the <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
||||
/// method receives a conversation update activity, it calls this method.
|
||||
/// If the conversation update activity indicates that members other than the bot joined the conversation, it calls
|
||||
/// <see cref="OnMembersAddedAsync(IList{ChannelAccount}, ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>.
|
||||
/// If the conversation update activity indicates that members other than the bot left the conversation, it calls
|
||||
/// <see cref="OnMembersRemovedAsync(IList{ChannelAccount}, ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>.
|
||||
///
|
||||
/// In a derived class, override this method to add logic that applies to all conversation update activities.
|
||||
/// Add logic to apply before the member added or removed logic before the call to the base class
|
||||
/// <see cref="OnConversationUpdateActivityAsync(ITurnContext{IConversationUpdateActivity}, CancellationToken)"/> method.
|
||||
/// Add logic to apply after the member added or removed logic after the call to the base class
|
||||
/// <see cref="OnConversationUpdateActivityAsync(ITurnContext{IConversationUpdateActivity}, CancellationToken)"/> method.
|
||||
/// </remarks>
|
||||
/// <seealso cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
||||
/// <seealso cref="OnMembersAddedAsync(IList{ChannelAccount}, ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>
|
||||
/// <seealso cref="OnMembersRemovedAsync(IList{ChannelAccount}, ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>
|
||||
protected virtual Task OnConversationUpdateActivityAsync(ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
if (turnContext.Activity.MembersAdded != null)
|
||||
|
@ -111,32 +150,42 @@ namespace Microsoft.Bot.Builder
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when members other than this bot (like a user) are added to the conversation when the base behavior of
|
||||
/// <see cref="OnConversationUpdateActivityAsync(ITurnContext{IConversationUpdateActivity}, CancellationToken)"/> is used.
|
||||
/// If overridden, this could potentially send a greeting message to the user instead of waiting for the user to send a message first.
|
||||
/// By default, this method does nothing.
|
||||
/// Override this in a derived class to provide logic for when members other than the bot
|
||||
/// join the conversation, such as your bot's welcome logic.
|
||||
/// </summary>
|
||||
/// <param name="membersAdded">A list of all the users that have been added in the conversation update.</param>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="membersAdded">A list of all the members added to the conversation, as
|
||||
/// described by the conversation update activity.</param>
|
||||
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>
|
||||
/// When the <see cref="OnConversationUpdateActivityAsync(ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>
|
||||
/// method receives a conversation update activity that indicates one or more users other than the bot
|
||||
/// are joining the conversation, it calls this method.
|
||||
/// </remarks>
|
||||
/// <seealso cref="OnConversationUpdateActivityAsync(ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>
|
||||
protected virtual Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when members other than this bot (like a user) are removed from the conversation when the base behavior of
|
||||
/// <see cref="OnConversationUpdateActivityAsync(ITurnContext{IConversationUpdateActivity}, CancellationToken)"/> is used.
|
||||
/// This method could optionally be overridden to perform actions related to users leaving a group conversation.
|
||||
/// By default, this method does nothing.
|
||||
/// Override this in a derived class to provide logic for when members other than the bot
|
||||
/// leave the conversation, such as your bot's good-bye logic.
|
||||
/// </summary>
|
||||
/// <param name="membersRemoved">A list of all the users that have been removed in the conversation update.</param>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="membersRemoved">A list of all the members removed from the conversation, as
|
||||
/// described by the conversation update activity.</param>
|
||||
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>
|
||||
/// When the <see cref="OnConversationUpdateActivityAsync(ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>
|
||||
/// method receives a conversation update activity that indicates one or more users other than the bot
|
||||
/// are leaving the conversation, it calls this method.
|
||||
/// </remarks>
|
||||
/// <seealso cref="OnConversationUpdateActivityAsync(ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>
|
||||
protected virtual Task OnMembersRemovedAsync(IList<ChannelAccount> membersRemoved, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
|
@ -151,10 +200,28 @@ namespace Microsoft.Bot.Builder
|
|||
/// The value of this property is the activity id of a previously sent activity given back to the
|
||||
/// bot as the response from a send call.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>
|
||||
/// When the <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
||||
/// method receives a message reaction activity, it calls this method.
|
||||
/// If the message reaction indicates that reactions were added to a message, it calls
|
||||
/// <see cref="OnReactionsAddedAsync(IList{MessageReaction}, ITurnContext{IMessageReactionActivity}, CancellationToken)"/>.
|
||||
/// If the message reaction indicates that reactions were removed from a message, it calls
|
||||
/// <see cref="OnReactionsRemovedAsync(IList{MessageReaction}, ITurnContext{IMessageReactionActivity}, CancellationToken)"/>.
|
||||
///
|
||||
/// In a derived class, override this method to add logic that applies to all message reaction activities.
|
||||
/// Add logic to apply before the reactions added or removed logic before the call to the base class
|
||||
/// <see cref="OnMessageReactionActivityAsync(ITurnContext{IMessageReactionActivity}, CancellationToken)"/> method.
|
||||
/// Add logic to apply after the reactions added or removed logic after the call to the base class
|
||||
/// <see cref="OnMessageReactionActivityAsync(ITurnContext{IMessageReactionActivity}, CancellationToken)"/> method.
|
||||
///
|
||||
/// </remarks>
|
||||
/// <seealso cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
||||
/// <seealso cref="OnReactionsAddedAsync(IList{MessageReaction}, ITurnContext{IMessageReactionActivity}, CancellationToken)"/>
|
||||
/// <seealso cref="OnReactionsRemovedAsync(IList{MessageReaction}, ITurnContext{IMessageReactionActivity}, CancellationToken)"/>
|
||||
protected virtual async Task OnMessageReactionActivityAsync(ITurnContext<IMessageReactionActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
if (turnContext.Activity.ReactionsAdded != null)
|
||||
|
@ -169,26 +236,52 @@ namespace Microsoft.Bot.Builder
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when there have been Reactions added that reference a previous Activity.
|
||||
/// Override this in a derived class to provide logic for when reactions to a previous activity
|
||||
/// are added to the conversation.
|
||||
/// </summary>
|
||||
/// <param name="messageReactions">The list of reactions added.</param>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>
|
||||
/// Message reactions correspond to the user adding a 'like' or 'sad' etc. (often an emoji) to a
|
||||
/// previously sent message on the conversation. Message reactions are supported by only a few channels.
|
||||
/// The activity that the message is in reaction to is identified by the activity's
|
||||
/// <see cref="Activity.ReplyToId"/> property. The value of this property is the activity ID
|
||||
/// of a previously sent activity. When the bot sends an activity, the channel assigns an ID to it,
|
||||
/// which is available in the <see cref="ResourceResponse.Id"/> of the result.
|
||||
/// </remarks>
|
||||
/// <seealso cref="OnMessageReactionActivityAsync(ITurnContext{IMessageReactionActivity}, CancellationToken)"/>
|
||||
/// <seealso cref="Activity.Id"/>
|
||||
/// <seealso cref="ITurnContext.SendActivityAsync(IActivity, CancellationToken)"/>
|
||||
/// <seealso cref="ResourceResponse.Id"/>
|
||||
protected virtual Task OnReactionsAddedAsync(IList<MessageReaction> messageReactions, ITurnContext<IMessageReactionActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when there have been Reactions removed that reference a previous Activity.
|
||||
/// Override this in a derived class to provide logic for when reactions to a previous activity
|
||||
/// are removed from the conversation.
|
||||
/// </summary>
|
||||
/// <param name="messageReactions">The list of reactions removed.</param>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>
|
||||
/// Message reactions correspond to the user adding a 'like' or 'sad' etc. (often an emoji) to a
|
||||
/// previously sent message on the conversation. Message reactions are supported by only a few channels.
|
||||
/// The activity that the message is in reaction to is identified by the activity's
|
||||
/// <see cref="Activity.ReplyToId"/> property. The value of this property is the activity ID
|
||||
/// of a previously sent activity. When the bot sends an activity, the channel assigns an ID to it,
|
||||
/// which is available in the <see cref="ResourceResponse.Id"/> of the result.
|
||||
/// </remarks>
|
||||
/// <seealso cref="OnMessageReactionActivityAsync(ITurnContext{IMessageReactionActivity}, CancellationToken)"/>
|
||||
/// <seealso cref="Activity.Id"/>
|
||||
/// <seealso cref="ITurnContext.SendActivityAsync(IActivity, CancellationToken)"/>
|
||||
/// <seealso cref="ResourceResponse.Id"/>
|
||||
protected virtual Task OnReactionsRemovedAsync(IList<MessageReaction> messageReactions, ITurnContext<IMessageReactionActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
|
@ -202,10 +295,31 @@ namespace Microsoft.Bot.Builder
|
|||
/// activity's name is <c>tokens/response</c> or <see cref="OnEventAsync(ITurnContext{IEventActivity}, CancellationToken)"/> otherwise.
|
||||
/// A <c>tokens/response</c> event can be triggered by an <see cref="OAuthCard"/>.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>
|
||||
/// When the <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
||||
/// method receives an event activity, it calls this method.
|
||||
/// If the event <see cref="IEventActivity.Name"/> is `tokens/response`, it calls
|
||||
/// <see cref="OnTokenResponseEventAsync(ITurnContext{IEventActivity}, CancellationToken)"/>;
|
||||
/// otherwise, it calls <see cref="OnEventAsync(ITurnContext{IEventActivity}, CancellationToken)"/>.
|
||||
///
|
||||
/// In a derived class, override this method to add logic that applies to all event activities.
|
||||
/// Add logic to apply before the specific event-handling logic before the call to the base class
|
||||
/// <see cref="OnEventActivityAsync(ITurnContext{IEventActivity}, CancellationToken)"/> method.
|
||||
/// Add logic to apply after the specific event-handling logic after the call to the base class
|
||||
/// <see cref="OnEventActivityAsync(ITurnContext{IEventActivity}, CancellationToken)"/> method.
|
||||
///
|
||||
/// Event activities communicate programmatic information from a client or channel to a bot.
|
||||
/// The meaning of an event activity is defined by the <see cref="IEventActivity.Name"/> property,
|
||||
/// which is meaningful within the scope of a channel.
|
||||
/// A `tokens/response` event can be triggered by an <see cref="OAuthCard"/> or an OAuth prompt.
|
||||
/// </remarks>
|
||||
/// <seealso cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
||||
/// <seealso cref="OnTokenResponseEventAsync(ITurnContext{IEventActivity}, CancellationToken)"/>
|
||||
/// <seealso cref="OnEventAsync(ITurnContext{IEventActivity}, CancellationToken)"/>
|
||||
protected virtual Task OnEventActivityAsync(ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
if (turnContext.Activity.Name == "tokens/response")
|
||||
|
@ -222,10 +336,20 @@ namespace Microsoft.Bot.Builder
|
|||
/// If using an <c>OAuthPrompt</c>, override this method to forward this <see cref="Activity"/> to the current dialog.
|
||||
/// By default, this method does nothing.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>
|
||||
/// When the <see cref="OnEventActivityAsync(ITurnContext{IEventActivity}, CancellationToken)"/>
|
||||
/// method receives an event with a <see cref="IEventActivity.Name"/> of `tokens/response`,
|
||||
/// it calls this method.
|
||||
///
|
||||
/// If your bot uses the <c>OAuthPrompt</c>, forward the incoming <see cref="Activity"/> to
|
||||
/// the current dialog.
|
||||
/// </remarks>
|
||||
/// <seealso cref="OnEventActivityAsync(ITurnContext{IEventActivity}, CancellationToken)"/>
|
||||
/// <seealso cref="OnEventAsync(ITurnContext{IEventActivity}, CancellationToken)"/>
|
||||
protected virtual Task OnTokenResponseEventAsync(ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
|
@ -237,10 +361,17 @@ namespace Microsoft.Bot.Builder
|
|||
/// This method could optionally be overridden if the bot is meant to handle miscellaneous events.
|
||||
/// By default, this method does nothing.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="turnContext">A strongly-typed context object for this turn.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>
|
||||
/// When the <see cref="OnEventActivityAsync(ITurnContext{IEventActivity}, CancellationToken)"/>
|
||||
/// method receives an event with a <see cref="IEventActivity.Name"/> other than `tokens/response`,
|
||||
/// it calls this method.
|
||||
/// </remarks>
|
||||
/// <seealso cref="OnEventActivityAsync(ITurnContext{IEventActivity}, CancellationToken)"/>
|
||||
/// <seealso cref="OnTokenResponseEventAsync(ITurnContext{IEventActivity}, CancellationToken)"/>
|
||||
protected virtual Task OnEventAsync(ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
|
@ -257,6 +388,18 @@ namespace Microsoft.Bot.Builder
|
|||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>
|
||||
/// When the <see cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
||||
/// method receives an activity that is not a message, conversation update, message reaction,
|
||||
/// or event activity, it calls this method.
|
||||
/// </remarks>
|
||||
/// <seealso cref="OnTurnAsync(ITurnContext, CancellationToken)"/>
|
||||
/// <seealso cref="OnMessageActivityAsync(ITurnContext{IMessageActivity}, CancellationToken)"/>
|
||||
/// <seealso cref="OnConversationUpdateActivityAsync(ITurnContext{IConversationUpdateActivity}, CancellationToken)"/>
|
||||
/// <seealso cref="OnMessageReactionActivityAsync(ITurnContext{IMessageReactionActivity}, CancellationToken)"/>
|
||||
/// <seealso cref="OnEventActivityAsync(ITurnContext{IEventActivity}, CancellationToken)"/>
|
||||
/// <seealso cref="Activity.Type"/>
|
||||
/// <seealso cref="ActivityTypes"/>
|
||||
protected virtual Task OnUnrecognizedActivityTypeAsync(ITurnContext turnContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace Microsoft.Bot.Builder
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an error handler that can catche exceptions in the middleware or application.
|
||||
/// Gets or sets an error handler that can catch exceptions in the middleware or application.
|
||||
/// </summary>
|
||||
/// <value>An error handler that can catch exceptions in the middleware or application.</value>
|
||||
public Func<ITurnContext, Exception, Task> OnTurnError { get; set; }
|
||||
|
@ -111,7 +111,7 @@ namespace Microsoft.Bot.Builder
|
|||
/// <summary>
|
||||
/// Sends a proactive message to a conversation.
|
||||
/// </summary>
|
||||
/// <param name="botId">The application ID of the bot. This paramter is ignored in
|
||||
/// <param name="botId">The application ID of the bot. This parameter is ignored in
|
||||
/// single tenant the Adapters (Console, Test, etc) but is critical to the BotFrameworkAdapter
|
||||
/// which is multi-tenant aware. </param>
|
||||
/// <param name="reference">A reference to the conversation to continue.</param>
|
||||
|
|
|
@ -10,6 +10,7 @@ using System.Net.Http;
|
|||
using System.Security.Claims;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Bot.Builder.Integration;
|
||||
|
@ -827,9 +828,7 @@ namespace Microsoft.Bot.Builder
|
|||
{
|
||||
if (reference.Conversation != null)
|
||||
{
|
||||
var typeOfDynamic = reference.Conversation.GetType();
|
||||
var tenantProperty = typeOfDynamic.GetProperty("tenantId");
|
||||
var tenantId = tenantProperty?.GetValue(reference.Conversation, null);
|
||||
var tenantId = reference.Conversation.TenantId;
|
||||
|
||||
if (tenantId != null)
|
||||
{
|
||||
|
@ -976,8 +975,8 @@ namespace Microsoft.Bot.Builder
|
|||
// NOTE: we can't do async operations inside of a AddOrUpdate, so we split access pattern
|
||||
string appPassword = await _credentialProvider.GetAppPasswordAsync(appId).ConfigureAwait(false);
|
||||
appCredentials = (_channelProvider != null && _channelProvider.IsGovernment()) ?
|
||||
new MicrosoftGovernmentAppCredentials(appId, appPassword, _httpClient) :
|
||||
new MicrosoftAppCredentials(appId, appPassword, _httpClient);
|
||||
new MicrosoftGovernmentAppCredentials(appId, appPassword, _httpClient, _logger) :
|
||||
new MicrosoftAppCredentials(appId, appPassword, _httpClient, _logger);
|
||||
_appCredentialMap[appId] = appCredentials;
|
||||
return appCredentials;
|
||||
}
|
||||
|
|
|
@ -12,8 +12,20 @@ using Newtonsoft.Json.Linq;
|
|||
namespace Microsoft.Bot.Builder
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads and writes state for your bot to storage.
|
||||
/// Defines a state management object and automates the reading and writing of associated state
|
||||
/// properties to a storage layer.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Each state management object defines a scope for a storage layer.
|
||||
///
|
||||
/// State properties are created within a state management scope, and the Bot Framework
|
||||
/// defines these scopes:
|
||||
/// <see cref="ConversationState"/>, <see cref="UserState"/>, and <see cref="PrivateConversationState"/>.
|
||||
///
|
||||
/// You can define additional scopes for your bot.
|
||||
/// </remarks>
|
||||
/// <seealso cref="IStorage"/>
|
||||
/// <seealso cref="IStatePropertyAccessor{T}"/>
|
||||
public abstract class BotState : IPropertyManager
|
||||
{
|
||||
private readonly string _contextServiceKey;
|
||||
|
@ -22,8 +34,16 @@ namespace Microsoft.Bot.Builder
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BotState"/> class.
|
||||
/// </summary>
|
||||
/// <param name="storage">The storage provider to use.</param>
|
||||
/// <param name="contextServiceKey">the key for caching on the context services dictionary.</param>
|
||||
/// <param name="storage">The storage layer this state management object will use to store
|
||||
/// and retrieve state.</param>
|
||||
/// <param name="contextServiceKey">The key for the state cache for this <see cref="BotState"/>.</param>
|
||||
/// <remarks>This constructor creates a state management object and associated scope.
|
||||
/// The object uses <paramref name="storage"/> to persist state property values.
|
||||
/// The object uses the <paramref name="contextServiceKey"/> to cache state within the context for each turn.
|
||||
/// </remarks>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="storage"/> or <paramref name="contextServiceKey"/>
|
||||
/// is <c>null</c>.</exception>
|
||||
/// <seealso cref="ITurnContext"/>
|
||||
public BotState(IStorage storage, string contextServiceKey)
|
||||
{
|
||||
_storage = storage ?? throw new ArgumentNullException(nameof(storage));
|
||||
|
@ -31,11 +51,13 @@ namespace Microsoft.Bot.Builder
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a property definition and register it with this BotState.
|
||||
/// Creates a named state property within the scope of a <see cref="BotState"/> and returns
|
||||
/// an accessor for the property.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">type of property.</typeparam>
|
||||
/// <param name="name">name of the property.</param>
|
||||
/// <returns>The created state property accessor.</returns>
|
||||
/// <typeparam name="T">The value type of the property.</typeparam>
|
||||
/// <param name="name">The name of the property.</param>
|
||||
/// <returns>An accessor for the property.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="name"/> is <c>null</c>.</exception>
|
||||
public IStatePropertyAccessor<T> CreateProperty<T>(string name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
|
@ -47,13 +69,15 @@ namespace Microsoft.Bot.Builder
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads in the current state object and caches it in the context object for this turm.
|
||||
/// Populates the state cache for this <see cref="BotState"/> from the storage layer.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="force">Optional. True to bypass the cache.</param>
|
||||
/// <param name="force">Optional, <c>true</c> to overwrite any existing state cache;
|
||||
/// or <c>false</c> to load state from storage only if the cache doesn't already exist.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="turnContext"/> is <c>null</c>.</exception>
|
||||
public virtual async Task LoadAsync(ITurnContext turnContext, bool force = false, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (turnContext == null)
|
||||
|
@ -72,13 +96,15 @@ namespace Microsoft.Bot.Builder
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// If it has changed, writes to storage the state object that is cached in the current context object for this turn.
|
||||
/// Writes the state cache for this <see cref="BotState"/> to the storage layer.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="force">Optional. True to save state to storage whether or not there are changes.</param>
|
||||
/// <param name="force">Optional, <c>true</c> to save the state cache to storage;
|
||||
/// or <c>false</c> to save state to storage only if a property in the cache has changed.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="turnContext"/> is <c>null</c>.</exception>
|
||||
public virtual async Task SaveChangesAsync(ITurnContext turnContext, bool force = false, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (turnContext == null)
|
||||
|
@ -87,7 +113,7 @@ namespace Microsoft.Bot.Builder
|
|||
}
|
||||
|
||||
var cachedState = turnContext.TurnState.Get<CachedBotState>(_contextServiceKey);
|
||||
if (force || (cachedState != null && cachedState.IsChanged()))
|
||||
if (cachedState != null && (force || cachedState.IsChanged()))
|
||||
{
|
||||
var key = GetStorageKey(turnContext);
|
||||
var changes = new Dictionary<string, object>
|
||||
|
@ -101,12 +127,17 @@ namespace Microsoft.Bot.Builder
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears any state currently stored in this state scope.
|
||||
/// Clears the state cache for this <see cref="BotState"/>.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="cancellationToken">cancellation token.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
/// <remarks>NOTE: that SaveChangesAsync must be called in order for the cleared state to be persisted to the underlying store.</remarks>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>This method clears the state cache in the turn context. Call
|
||||
/// <see cref="SaveChangesAsync(ITurnContext, bool, CancellationToken)"/> to persist this
|
||||
/// change in the storage layer.
|
||||
/// </remarks>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="turnContext"/> is <c>null</c>.</exception>
|
||||
public virtual Task ClearStateAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (turnContext == null)
|
||||
|
@ -121,11 +152,13 @@ namespace Microsoft.Bot.Builder
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete any state currently stored in this state scope.
|
||||
/// Deletes any state in storage and the cache for this <see cref="BotState"/>.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="cancellationToken">cancellation token.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="turnContext"/> is <c>null</c>.</exception>
|
||||
public virtual async Task DeleteAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (turnContext == null)
|
||||
|
@ -144,10 +177,11 @@ namespace Microsoft.Bot.Builder
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of the raw cached data from the TurnContext, this can be used for tracing scenarios.
|
||||
/// Gets a copy of the raw cached data for this <see cref="BotState"/> from the turn context.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <returns>A JSON representation of the cached state.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="turnContext"/> is <c>null</c>.</exception>
|
||||
public JToken Get(ITurnContext turnContext)
|
||||
{
|
||||
if (turnContext == null)
|
||||
|
@ -168,15 +202,17 @@ namespace Microsoft.Bot.Builder
|
|||
protected abstract string GetStorageKey(ITurnContext turnContext);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a property from the state cache in the turn context.
|
||||
/// Gets the value of a property from the state cache for this <see cref="BotState"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The property type.</typeparam>
|
||||
/// <typeparam name="T">The value type of the property.</typeparam>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="propertyName">The name of the property to get.</param>
|
||||
/// <param name="propertyName">The name of the property.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>If the task is successful, the result contains the property value.</remarks>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="turnContext"/> or
|
||||
/// <paramref name="propertyName"/> is <c>null</c>.</exception>
|
||||
protected Task<T> GetPropertyValueAsync<T>(ITurnContext turnContext, string propertyName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (turnContext == null)
|
||||
|
@ -197,13 +233,15 @@ namespace Microsoft.Bot.Builder
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a property from the state cache in the turn context.
|
||||
/// Deletes a property from the state cache for this <see cref="BotState"/>.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="propertyName">The name of the property to delete.</param>
|
||||
/// <param name="propertyName">The name of the property.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="turnContext"/> or
|
||||
/// <paramref name="propertyName"/> is <c>null</c>.</exception>
|
||||
protected Task DeletePropertyValueAsync(ITurnContext turnContext, string propertyName, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (turnContext == null)
|
||||
|
@ -222,7 +260,7 @@ namespace Microsoft.Bot.Builder
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the value of a property in the state cache in the turn context.
|
||||
/// Sets the value of a property in the state cache for this <see cref="BotState"/>.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="propertyName">The name of the property to set.</param>
|
||||
|
@ -230,6 +268,8 @@ namespace Microsoft.Bot.Builder
|
|||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="turnContext"/> or
|
||||
/// <paramref name="propertyName"/> is <c>null</c>.</exception>
|
||||
protected Task SetPropertyValueAsync(ITurnContext turnContext, string propertyName, object value, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (turnContext == null)
|
||||
|
|
|
@ -6,14 +6,18 @@ using System;
|
|||
namespace Microsoft.Bot.Builder
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles persistence of a conversation state object using the conversation ID as part of the key.
|
||||
/// Defines a state management object for conversation state.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Conversation state is available in any turn in a specific conversation, regardless of user,
|
||||
/// such as in a group conversation.
|
||||
/// </remarks>
|
||||
public class ConversationState : BotState
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConversationState"/> class.
|
||||
/// </summary>
|
||||
/// <param name="storage">The storage provider to use.</param>
|
||||
/// <param name="storage">The storage layer to use.</param>
|
||||
public ConversationState(IStorage storage)
|
||||
: base(storage, nameof(ConversationState))
|
||||
{
|
||||
|
@ -24,6 +28,13 @@ namespace Microsoft.Bot.Builder
|
|||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <returns>The storage key.</returns>
|
||||
/// <remarks>
|
||||
/// Conversation state includes the channel ID and conversation ID as part of its storage key.
|
||||
/// </remarks>
|
||||
/// <exception cref="ArgumentNullException">The <see cref="ITurnContext.Activity"/> for the
|
||||
/// current turn is missing <see cref="Schema.Activity.ChannelId"/> or
|
||||
/// <see cref="Schema.Activity.Conversation"/> information, or the conversation's
|
||||
/// <see cref="Schema.ConversationAccount.Id"/> is missing.</exception>
|
||||
protected override string GetStorageKey(ITurnContext turnContext)
|
||||
{
|
||||
var channelId = turnContext.Activity.ChannelId ?? throw new ArgumentNullException("invalid activity-missing channelId");
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace Microsoft.Bot.Builder
|
|||
public interface IMiddleware
|
||||
{
|
||||
/// <summary>
|
||||
/// When implemented in middleware, processess an incoming activity.
|
||||
/// When implemented in middleware, processes an incoming activity.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="next">The delegate to call to continue the bot middleware pipeline.</param>
|
||||
|
|
|
@ -43,7 +43,7 @@ namespace Microsoft.Bot.Builder
|
|||
/// activity.
|
||||
/// <para>The activity's <see cref="IActivity.Id"/> indicates the activity in the
|
||||
/// conversation to replace.</para>
|
||||
/// <para>If the activity is successfully sent, the <paramref name="next"/> delegater returns
|
||||
/// <para>If the activity is successfully sent, the <paramref name="next"/> delegate returns
|
||||
/// a <see cref="ResourceResponse"/> object containing the ID that the receiving
|
||||
/// channel assigned to the activity. Use this response object as the return value of this handler.</para>
|
||||
/// </remarks>
|
||||
|
@ -74,12 +74,23 @@ namespace Microsoft.Bot.Builder
|
|||
public delegate Task DeleteActivityHandler(ITurnContext turnContext, ConversationReference reference, Func<Task> next);
|
||||
|
||||
/// <summary>
|
||||
/// A ITurnContext where the inbound Activity property is strongly typed.
|
||||
/// Provides context for a turn of a bot, where the context's <see cref="Activity"/> property is strongly typed.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">An IActivity derived type, that is one of IMessageActivity, IConversationUpdateActivity etc.</typeparam>
|
||||
/// <typeparam name="T">The activity type for this turn of the bot.</typeparam>
|
||||
/// <remarks>The <see cref="IActivity"/> interface defines properties shared by every type of activity.
|
||||
/// The interfaces that derive from <see cref="IActivity"/> include properties specific to a specific
|
||||
/// type of activity. For example, <see cref="IMessageActivity"/> includes properties associated with
|
||||
/// message activities, and <see cref="IEventActivity"/> includes properties associated with event activities.</remarks>
|
||||
/// <seealso cref="IActivity"/>
|
||||
/// <seealso cref="ActivityHandler"/>
|
||||
/// <seealso cref="ITurnContext"/>
|
||||
public interface ITurnContext<T> : ITurnContext
|
||||
where T : IActivity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the activity for this turn of the bot.
|
||||
/// </summary>
|
||||
/// <value>The activity for this turn of the bot.</value>
|
||||
new T Activity { get; }
|
||||
}
|
||||
|
||||
|
@ -91,6 +102,7 @@ namespace Microsoft.Bot.Builder
|
|||
/// length of the turn.</remarks>
|
||||
/// <seealso cref="IBot"/>
|
||||
/// <seealso cref="IMiddleware"/>
|
||||
/// <seealso cref="ITurnContext{T}"/>
|
||||
public interface ITurnContext
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -106,15 +118,16 @@ namespace Microsoft.Bot.Builder
|
|||
TurnContextStateCollection TurnState { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the incoming request.
|
||||
/// Gets the activity for this turn of the bot.
|
||||
/// </summary>
|
||||
/// <value>The incoming request.</value>
|
||||
/// <value>The activity for this turn of the bot.</value>
|
||||
Activity Activity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether at least one response was sent for the current turn.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if at least one response was sent for the current turn; otherwise, <c>false</c>.</value>
|
||||
/// <seealso cref="SendActivityAsync(IActivity, CancellationToken)"/>
|
||||
bool Responded { get; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -125,12 +138,13 @@ namespace Microsoft.Bot.Builder
|
|||
/// channel.</param>
|
||||
/// <param name="inputHint">Optional, indicates whether your bot is accepting,
|
||||
/// expecting, or ignoring user input after the message is delivered to the client.
|
||||
/// One of: "acceptingInput", "ignoringInput", or "expectingInput".
|
||||
/// Default is "acceptingInput".</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <see cref="InputHints"/> defines the possible values.
|
||||
/// Default is <see cref="InputHints.AcceptingInput"/>.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>If the activity is successfully sent, the task result contains
|
||||
/// a <see cref="ResourceResponse"/> object containing the ID that the receiving
|
||||
/// a <see cref="ResourceResponse"/> object that contains the ID that the receiving
|
||||
/// channel assigned to the activity.
|
||||
/// <para>See the channel's documentation for limits imposed upon the contents of
|
||||
/// <paramref name="textReplyToSend"/>.</para>
|
||||
|
@ -149,7 +163,8 @@ namespace Microsoft.Bot.Builder
|
|||
/// Sends an activity to the sender of the incoming activity.
|
||||
/// </summary>
|
||||
/// <param name="activity">The activity to send.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>If the activity is successfully sent, the task result contains
|
||||
/// a <see cref="ResourceResponse"/> object containing the ID that the receiving
|
||||
|
@ -165,7 +180,8 @@ namespace Microsoft.Bot.Builder
|
|||
/// Sends a set of activities to the sender of the incoming activity.
|
||||
/// </summary>
|
||||
/// <param name="activities">The activities to send.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>If the activities are successfully sent, the task result contains
|
||||
/// an array of <see cref="ResourceResponse"/> objects containing the IDs that
|
||||
|
@ -181,14 +197,15 @@ namespace Microsoft.Bot.Builder
|
|||
/// Replaces an existing activity.
|
||||
/// </summary>
|
||||
/// <param name="activity">New replacement activity.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>If the activity is successfully sent, the task result contains
|
||||
/// a <see cref="ResourceResponse"/> object containing the ID that the receiving
|
||||
/// channel assigned to the activity.
|
||||
/// <para>Before calling this, set the ID of the replacement activity to the ID
|
||||
/// of the activity to replace.</para>
|
||||
/// <para>Not all channels support this operation. Channels that don't, may throw an exception.</para></remarks>
|
||||
/// <para>Not all channels support this operation. For channels that don't, this call may throw an exception.</para></remarks>
|
||||
/// <seealso cref="OnUpdateActivity(UpdateActivityHandler)"/>
|
||||
/// <seealso cref="SendActivitiesAsync(IActivity[], CancellationToken)"/>
|
||||
/// <seealso cref="DeleteActivityAsync(ConversationReference, CancellationToken)"/>
|
||||
|
@ -198,9 +215,10 @@ namespace Microsoft.Bot.Builder
|
|||
/// Deletes an existing activity.
|
||||
/// </summary>
|
||||
/// <param name="activityId">The ID of the activity to delete.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>Not all channels support this operation. Channels that don't, may throw an exception.</remarks>
|
||||
/// <para>Not all channels support this operation. For channels that don\'t, this call may throw an exception.</para>
|
||||
/// <seealso cref="OnDeleteActivity(DeleteActivityHandler)"/>
|
||||
/// <seealso cref="DeleteActivityAsync(ConversationReference, CancellationToken)"/>
|
||||
/// <seealso cref="SendActivitiesAsync(IActivity[], CancellationToken)"/>
|
||||
|
@ -211,11 +229,12 @@ namespace Microsoft.Bot.Builder
|
|||
/// Deletes an existing activity.
|
||||
/// </summary>
|
||||
/// <param name="conversationReference">The conversation containing the activity to delete.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects
|
||||
/// or threads to receive notice of cancellation.</param>
|
||||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>The conversation reference's <see cref="ConversationReference.ActivityId"/>
|
||||
/// indicates the activity in the conversation to delete.
|
||||
/// <para>Not all channels support this operation. Channels that don't, may throw an exception.</para></remarks>
|
||||
/// <para>Not all channels support this operation. For channels that don't, this call may throw an exception.</para></remarks>
|
||||
/// <seealso cref="OnDeleteActivity(DeleteActivityHandler)"/>
|
||||
/// <seealso cref="DeleteActivityAsync(string, CancellationToken)"/>
|
||||
/// <seealso cref="SendActivitiesAsync(IActivity[], CancellationToken)"/>
|
||||
|
@ -228,7 +247,7 @@ namespace Microsoft.Bot.Builder
|
|||
/// <param name="handler">The handler to add to the context object.</param>
|
||||
/// <returns>The updated context object.</returns>
|
||||
/// <remarks>When the context's <see cref="SendActivityAsync(IActivity, CancellationToken)"/>
|
||||
/// or <see cref="SendActivitiesAsync(IActivity[], CancellationToken)"/> methods are called,
|
||||
/// or <see cref="SendActivitiesAsync(IActivity[], CancellationToken)"/> method is called,
|
||||
/// the adapter calls the registered handlers in the order in which they were
|
||||
/// added to the context object.
|
||||
/// </remarks>
|
||||
|
|
|
@ -9,7 +9,7 @@ using Microsoft.Bot.Schema;
|
|||
namespace Microsoft.Bot.Builder.TraceExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains methods for woring with <see cref="ITurnContext"/> objects.
|
||||
/// Contains methods for working with <see cref="ITurnContext"/> objects.
|
||||
/// </summary>
|
||||
public static class ITurnContextExtensions
|
||||
{
|
||||
|
@ -26,7 +26,7 @@ namespace Microsoft.Bot.Builder.TraceExtensions
|
|||
/// <returns>A task that represents the work queued to execute.</returns>
|
||||
/// <remarks>If the adapter is being hosted in the Emulator, the task result contains
|
||||
/// a <see cref="ResourceResponse"/> object with the original trace activity's ID; otherwise,
|
||||
/// it containsa <see cref="ResourceResponse"/> object containing the ID that the receiving
|
||||
/// it contains a <see cref="ResourceResponse"/> object containing the ID that the receiving
|
||||
/// channel assigned to the activity.</remarks>
|
||||
public static Task<ResourceResponse> TraceActivityAsync(this ITurnContext turnContext, string name, object value = null, string valueType = null, [CallerMemberName] string label = null, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace Microsoft.Bot.Builder.Integration
|
|||
public IChannelProvider ChannelProvider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an error handler to use to catche exceptions in the middleware or application.
|
||||
/// Gets or sets an error handler to use to catch exceptions in the middleware or application.
|
||||
/// </summary>
|
||||
/// <value>The error handler.</value>
|
||||
public Func<ITurnContext, Exception, Task> OnTurnError { get; set; }
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace Microsoft.Bot.Builder.Integration
|
|||
/// <summary>
|
||||
/// Sends a proactive message to a conversation.
|
||||
/// </summary>
|
||||
/// <param name="botId">The application ID of the bot. This paramter is ignored in
|
||||
/// <param name="botId">The application ID of the bot. This parameter is ignored in
|
||||
/// single tenant the Adapters (Console, Test, etc) but is critical to the BotFrameworkAdapter
|
||||
/// which is multi-tenant aware. </param>
|
||||
/// <param name="reference">A reference to the conversation to continue.</param>
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace Microsoft.Bot.Builder
|
|||
/// The memory transcript store stores transcripts in volatile memory in a Dictionary.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Because this uses an unbounded volitile dictionary this should only be used for unit tests or non-production environments.
|
||||
/// Because this uses an unbounded volatile dictionary this should only be used for unit tests or non-production environments.
|
||||
/// </remarks>
|
||||
public class MemoryTranscriptStore : ITranscriptStore
|
||||
{
|
||||
|
|
|
@ -6,14 +6,17 @@ using System;
|
|||
namespace Microsoft.Bot.Builder
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles persistence of a conversation state object using the conversation.Id and from.Id part of an activity.
|
||||
/// Defines a state management object for private conversation state.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Private conversation state is scoped to both the specific conversation and to that specific user.
|
||||
/// </remarks>
|
||||
public class PrivateConversationState : BotState
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PrivateConversationState"/> class.
|
||||
/// </summary>
|
||||
/// <param name="storage">The storage provider to use.</param>
|
||||
/// <param name="storage">The storage layer to use.</param>
|
||||
public PrivateConversationState(IStorage storage)
|
||||
: base(storage, nameof(PrivateConversationState))
|
||||
{
|
||||
|
@ -24,6 +27,16 @@ namespace Microsoft.Bot.Builder
|
|||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <returns>The storage key.</returns>
|
||||
/// <remarks>
|
||||
/// Private conversation state includes the channel ID, conversation ID, and user ID as part
|
||||
/// of its storage key.
|
||||
/// </remarks>
|
||||
/// <exception cref="ArgumentNullException">The <see cref="ITurnContext.Activity"/> for the
|
||||
/// current turn is missing <see cref="Schema.Activity.ChannelId"/>,
|
||||
/// <see cref="Schema.Activity.Conversation"/>, or
|
||||
/// <see cref="Schema.Activity.From"/> information; or
|
||||
/// the conversation's or sender's <see cref="Schema.ConversationAccount.Id"/> is missing.
|
||||
/// </exception>
|
||||
protected override string GetStorageKey(ITurnContext turnContext)
|
||||
{
|
||||
var channelId = turnContext.Activity.ChannelId ?? throw new ArgumentNullException("invalid activity-missing channelId");
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace Microsoft.Bot.Builder
|
|||
{
|
||||
/// <summary>
|
||||
/// When added, this middleware will send typing activities back to the user when a Message activity
|
||||
/// is receieved to let them know that the bot has receieved the message and is working on the response.
|
||||
/// is received to let them know that the bot has received the message and is working on the response.
|
||||
/// You can specify a delay in milliseconds before the first typing activity is sent and then a frequency,
|
||||
/// also in milliseconds which determines how often another typing activity is sent. Typing activities
|
||||
/// will continue to be sent until your bot sends another message back to the user.
|
||||
|
@ -42,7 +42,7 @@ namespace Microsoft.Bot.Builder
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processess an incoming activity.
|
||||
/// Processes an incoming activity.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <param name="next">The delegate to call to continue the bot middleware pipeline.</param>
|
||||
|
@ -64,7 +64,7 @@ namespace Microsoft.Bot.Builder
|
|||
cts = new CancellationTokenSource();
|
||||
cancellationToken.Register(() => cts.Cancel());
|
||||
|
||||
// do not await task - we want this to run in thw background and we wil cancel it when its done
|
||||
// do not await task - we want this to run in the background and we will cancel it when its done
|
||||
var task = Task.Run(() => SendTypingAsync(turnContext, _delay, _period, cts.Token), cancellationToken);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Bot.Connector;
|
||||
using Microsoft.Bot.Schema;
|
||||
|
||||
namespace Microsoft.Bot.Builder
|
||||
{
|
||||
/// <summary>
|
||||
/// Middleware to patch mention Entities from Skype since they don't conform to expected values.
|
||||
/// Bots that interact with Skype should use this middleware if mentions are used.
|
||||
/// </summary>
|
||||
/// <description>
|
||||
/// A Skype mention "text" field is of the format:
|
||||
/// <at id=\"28:2bc5b54d-5d48-4ff1-bd25-03dcbb5ce918\">botname</at>
|
||||
/// But Activity.Text doesn't contain those tags and RemoveMentionText can't remove
|
||||
/// the entity from Activity.Text.
|
||||
/// This will remove the <at> nodes, leaving just the name.
|
||||
/// </description>
|
||||
public class SkypeMentionNormalizeMiddleware : IMiddleware
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SkypeMentionNormalizeMiddleware"/> class.
|
||||
/// </summary>
|
||||
public SkypeMentionNormalizeMiddleware()
|
||||
{
|
||||
}
|
||||
|
||||
public static void NormalizeSkypMentionText(Activity activity)
|
||||
{
|
||||
if (activity.ChannelId == Channels.Skype && activity.Type == ActivityTypes.Message)
|
||||
{
|
||||
foreach (var entity in activity.Entities)
|
||||
{
|
||||
if (entity.Type == "mention")
|
||||
{
|
||||
string text = (string)entity.Properties["text"];
|
||||
var mentionNameMatch = Regex.Match(text, @"(?<=<at.*>)(.*?)(?=<\/at>)", RegexOptions.IgnoreCase);
|
||||
if (mentionNameMatch.Success)
|
||||
{
|
||||
entity.Properties["text"] = mentionNameMatch.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Middleware implementation which corrects Enity.Mention.Text to a value RemoveMentionText can work with.
|
||||
/// </summary>
|
||||
/// <param name="turnContext">turn context.</param>
|
||||
/// <param name="next">next middleware.</param>
|
||||
/// <param name="cancellationToken">cancellationToken.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||
public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
NormalizeSkypMentionText(turnContext.Activity);
|
||||
await next(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,7 +35,9 @@ namespace Microsoft.Bot.Builder
|
|||
/// <summary>
|
||||
/// Gets the currently configured <see cref="IBotTelemetryClient"/> that logs the QnaMessage event.
|
||||
/// </summary>
|
||||
/// <value>The <see cref="IBotTelemetryClient"/> being used to log events.</value>
|
||||
/// <value>
|
||||
/// The <see cref="IBotTelemetryClient"/> being used to log events.
|
||||
/// </value>
|
||||
public IBotTelemetryClient TelemetryClient { get; }
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -300,9 +300,12 @@ namespace Microsoft.Bot.Builder
|
|||
/// channel assigned to the activity.
|
||||
/// <para>Before calling this, set the ID of the replacement activity to the ID
|
||||
/// of the activity to replace.</para></remarks>
|
||||
public async Task<ResourceResponse> UpdateActivityAsync(IActivity activity, CancellationToken cancellationToken = default(CancellationToken))
|
||||
public async Task<ResourceResponse> UpdateActivityAsync(IActivity activity, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Activity a = (Activity)activity;
|
||||
BotAssert.ActivityNotNull(activity);
|
||||
|
||||
var conversationReference = Activity.GetConversationReference();
|
||||
var a = activity.ApplyConversationReference(conversationReference);
|
||||
|
||||
async Task<ResourceResponse> ActuallyUpdateStuff()
|
||||
{
|
||||
|
|
|
@ -6,14 +6,18 @@ using System;
|
|||
namespace Microsoft.Bot.Builder
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles persistence of a user state object using the user ID as part of the key.
|
||||
/// Defines a state management object for user state.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// User state is available in any turn that the bot is conversing with that user on that
|
||||
/// channel, regardless of the conversation.
|
||||
/// </remarks>
|
||||
public class UserState : BotState
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UserState"/> class.
|
||||
/// </summary>
|
||||
/// <param name="storage">The storage provider to use.</param>
|
||||
/// <param name="storage">The storage layer to use.</param>
|
||||
public UserState(IStorage storage)
|
||||
: base(storage, nameof(UserState))
|
||||
{
|
||||
|
@ -24,6 +28,13 @@ namespace Microsoft.Bot.Builder
|
|||
/// </summary>
|
||||
/// <param name="turnContext">The context object for this turn.</param>
|
||||
/// <returns>The storage key.</returns>
|
||||
/// <remarks>
|
||||
/// User state includes the channel ID and user ID as part of its storage key.
|
||||
/// </remarks>
|
||||
/// <exception cref="ArgumentNullException">The <see cref="ITurnContext.Activity"/> for the
|
||||
/// current turn is missing <see cref="Schema.Activity.ChannelId"/> or
|
||||
/// <see cref="Schema.Activity.From"/> information, or the sender's
|
||||
/// <see cref="Schema.ConversationAccount.Id"/> is missing.</exception>
|
||||
protected override string GetStorageKey(ITurnContext turnContext)
|
||||
{
|
||||
var channelId = turnContext.Activity.ChannelId ?? throw new ArgumentNullException("invalid activity-missing channelId");
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
// <auto-generated>
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for
|
||||
// license information.
|
||||
//
|
||||
// Code generated by Microsoft (R) AutoRest Code Generator.
|
||||
// Changes may cause incorrect behavior and will be lost if the code is
|
||||
// regenerated.
|
||||
// </auto-generated>
|
||||
|
||||
namespace Microsoft.Bot.Connector.Teams
|
||||
{
|
||||
using Microsoft.Rest;
|
||||
using Microsoft.Bot.Schema.Teams;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
/// <summary>
|
||||
/// The Bot Connector REST API extension for Microsoft Teams allows your
|
||||
/// bot to perform extended operations on to Microsoft Teams channel
|
||||
/// configured in the
|
||||
/// [Bot Framework Developer Portal](https://dev.botframework.com). The
|
||||
/// Connector service uses industry-standard REST and JSON over HTTPS.
|
||||
///
|
||||
/// Client libraries for this REST API are available. See below for a list.
|
||||
///
|
||||
///
|
||||
///
|
||||
/// Authentication for both the Bot Connector and Bot State REST APIs is
|
||||
/// accomplished with JWT Bearer tokens, and is
|
||||
/// described in detail in the [Connector
|
||||
/// Authentication](https://docs.botframework.com/en-us/restapi/authentication)
|
||||
/// document.
|
||||
///
|
||||
/// # Client Libraries for the Bot Connector REST API
|
||||
///
|
||||
/// * [Bot Builder for
|
||||
/// C#](https://docs.botframework.com/en-us/csharp/builder/sdkreference/)
|
||||
/// * [Bot Builder for
|
||||
/// Node.js](https://docs.botframework.com/en-us/node/builder/overview/)
|
||||
///
|
||||
/// © 2016 Microsoft
|
||||
/// </summary>
|
||||
public partial interface ITeamsConnectorClient : System.IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The base URI of the service.
|
||||
/// </summary>
|
||||
System.Uri BaseUri { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets json serialization settings.
|
||||
/// </summary>
|
||||
JsonSerializerSettings SerializationSettings { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets json deserialization settings.
|
||||
/// </summary>
|
||||
JsonSerializerSettings DeserializationSettings { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Subscription credentials which uniquely identify client
|
||||
/// subscription.
|
||||
/// </summary>
|
||||
ServiceClientCredentials Credentials { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ITeamsOperations.
|
||||
/// </summary>
|
||||
ITeamsOperations Teams { get; }
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
// <auto-generated>
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for
|
||||
// license information.
|
||||
//
|
||||
// Code generated by Microsoft (R) AutoRest Code Generator.
|
||||
// Changes may cause incorrect behavior and will be lost if the code is
|
||||
// regenerated.
|
||||
// </auto-generated>
|
||||
|
||||
namespace Microsoft.Bot.Connector.Teams
|
||||
{
|
||||
using Microsoft.Rest;
|
||||
using Microsoft.Bot.Schema.Teams;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// TeamsOperations operations.
|
||||
/// </summary>
|
||||
public partial interface ITeamsOperations
|
||||
{
|
||||
/// <summary>
|
||||
/// Fetches channel list for a given team
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Fetch the channel list.
|
||||
/// </remarks>
|
||||
/// <param name='teamId'>
|
||||
/// Team Id
|
||||
/// </param>
|
||||
/// <param name='customHeaders'>
|
||||
/// The headers that will be added to request.
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
/// <exception cref="Microsoft.Rest.HttpOperationException">
|
||||
/// Thrown when the operation returned an invalid status code
|
||||
/// </exception>
|
||||
/// <exception cref="Microsoft.Rest.SerializationException">
|
||||
/// Thrown when unable to deserialize the response
|
||||
/// </exception>
|
||||
/// <exception cref="Microsoft.Rest.ValidationException">
|
||||
/// Thrown when a required parameter is null
|
||||
/// </exception>
|
||||
Task<HttpOperationResponse<ConversationList>> FetchChannelListWithHttpMessagesAsync(string teamId, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
/// <summary>
|
||||
/// Fetches details related to a team
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Fetch details for a team
|
||||
/// </remarks>
|
||||
/// <param name='teamId'>
|
||||
/// Team Id
|
||||
/// </param>
|
||||
/// <param name='customHeaders'>
|
||||
/// The headers that will be added to request.
|
||||
/// </param>
|
||||
/// <param name='cancellationToken'>
|
||||
/// The cancellation token.
|
||||
/// </param>
|
||||
/// <exception cref="Microsoft.Rest.HttpOperationException">
|
||||
/// Thrown when the operation returned an invalid status code
|
||||
/// </exception>
|
||||
/// <exception cref="Microsoft.Rest.SerializationException">
|
||||
/// Thrown when unable to deserialize the response
|
||||
/// </exception>
|
||||
/// <exception cref="Microsoft.Rest.ValidationException">
|
||||
/// Thrown when a required parameter is null
|
||||
/// </exception>
|
||||
Task<HttpOperationResponse<TeamDetails>> FetchTeamDetailsWithHttpMessagesAsync(string teamId, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Version Condition=" '$(PackageVersion)' == '' ">4.0.0-local</Version>
|
||||
<Version Condition=" '$(PackageVersion)' != '' ">$(PackageVersion)</Version>
|
||||
<PackageVersion Condition=" '$(PackageVersion)' == '' ">4.0.0-local</PackageVersion>
|
||||
<PackageVersion Condition=" '$(PackageVersion)' != '' ">$(PackageVersion)</PackageVersion>
|
||||
<Configurations>Debug;Release;Debug - NuGet Packages</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<DelaySign>true</DelaySign>
|
||||
<AssemblyOriginatorKeyFile>..\..\build\35MSSharedLib1024.snk</AssemblyOriginatorKeyFile>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<PackageId>Microsoft.Bot.Connector.Teams</PackageId>
|
||||
<Description>Library for building bots using Microsoft Bot Framework</Description>
|
||||
<Summary>Library for building bots using Microsoft Bot Framework</Summary>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Rest.ClientRuntime" Version="2.3.10" />
|
||||
<PackageReference Include="Microsoft.Bot.Schema.Teams" Condition=" '$(PackageVersion)' == '' " Version="4.0.0-local" />
|
||||
<PackageReference Include="Microsoft.Bot.Schema.Teams" Condition=" '$(PackageVersion)' != '' " Version="$(PackageVersion)" />
|
||||
|
||||
<!-- This may move to the root level dir.props file at some point. -->
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.Bot.Schema.Teams\Microsoft.Bot.Schema.Teams.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,306 @@
|
|||
// <auto-generated>
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for
|
||||
// license information.
|
||||
//
|
||||
// Code generated by Microsoft (R) AutoRest Code Generator.
|
||||
// Changes may cause incorrect behavior and will be lost if the code is
|
||||
// regenerated.
|
||||
// </auto-generated>
|
||||
|
||||
namespace Microsoft.Bot.Connector.Teams
|
||||
{
|
||||
using Microsoft.Rest;
|
||||
using Microsoft.Rest.Serialization;
|
||||
using Microsoft.Bot.Schema.Teams;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
|
||||
/// <summary>
|
||||
/// The Bot Connector REST API extension for Microsoft Teams allows your
|
||||
/// bot to perform extended operations on to Microsoft Teams channel
|
||||
/// configured in the
|
||||
/// [Bot Framework Developer Portal](https://dev.botframework.com). The
|
||||
/// Connector service uses industry-standard REST and JSON over HTTPS.
|
||||
///
|
||||
/// Client libraries for this REST API are available. See below for a list.
|
||||
///
|
||||
///
|
||||
///
|
||||
/// Authentication for both the Bot Connector and Bot State REST APIs is
|
||||
/// accomplished with JWT Bearer tokens, and is
|
||||
/// described in detail in the [Connector
|
||||
/// Authentication](https://docs.botframework.com/en-us/restapi/authentication)
|
||||
/// document.
|
||||
///
|
||||
/// # Client Libraries for the Bot Connector REST API
|
||||
///
|
||||
/// * [Bot Builder for
|
||||
/// C#](https://docs.botframework.com/en-us/csharp/builder/sdkreference/)
|
||||
/// * [Bot Builder for
|
||||
/// Node.js](https://docs.botframework.com/en-us/node/builder/overview/)
|
||||
///
|
||||
/// © 2016 Microsoft
|
||||
/// </summary>
|
||||
public partial class TeamsConnectorClient : ServiceClient<TeamsConnectorClient>, ITeamsConnectorClient
|
||||
{
|
||||
/// <summary>
|
||||
/// The base URI of the service.
|
||||
/// </summary>
|
||||
public System.Uri BaseUri { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets json serialization settings.
|
||||
/// </summary>
|
||||
public JsonSerializerSettings SerializationSettings { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets json deserialization settings.
|
||||
/// </summary>
|
||||
public JsonSerializerSettings DeserializationSettings { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Subscription credentials which uniquely identify client subscription.
|
||||
/// </summary>
|
||||
public ServiceClientCredentials Credentials { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ITeamsOperations.
|
||||
/// </summary>
|
||||
public virtual ITeamsOperations Teams { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the TeamsConnectorClient class.
|
||||
/// </summary>
|
||||
/// <param name='handlers'>
|
||||
/// Optional. The delegating handlers to add to the http client pipeline.
|
||||
/// </param>
|
||||
protected TeamsConnectorClient(params DelegatingHandler[] handlers) : base(handlers)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the TeamsConnectorClient class.
|
||||
/// </summary>
|
||||
/// <param name='rootHandler'>
|
||||
/// Optional. The http client handler used to handle http transport.
|
||||
/// </param>
|
||||
/// <param name='handlers'>
|
||||
/// Optional. The delegating handlers to add to the http client pipeline.
|
||||
/// </param>
|
||||
protected TeamsConnectorClient(HttpClientHandler rootHandler, params DelegatingHandler[] handlers) : base(rootHandler, handlers)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the TeamsConnectorClient class.
|
||||
/// </summary>
|
||||
/// <param name='baseUri'>
|
||||
/// Optional. The base URI of the service.
|
||||
/// </param>
|
||||
/// <param name='handlers'>
|
||||
/// Optional. The delegating handlers to add to the http client pipeline.
|
||||
/// </param>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// Thrown when a required parameter is null
|
||||
/// </exception>
|
||||
protected TeamsConnectorClient(System.Uri baseUri, params DelegatingHandler[] handlers) : this(handlers)
|
||||
{
|
||||
if (baseUri == null)
|
||||
{
|
||||
throw new System.ArgumentNullException("baseUri");
|
||||
}
|
||||
BaseUri = baseUri;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the TeamsConnectorClient class.
|
||||
/// </summary>
|
||||
/// <param name='baseUri'>
|
||||
/// Optional. The base URI of the service.
|
||||
/// </param>
|
||||
/// <param name='rootHandler'>
|
||||
/// Optional. The http client handler used to handle http transport.
|
||||
/// </param>
|
||||
/// <param name='handlers'>
|
||||
/// Optional. The delegating handlers to add to the http client pipeline.
|
||||
/// </param>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// Thrown when a required parameter is null
|
||||
/// </exception>
|
||||
protected TeamsConnectorClient(System.Uri baseUri, HttpClientHandler rootHandler, params DelegatingHandler[] handlers) : this(rootHandler, handlers)
|
||||
{
|
||||
if (baseUri == null)
|
||||
{
|
||||
throw new System.ArgumentNullException("baseUri");
|
||||
}
|
||||
BaseUri = baseUri;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the TeamsConnectorClient class.
|
||||
/// </summary>
|
||||
/// <param name='credentials'>
|
||||
/// Required. Subscription credentials which uniquely identify client subscription.
|
||||
/// </param>
|
||||
/// <param name='handlers'>
|
||||
/// Optional. The delegating handlers to add to the http client pipeline.
|
||||
/// </param>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// Thrown when a required parameter is null
|
||||
/// </exception>
|
||||
public TeamsConnectorClient(ServiceClientCredentials credentials, params DelegatingHandler[] handlers) : this(handlers)
|
||||
{
|
||||
if (credentials == null)
|
||||
{
|
||||
throw new System.ArgumentNullException("credentials");
|
||||
}
|
||||
Credentials = credentials;
|
||||
if (Credentials != null)
|
||||
{
|
||||
Credentials.InitializeServiceClient(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the TeamsConnectorClient class.
|
||||
/// </summary>
|
||||
/// <param name='credentials'>
|
||||
/// Required. Subscription credentials which uniquely identify client subscription.
|
||||
/// </param>
|
||||
/// <param name='rootHandler'>
|
||||
/// Optional. The http client handler used to handle http transport.
|
||||
/// </param>
|
||||
/// <param name='handlers'>
|
||||
/// Optional. The delegating handlers to add to the http client pipeline.
|
||||
/// </param>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// Thrown when a required parameter is null
|
||||
/// </exception>
|
||||
public TeamsConnectorClient(ServiceClientCredentials credentials, HttpClientHandler rootHandler, params DelegatingHandler[] handlers) : this(rootHandler, handlers)
|
||||
{
|
||||
if (credentials == null)
|
||||
{
|
||||
throw new System.ArgumentNullException("credentials");
|
||||
}
|
||||
Credentials = credentials;
|
||||
if (Credentials != null)
|
||||
{
|
||||
Credentials.InitializeServiceClient(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the TeamsConnectorClient class.
|
||||
/// </summary>
|
||||
/// <param name='baseUri'>
|
||||
/// Optional. The base URI of the service.
|
||||
/// </param>
|
||||
/// <param name='credentials'>
|
||||
/// Required. Subscription credentials which uniquely identify client subscription.
|
||||
/// </param>
|
||||
/// <param name='handlers'>
|
||||
/// Optional. The delegating handlers to add to the http client pipeline.
|
||||
/// </param>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// Thrown when a required parameter is null
|
||||
/// </exception>
|
||||
public TeamsConnectorClient(System.Uri baseUri, ServiceClientCredentials credentials, params DelegatingHandler[] handlers) : this(handlers)
|
||||
{
|
||||
if (baseUri == null)
|
||||
{
|
||||
throw new System.ArgumentNullException("baseUri");
|
||||
}
|
||||
if (credentials == null)
|
||||
{
|
||||
throw new System.ArgumentNullException("credentials");
|
||||
}
|
||||
BaseUri = baseUri;
|
||||
Credentials = credentials;
|
||||
if (Credentials != null)
|
||||
{
|
||||
Credentials.InitializeServiceClient(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the TeamsConnectorClient class.
|
||||
/// </summary>
|
||||
/// <param name='baseUri'>
|
||||
/// Optional. The base URI of the service.
|
||||
/// </param>
|
||||
/// <param name='credentials'>
|
||||
/// Required. Subscription credentials which uniquely identify client subscription.
|
||||
/// </param>
|
||||
/// <param name='rootHandler'>
|
||||
/// Optional. The http client handler used to handle http transport.
|
||||
/// </param>
|
||||
/// <param name='handlers'>
|
||||
/// Optional. The delegating handlers to add to the http client pipeline.
|
||||
/// </param>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// Thrown when a required parameter is null
|
||||
/// </exception>
|
||||
public TeamsConnectorClient(System.Uri baseUri, ServiceClientCredentials credentials, HttpClientHandler rootHandler, params DelegatingHandler[] handlers) : this(rootHandler, handlers)
|
||||
{
|
||||
if (baseUri == null)
|
||||
{
|
||||
throw new System.ArgumentNullException("baseUri");
|
||||
}
|
||||
if (credentials == null)
|
||||
{
|
||||
throw new System.ArgumentNullException("credentials");
|
||||
}
|
||||
BaseUri = baseUri;
|
||||
Credentials = credentials;
|
||||
if (Credentials != null)
|
||||
{
|
||||
Credentials.InitializeServiceClient(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An optional partial-method to perform custom initialization.
|
||||
///</summary>
|
||||
partial void CustomInitialize();
|
||||
/// <summary>
|
||||
/// Initializes client properties.
|
||||
/// </summary>
|
||||
private void Initialize()
|
||||
{
|
||||
Teams = new TeamsOperations(this);
|
||||
BaseUri = new System.Uri("https://api.botframework.com");
|
||||
SerializationSettings = new JsonSerializerSettings
|
||||
{
|
||||
Formatting = Newtonsoft.Json.Formatting.Indented,
|
||||
DateFormatHandling = Newtonsoft.Json.DateFormatHandling.IsoDateFormat,
|
||||
DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc,
|
||||
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
|
||||
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize,
|
||||
ContractResolver = new ReadOnlyJsonContractResolver(),
|
||||
Converters = new List<JsonConverter>
|
||||
{
|
||||
new Iso8601TimeSpanConverter()
|
||||
}
|
||||
};
|
||||
DeserializationSettings = new JsonSerializerSettings
|
||||
{
|
||||
DateFormatHandling = Newtonsoft.Json.DateFormatHandling.IsoDateFormat,
|
||||
DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc,
|
||||
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
|
||||
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize,
|
||||
ContractResolver = new ReadOnlyJsonContractResolver(),
|
||||
Converters = new List<JsonConverter>
|
||||
{
|
||||
new Iso8601TimeSpanConverter()
|
||||
}
|
||||
};
|
||||
CustomInitialize();
|
||||
}
|
||||
}
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче