This commit is contained in:
Axel Suarez 2020-09-24 10:27:34 -07:00
Родитель 48affcdbe0
Коммит 670677dcdb
3166 изменённых файлов: 1 добавлений и 307784 удалений

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

@ -16,97 +16,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{AD743B78
tests\updateTranscripts.cmd = tests\updateTranscripts.cmd
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialogs.Adaptive", "libraries\Microsoft.Bot.Builder.Dialogs.Adaptive\Microsoft.Bot.Builder.Dialogs.Adaptive.csproj", "{3CF175CF-1AF4-4109-96CB-221684DCED7D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialogs.Adaptive.Tests", "tests\Microsoft.Bot.Builder.Dialogs.Adaptive.Tests\Microsoft.Bot.Builder.Dialogs.Adaptive.Tests.csproj", "{CA008713-655E-46DA-BBDF-1EF6B8CE7DCA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Azure.Tests", "tests\Microsoft.Bot.Builder.Azure.Tests\Microsoft.Bot.Builder.Azure.Tests.csproj", "{E325A0E2-716A-49E0-9767-5087CF05727C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Azure", "libraries\Microsoft.Bot.Builder.Azure\Microsoft.Bot.Builder.Azure.csproj", "{B2C5EED9-21B2-4763-AB92-C1995B76DA3A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.AI.QnA.Tests", "tests\Microsoft.Bot.Builder.AI.QnA.Tests\Microsoft.Bot.Builder.AI.QnA.Tests.csproj", "{9BDF7020-A19F-4C6C-B329-E4BFEFF6DE6B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Connector", "libraries\Microsoft.Bot.Connector\Microsoft.Bot.Connector.csproj", "{6462DA5D-27DC-4CD5-9467-5EFB998FD838}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Schema", "libraries\Microsoft.Bot.Schema\Microsoft.Bot.Schema.csproj", "{C1F54CDC-AD1D-45BB-8F7D-F49E411AFAF1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder", "libraries\Microsoft.Bot.Builder\Microsoft.Bot.Builder.csproj", "{ADA8AB8B-2066-4193-B8F7-985669B23E00}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Connector.Tests", "tests\Microsoft.Bot.Connector.Tests\Microsoft.Bot.Connector.Tests.csproj", "{BF414C86-DB3B-4022-9B29-DCE8AA954C12}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Integration", "Integration", "{276EBE79-A13A-46BD-A566-B01DC0477A9B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.AI.Luis", "libraries\Microsoft.Bot.Builder.AI.LUIS\Microsoft.Bot.Builder.AI.Luis.csproj", "{67AA3C00-E2C5-4D13-BA5E-72EB0E5B8DAA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.AI.Luis.Tests", "tests\Microsoft.Bot.Builder.AI.LUIS.Tests\Microsoft.Bot.Builder.AI.Luis.Tests.csproj", "{7BCEBDC1-D57F-4717-9B15-4FACD5473489}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Integration.AspNet.Core", "libraries\integration\Microsoft.Bot.Builder.Integration.AspNet.Core\Microsoft.Bot.Builder.Integration.AspNet.Core.csproj", "{053BD8B8-B5C1-4C45-81D4-C9BA8D5B3CE2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.TemplateManager", "libraries\Microsoft.Bot.Builder.TemplateManager\Microsoft.Bot.Builder.TemplateManager.csproj", "{EED5F0D3-6F00-4ED1-9108-5ADCB10A3734}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.TemplateManager.Tests", "tests\Microsoft.Bot.Builder.TemplateManager\Microsoft.Bot.Builder.TemplateManager.Tests.csproj", "{C93F6192-0123-4121-AD92-374A71E4B0F3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Schema.Tests", "tests\Microsoft.Bot.Schema.Tests\Microsoft.Bot.Schema.Tests.csproj", "{A4184239-F13F-4A09-B2D3-0B9532609248}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.AI.QnA", "libraries\Microsoft.Bot.Builder.AI.QnA\Microsoft.Bot.Builder.AI.QnA.csproj", "{D9B2EF5D-0515-460F-948A-2BB70C8DCF62}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AI", "AI", "{763168FA-A590-482C-84D8-2922F7ADB1A2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialogs", "libraries\Microsoft.Bot.Builder.Dialogs\Microsoft.Bot.Builder.Dialogs.csproj", "{0F639EB4-FB64-4909-8A10-FB93E7BE3AFB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialogs.Tests", "tests\Microsoft.Bot.Builder.Dialogs.Tests\Microsoft.Bot.Builder.Dialogs.Tests.csproj", "{2F77CA1D-E6F0-4DEA-96BB-8A039F4D0FF8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Integration", "Integration", "{0A0E26B0-7A46-4F1A-8BFE-9A763FDF6CF8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Integration.AspNet.Core.Tests", "tests\integration\Microsoft.Bot.Builder.Integration.AspNet.Core.Tests\Microsoft.Bot.Builder.Integration.AspNet.Core.Tests.csproj", "{62C4DC83-3B07-45D7-856E-224FFB368E5F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Tests", "tests\Microsoft.Bot.Builder.Tests\Microsoft.Bot.Builder.Tests.csproj", "{35F9B8A8-1974-4795-930B-5E4980EE85E5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Transcripts.Tests", "tests\Microsoft.Bot.Builder.Transcripts.Tests\Microsoft.Bot.Builder.Transcripts.Tests.csproj", "{71698D71-4C6F-40D5-8BDE-2587514CA21C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Configuration", "libraries\Microsoft.Bot.Configuration\Microsoft.Bot.Configuration.csproj", "{0B8ABFDB-F9CF-4EC6-988E-9C32D9E01C26}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Configuration.Tests", "tests\Microsoft.Bot.Configuration.Tests\Microsoft.Bot.Configuration.Tests.csproj", "{1B7920E6-5262-4054-B72D-3A8DBBA057D2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Integration.ApplicationInsights.Core", "libraries\integration\Microsoft.Bot.Builder.Integration.ApplicationInsights.Core\Microsoft.Bot.Builder.Integration.ApplicationInsights.Core.csproj", "{8FC920C6-E895-4A17-AB2F-452FAAA36CC8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.ApplicationInsights", "libraries\Microsoft.Bot.Builder.ApplicationInsights\Microsoft.Bot.Builder.ApplicationInsights.csproj", "{B609DB2C-5C1F-46D1-A0FA-A0FF9216899A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.ApplicationInsights.Core.Tests", "tests\integration\Microsoft.Bot.ApplicationInsights.Core.Tests\Microsoft.Bot.ApplicationInsights.Core.Tests.csproj", "{3F4A0DD8-4D47-4B9C-939A-3146E68C84F7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.ApplicationInsights.Tests", "tests\Microsoft.Bot.Builder.ApplicationInsights.Tests\Microsoft.Bot.Builder.ApplicationInsights.Tests.csproj", "{D790A4BB-D8AC-4AAE-B3FE-0CF432CA8031}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.TestBot", "tests\Microsoft.Bot.Builder.TestBot\Microsoft.Bot.Builder.TestBot.csproj", "{C113E0AE-5564-4389-BA39-183A8D574210}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.FunctionalTests", "FunctionalTests\Microsoft.Bot.Builder.FunctionalTests\Microsoft.Bot.Builder.FunctionalTests.csproj", "{B9DDC8CB-8EDF-4D98-913A-22F19E642223}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FunctionalTests", "FunctionalTests", "{8667F820-8ADA-4498-91AE-AE95DEE5227E}"
ProjectSection(SolutionItems) = preProject
FunctionalTests\Directory.Build.props = FunctionalTests\Directory.Build.props
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Testing", "libraries\Microsoft.Bot.Builder.Testing\Microsoft.Bot.Builder.Testing.csproj", "{060F070A-BBFA-490E-BE89-3844C857B771}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Testing.Tests", "tests\Microsoft.Bot.Builder.Testing.Tests\Microsoft.Bot.Builder.Testing.Tests.csproj", "{E4E13301-9193-4106-B0E3-41276B478E7C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.TestBot.Tests", "tests\Microsoft.Bot.Builder.TestBot.Tests\Microsoft.Bot.Builder.TestBot.Tests.csproj", "{76391566-9F22-4994-8B0F-02EFC0E9E228}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialogs.Declarative", "libraries\Microsoft.Bot.Builder.Dialogs.Declarative\Microsoft.Bot.Builder.Dialogs.Declarative.csproj", "{1BC05915-044E-4776-8956-B44BBEFF2F84}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdaptiveExpressions.Tests", "tests\AdaptiveExpressions.Tests\AdaptiveExpressions.Tests.csproj", "{AE3FC7F6-B212-4438-B1D7-C121BB4C15ED}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdaptiveExpressions", "libraries\AdaptiveExpressions\AdaptiveExpressions.csproj", "{8DC1257B-7650-40EB-97A2-C1CBA306DA6A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialogs.Declarative.Tests", "tests\Microsoft.Bot.Builder.Dialogs.Declarative.Tests\Microsoft.Bot.Builder.Dialogs.Declarative.Tests.csproj", "{D5E70443-4BA2-42ED-992A-010268440B08}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialogs.Debugging", "libraries\Microsoft.Bot.Builder.Dialogs.Debugging\Microsoft.Bot.Builder.Dialogs.Debugging.csproj", "{84E3B6A2-42D9-498A-9CD2-1C5F5BE0D526}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.LanguageGeneration", "libraries\Microsoft.Bot.Builder.LanguageGeneration\Microsoft.Bot.Builder.LanguageGeneration.csproj", "{52CDBBA9-E5AF-433C-80F0-5EF3C8B14946}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialogs.Debugging.Tests", "tests\Microsoft.Bot.Builder.Dialogs.Debugging.Tests\Microsoft.Bot.Builder.Dialogs.Debugging.Tests.csproj", "{F59673EA-2D0D-441A-BB85-CD343DE6BE45}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.LanguageGeneration.Tests", "tests\Microsoft.Bot.Builder.LanguageGeneration.Tests\Microsoft.Bot.Builder.LanguageGeneration.Tests.csproj", "{72EE26E2-E8BE-4169-82AD-93E021539C34}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6B6AFE9D-6FA5-4699-B0EB-62335FD431C8}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
@ -118,107 +27,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
tests\tests.schema = tests\tests.schema
EndProjectSection
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.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.Builder.Integration.ApplicationInsights.WebApi", "libraries\integration\Microsoft.Bot.Builder.Integration.ApplicationInsights.WebApi\Microsoft.Bot.Builder.Integration.ApplicationInsights.WebApi.csproj", "{E0223463-97C6-4108-B5A9-AFBA16B9D5CC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Integration.AspNet.WebApi", "libraries\integration\Microsoft.Bot.Builder.Integration.AspNet.WebApi\Microsoft.Bot.Builder.Integration.AspNet.WebApi.csproj", "{EE76BD65-FB01-498F-B053-4E1B2693D85F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Bot.ApplicationInsights.WebApi.Tests", "tests\integration\Microsoft.Bot.ApplicationInsights.WebApi.Tests\Microsoft.Bot.ApplicationInsights.WebApi.Tests.csproj", "{873BCC1F-ED12-424E-93FA-D76F4BA022C2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Bot.Builder.Integration.AspNet.WebApi.Tests", "tests\integration\Microsoft.Bot.Builder.Integration.AspNet.WebApi.Tests\Microsoft.Bot.Builder.Integration.AspNet.WebApi.Tests.csproj", "{2614D290-1345-4A41-BE90-F85F817CEADE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Adapters.Webex", "libraries\Adapters\Microsoft.Bot.Builder.Adapters.Webex\Microsoft.Bot.Builder.Adapters.Webex.csproj", "{F3669415-319E-4C4C-A398-89321589B3A9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Adapters.Webex.Tests", "tests\Adapters\Microsoft.Bot.Builder.Adapters.Webex.Tests\Microsoft.Bot.Builder.Adapters.Webex.Tests.csproj", "{9878B523-BD1E-4285-A875-3CAB4127F7E6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Adapters.Webex.TestBot", "tests\Adapters\Microsoft.Bot.Builder.Adapters.Webex.TestBot\Microsoft.Bot.Builder.Adapters.Webex.TestBot.csproj", "{6E39C647-902F-4CFF-A4E4-194B295E3AF4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialogs.Adaptive.Templates.Tests", "tests\Microsoft.Bot.Builder.Dialogs.Adaptive.Templates.Tests\Microsoft.Bot.Builder.Dialogs.Adaptive.Templates.Tests.csproj", "{EFAA1D3D-3102-4307-A5DD-EAB4DB9B0386}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Streaming", "libraries\Microsoft.Bot.Streaming\Microsoft.Bot.Streaming.csproj", "{4C82FD14-418F-43E4-AC59-3D926B55CEA3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Streaming.Tests", "tests\Microsoft.Bot.Streaming.Tests\Microsoft.Bot.Streaming.Tests.csproj", "{D243AC2D-7823-4177-9D8A-23FDFDA274D2}"
ProjectSection(ProjectDependencies) = postProject
{ADA8AB8B-2066-4193-B8F7-985669B23E00} = {ADA8AB8B-2066-4193-B8F7-985669B23E00}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Adapters.Slack", "libraries\Adapters\Microsoft.Bot.Builder.Adapters.Slack\Microsoft.Bot.Builder.Adapters.Slack.csproj", "{D2D9931A-EBFC-4923-A7BC-EF8BBD76D079}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Adapters.Slack.TestBot", "tests\Adapters\Microsoft.Bot.Builder.Adapters.Slack.TestBot\Microsoft.Bot.Builder.Adapters.Slack.TestBot.csproj", "{170EA6A3-26A6-4A1A-A6F9-44BE5208FA06}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Adapters.Slack.Tests", "tests\Adapters\Microsoft.Bot.Builder.Adapters.Slack.Tests\Microsoft.Bot.Builder.Adapters.Slack.Tests.csproj", "{195FC24B-C4DA-4055-918D-5ABF50FD805B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Adapters.Facebook.Tests", "tests\Adapters\Microsoft.Bot.Builder.Adapters.Facebook.Tests\Microsoft.Bot.Builder.Adapters.Facebook.Tests.csproj", "{9F7B2BDF-973B-4639-B890-357EB967B2ED}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Adapters.Facebook.PrimaryTestBot", "tests\Adapters\Microsoft.Bot.Builder.Adapters.Facebook.PrimaryTestBot\Microsoft.Bot.Builder.Adapters.Facebook.PrimaryTestBot.csproj", "{E372D54C-73AA-41DB-A471-81348AC37F36}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Adapters.Facebook", "libraries\Adapters\Microsoft.Bot.Builder.Adapters.Facebook\Microsoft.Bot.Builder.Adapters.Facebook.csproj", "{C8E31CD2-89D4-4659-9557-43EC9C99D984}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Adapters.Facebook.SecondaryTestBot", "tests\Adapters\Microsoft.Bot.Builder.Adapters.Facebook.SecondaryTestBot\Microsoft.Bot.Builder.Adapters.Facebook.SecondaryTestBot.csproj", "{1D05EFE4-7F25-4D5A-BCEE-1109B9EF25A8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.TestProtocol", "tests\Microsoft.Bot.Builder.TestProtocol\Microsoft.Bot.Builder.TestProtocol.csproj", "{24CCB459-B4F6-484F-8BA4-946A4AB816FA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialogs.Adaptive.Profiling", "tests\Microsoft.Bot.Builder.Dialogs.Adaptive.Profiling\Microsoft.Bot.Builder.Dialogs.Adaptive.Profiling.csproj", "{D9242899-AB3F-46BB-BAB4-386CB8EC535C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Adapters.Twilio.TestBot", "tests\Adapters\Microsoft.Bot.Builder.Adapters.Twilio.TestBot\Microsoft.Bot.Builder.Adapters.Twilio.TestBot.csproj", "{F98A9787-B175-450F-99FC-EC241EB9D581}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.TestBot.NetCore21", "tests\Microsoft.Bot.Builder.TestBot.NetCore21\Microsoft.Bot.Builder.TestBot.NetCore21.csproj", "{99C466C3-1931-4C0E-AA0A-A8D9D140F56E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.TestBot.NetCore21.Tests", "tests\Microsoft.Bot.Builder.TestBot.NetCore21.Tests\Microsoft.Bot.Builder.TestBot.NetCore21.Tests.csproj", "{2FBA2BB7-73C8-45CD-99B7-D65F817C9051}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialogs.Adaptive.Testing", "libraries\Microsoft.Bot.Builder.Dialogs.Adaptive.Testing\Microsoft.Bot.Builder.Dialogs.Adaptive.Testing.csproj", "{D921D320-0450-455F-8DF2-70EDAC5CCE68}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Skills", "Skills", "{54DA838C-8BB8-4038-8BDB-D887C02B2D9A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Parent", "tests\Skills\Parent\Parent.csproj", "{7F8ED2E7-A4BE-4855-BAF2-95657220E419}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Child", "tests\Skills\Child\Child.csproj", "{1958FBA4-BF2D-48D9-A5DB-8915F553EBD3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialogs.Adaptive.Teams", "libraries\Microsoft.Bot.Builder.Dialogs.Adaptive.Teams\Microsoft.Bot.Builder.Dialogs.Adaptive.Teams.csproj", "{23BF6935-D648-4874-91C7-1BF4D6FF64E3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialogs.Adaptive.Teams.Tests", "tests\Microsoft.Bot.Builder.Dialogs.Adaptive.Teams.Tests\Microsoft.Bot.Builder.Dialogs.Adaptive.Teams.Tests.csproj", "{DD5071A9-BAE1-45EF-814C-D071BE63CD47}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SSO", "SSO", "{AA545986-D3E6-406D-8BD5-305B809FB399}"
ProjectSection(SolutionItems) = preProject
tests\Skills\ReadMeForSSOTesting.md = tests\Skills\ReadMeForSSOTesting.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "End2End", "End2End", "{1024840D-8B5A-4E39-8F6D-F7430DD3749E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bot1", "tests\Skills\Bot1\Bot1.csproj", "{2367D12D-2A9B-4F52-8948-78C1EDE9059A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bot2", "tests\Skills\Bot2\Bot2.csproj", "{36D6E1D7-2506-4096-8D76-55C3CCD32DFE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bot3", "tests\Skills\Bot3\Bot3.csproj", "{85463C41-8D08-4A9D-A475-68A83730431C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.AI.Orchestrator", "libraries\Microsoft.Bot.Builder.AI.Orchestrator\Microsoft.Bot.Builder.AI.Orchestrator.csproj", "{19CE18FD-3F99-4994-B88E-7D32F9303547}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.AI.Orchestrator.Tests", "tests\Microsoft.Bot.Builder.AI.Orchestrator.Tests\Microsoft.Bot.Builder.AI.Orchestrator.Tests.csproj", "{F0033458-4C9D-4D66-B58D-F49ABABE8030}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Azure.Blobs", "libraries\Microsoft.Bot.Builder.Azure.Blobs\Microsoft.Bot.Builder.Azure.Blobs.csproj", "{8EDAA935-DAA1-4E00-9596-7E40D8C0F58C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Azure.Queues", "libraries\Microsoft.Bot.Builder.Azure.Queues\Microsoft.Bot.Builder.Azure.Queues.csproj", "{36641924-DE75-4EA9-B139-87F5330D4A09}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Parsers", "Parsers", "{ABB57A05-A284-40BA-A143-0CFEADE2B630}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Parsers.LU", "libraries\Parsers\Microsoft.Bot.Builder.Parsers.LU\Microsoft.Bot.Builder.Parsers.LU.csproj", "{C395D6D2-DF99-4FD9-B5C5-04E8BB866509}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Parsers", "Parsers", "{37BA1593-FE3D-4CB9-A040-6CAB7B2D134A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Bot.Builder.Parsers.LU.Tests", "tests\Parsers\Microsoft.Bot.Builder.Parsers.LU.Tests\Microsoft.Bot.Builder.Parsers.LU.Tests.csproj", "{D7FFAB17-C7BE-490F-A974-584B5E37CF6D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Parsers.LU.Tests", "tests\Parsers\Microsoft.Bot.Builder.Parsers.LU.Tests\Microsoft.Bot.Builder.Parsers.LU.Tests.csproj", "{D7FFAB17-C7BE-490F-A974-584B5E37CF6D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -228,630 +43,6 @@ Global
Release-Windows|Any CPU = Release-Windows|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3CF175CF-1AF4-4109-96CB-221684DCED7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3CF175CF-1AF4-4109-96CB-221684DCED7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3CF175CF-1AF4-4109-96CB-221684DCED7D}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{3CF175CF-1AF4-4109-96CB-221684DCED7D}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{3CF175CF-1AF4-4109-96CB-221684DCED7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3CF175CF-1AF4-4109-96CB-221684DCED7D}.Release|Any CPU.Build.0 = Release|Any CPU
{3CF175CF-1AF4-4109-96CB-221684DCED7D}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{3CF175CF-1AF4-4109-96CB-221684DCED7D}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{CA008713-655E-46DA-BBDF-1EF6B8CE7DCA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CA008713-655E-46DA-BBDF-1EF6B8CE7DCA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CA008713-655E-46DA-BBDF-1EF6B8CE7DCA}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{CA008713-655E-46DA-BBDF-1EF6B8CE7DCA}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{CA008713-655E-46DA-BBDF-1EF6B8CE7DCA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CA008713-655E-46DA-BBDF-1EF6B8CE7DCA}.Release|Any CPU.Build.0 = Release|Any CPU
{CA008713-655E-46DA-BBDF-1EF6B8CE7DCA}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{CA008713-655E-46DA-BBDF-1EF6B8CE7DCA}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{E325A0E2-716A-49E0-9767-5087CF05727C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E325A0E2-716A-49E0-9767-5087CF05727C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E325A0E2-716A-49E0-9767-5087CF05727C}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{E325A0E2-716A-49E0-9767-5087CF05727C}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{E325A0E2-716A-49E0-9767-5087CF05727C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E325A0E2-716A-49E0-9767-5087CF05727C}.Release|Any CPU.Build.0 = Release|Any CPU
{E325A0E2-716A-49E0-9767-5087CF05727C}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{E325A0E2-716A-49E0-9767-5087CF05727C}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{B2C5EED9-21B2-4763-AB92-C1995B76DA3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B2C5EED9-21B2-4763-AB92-C1995B76DA3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2C5EED9-21B2-4763-AB92-C1995B76DA3A}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{B2C5EED9-21B2-4763-AB92-C1995B76DA3A}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{B2C5EED9-21B2-4763-AB92-C1995B76DA3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B2C5EED9-21B2-4763-AB92-C1995B76DA3A}.Release|Any CPU.Build.0 = Release|Any CPU
{B2C5EED9-21B2-4763-AB92-C1995B76DA3A}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{B2C5EED9-21B2-4763-AB92-C1995B76DA3A}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{9BDF7020-A19F-4C6C-B329-E4BFEFF6DE6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9BDF7020-A19F-4C6C-B329-E4BFEFF6DE6B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9BDF7020-A19F-4C6C-B329-E4BFEFF6DE6B}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{9BDF7020-A19F-4C6C-B329-E4BFEFF6DE6B}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{9BDF7020-A19F-4C6C-B329-E4BFEFF6DE6B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9BDF7020-A19F-4C6C-B329-E4BFEFF6DE6B}.Release|Any CPU.Build.0 = Release|Any CPU
{9BDF7020-A19F-4C6C-B329-E4BFEFF6DE6B}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{9BDF7020-A19F-4C6C-B329-E4BFEFF6DE6B}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{6462DA5D-27DC-4CD5-9467-5EFB998FD838}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6462DA5D-27DC-4CD5-9467-5EFB998FD838}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6462DA5D-27DC-4CD5-9467-5EFB998FD838}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{6462DA5D-27DC-4CD5-9467-5EFB998FD838}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{6462DA5D-27DC-4CD5-9467-5EFB998FD838}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6462DA5D-27DC-4CD5-9467-5EFB998FD838}.Release|Any CPU.Build.0 = Release|Any CPU
{6462DA5D-27DC-4CD5-9467-5EFB998FD838}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{6462DA5D-27DC-4CD5-9467-5EFB998FD838}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{C1F54CDC-AD1D-45BB-8F7D-F49E411AFAF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C1F54CDC-AD1D-45BB-8F7D-F49E411AFAF1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C1F54CDC-AD1D-45BB-8F7D-F49E411AFAF1}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{C1F54CDC-AD1D-45BB-8F7D-F49E411AFAF1}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{C1F54CDC-AD1D-45BB-8F7D-F49E411AFAF1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C1F54CDC-AD1D-45BB-8F7D-F49E411AFAF1}.Release|Any CPU.Build.0 = Release|Any CPU
{C1F54CDC-AD1D-45BB-8F7D-F49E411AFAF1}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{C1F54CDC-AD1D-45BB-8F7D-F49E411AFAF1}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{ADA8AB8B-2066-4193-B8F7-985669B23E00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ADA8AB8B-2066-4193-B8F7-985669B23E00}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ADA8AB8B-2066-4193-B8F7-985669B23E00}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{ADA8AB8B-2066-4193-B8F7-985669B23E00}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{ADA8AB8B-2066-4193-B8F7-985669B23E00}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ADA8AB8B-2066-4193-B8F7-985669B23E00}.Release|Any CPU.Build.0 = Release|Any CPU
{ADA8AB8B-2066-4193-B8F7-985669B23E00}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{ADA8AB8B-2066-4193-B8F7-985669B23E00}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{BF414C86-DB3B-4022-9B29-DCE8AA954C12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BF414C86-DB3B-4022-9B29-DCE8AA954C12}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BF414C86-DB3B-4022-9B29-DCE8AA954C12}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{BF414C86-DB3B-4022-9B29-DCE8AA954C12}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{BF414C86-DB3B-4022-9B29-DCE8AA954C12}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BF414C86-DB3B-4022-9B29-DCE8AA954C12}.Release|Any CPU.Build.0 = Release|Any CPU
{BF414C86-DB3B-4022-9B29-DCE8AA954C12}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{BF414C86-DB3B-4022-9B29-DCE8AA954C12}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{67AA3C00-E2C5-4D13-BA5E-72EB0E5B8DAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{67AA3C00-E2C5-4D13-BA5E-72EB0E5B8DAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{67AA3C00-E2C5-4D13-BA5E-72EB0E5B8DAA}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{67AA3C00-E2C5-4D13-BA5E-72EB0E5B8DAA}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{67AA3C00-E2C5-4D13-BA5E-72EB0E5B8DAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{67AA3C00-E2C5-4D13-BA5E-72EB0E5B8DAA}.Release|Any CPU.Build.0 = Release|Any CPU
{67AA3C00-E2C5-4D13-BA5E-72EB0E5B8DAA}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{67AA3C00-E2C5-4D13-BA5E-72EB0E5B8DAA}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{7BCEBDC1-D57F-4717-9B15-4FACD5473489}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7BCEBDC1-D57F-4717-9B15-4FACD5473489}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7BCEBDC1-D57F-4717-9B15-4FACD5473489}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{7BCEBDC1-D57F-4717-9B15-4FACD5473489}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{7BCEBDC1-D57F-4717-9B15-4FACD5473489}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7BCEBDC1-D57F-4717-9B15-4FACD5473489}.Release|Any CPU.Build.0 = Release|Any CPU
{7BCEBDC1-D57F-4717-9B15-4FACD5473489}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{7BCEBDC1-D57F-4717-9B15-4FACD5473489}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{053BD8B8-B5C1-4C45-81D4-C9BA8D5B3CE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{053BD8B8-B5C1-4C45-81D4-C9BA8D5B3CE2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{053BD8B8-B5C1-4C45-81D4-C9BA8D5B3CE2}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{053BD8B8-B5C1-4C45-81D4-C9BA8D5B3CE2}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{053BD8B8-B5C1-4C45-81D4-C9BA8D5B3CE2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{053BD8B8-B5C1-4C45-81D4-C9BA8D5B3CE2}.Release|Any CPU.Build.0 = Release|Any CPU
{053BD8B8-B5C1-4C45-81D4-C9BA8D5B3CE2}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{053BD8B8-B5C1-4C45-81D4-C9BA8D5B3CE2}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{EED5F0D3-6F00-4ED1-9108-5ADCB10A3734}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EED5F0D3-6F00-4ED1-9108-5ADCB10A3734}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EED5F0D3-6F00-4ED1-9108-5ADCB10A3734}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{EED5F0D3-6F00-4ED1-9108-5ADCB10A3734}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{EED5F0D3-6F00-4ED1-9108-5ADCB10A3734}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EED5F0D3-6F00-4ED1-9108-5ADCB10A3734}.Release|Any CPU.Build.0 = Release|Any CPU
{EED5F0D3-6F00-4ED1-9108-5ADCB10A3734}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{EED5F0D3-6F00-4ED1-9108-5ADCB10A3734}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{C93F6192-0123-4121-AD92-374A71E4B0F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C93F6192-0123-4121-AD92-374A71E4B0F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C93F6192-0123-4121-AD92-374A71E4B0F3}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{C93F6192-0123-4121-AD92-374A71E4B0F3}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{C93F6192-0123-4121-AD92-374A71E4B0F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C93F6192-0123-4121-AD92-374A71E4B0F3}.Release|Any CPU.Build.0 = Release|Any CPU
{C93F6192-0123-4121-AD92-374A71E4B0F3}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{C93F6192-0123-4121-AD92-374A71E4B0F3}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{A4184239-F13F-4A09-B2D3-0B9532609248}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A4184239-F13F-4A09-B2D3-0B9532609248}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A4184239-F13F-4A09-B2D3-0B9532609248}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{A4184239-F13F-4A09-B2D3-0B9532609248}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{A4184239-F13F-4A09-B2D3-0B9532609248}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A4184239-F13F-4A09-B2D3-0B9532609248}.Release|Any CPU.Build.0 = Release|Any CPU
{A4184239-F13F-4A09-B2D3-0B9532609248}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{A4184239-F13F-4A09-B2D3-0B9532609248}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{D9B2EF5D-0515-460F-948A-2BB70C8DCF62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D9B2EF5D-0515-460F-948A-2BB70C8DCF62}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D9B2EF5D-0515-460F-948A-2BB70C8DCF62}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{D9B2EF5D-0515-460F-948A-2BB70C8DCF62}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{D9B2EF5D-0515-460F-948A-2BB70C8DCF62}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D9B2EF5D-0515-460F-948A-2BB70C8DCF62}.Release|Any CPU.Build.0 = Release|Any CPU
{D9B2EF5D-0515-460F-948A-2BB70C8DCF62}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{D9B2EF5D-0515-460F-948A-2BB70C8DCF62}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{0F639EB4-FB64-4909-8A10-FB93E7BE3AFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0F639EB4-FB64-4909-8A10-FB93E7BE3AFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0F639EB4-FB64-4909-8A10-FB93E7BE3AFB}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{0F639EB4-FB64-4909-8A10-FB93E7BE3AFB}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{0F639EB4-FB64-4909-8A10-FB93E7BE3AFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0F639EB4-FB64-4909-8A10-FB93E7BE3AFB}.Release|Any CPU.Build.0 = Release|Any CPU
{0F639EB4-FB64-4909-8A10-FB93E7BE3AFB}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{0F639EB4-FB64-4909-8A10-FB93E7BE3AFB}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{2F77CA1D-E6F0-4DEA-96BB-8A039F4D0FF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2F77CA1D-E6F0-4DEA-96BB-8A039F4D0FF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2F77CA1D-E6F0-4DEA-96BB-8A039F4D0FF8}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{2F77CA1D-E6F0-4DEA-96BB-8A039F4D0FF8}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{2F77CA1D-E6F0-4DEA-96BB-8A039F4D0FF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2F77CA1D-E6F0-4DEA-96BB-8A039F4D0FF8}.Release|Any CPU.Build.0 = Release|Any CPU
{2F77CA1D-E6F0-4DEA-96BB-8A039F4D0FF8}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{2F77CA1D-E6F0-4DEA-96BB-8A039F4D0FF8}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{62C4DC83-3B07-45D7-856E-224FFB368E5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{62C4DC83-3B07-45D7-856E-224FFB368E5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{62C4DC83-3B07-45D7-856E-224FFB368E5F}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{62C4DC83-3B07-45D7-856E-224FFB368E5F}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{62C4DC83-3B07-45D7-856E-224FFB368E5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{62C4DC83-3B07-45D7-856E-224FFB368E5F}.Release|Any CPU.Build.0 = Release|Any CPU
{62C4DC83-3B07-45D7-856E-224FFB368E5F}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{62C4DC83-3B07-45D7-856E-224FFB368E5F}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{35F9B8A8-1974-4795-930B-5E4980EE85E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{35F9B8A8-1974-4795-930B-5E4980EE85E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{35F9B8A8-1974-4795-930B-5E4980EE85E5}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{35F9B8A8-1974-4795-930B-5E4980EE85E5}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{35F9B8A8-1974-4795-930B-5E4980EE85E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{35F9B8A8-1974-4795-930B-5E4980EE85E5}.Release|Any CPU.Build.0 = Release|Any CPU
{35F9B8A8-1974-4795-930B-5E4980EE85E5}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{35F9B8A8-1974-4795-930B-5E4980EE85E5}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{71698D71-4C6F-40D5-8BDE-2587514CA21C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{71698D71-4C6F-40D5-8BDE-2587514CA21C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{71698D71-4C6F-40D5-8BDE-2587514CA21C}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{71698D71-4C6F-40D5-8BDE-2587514CA21C}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{71698D71-4C6F-40D5-8BDE-2587514CA21C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{71698D71-4C6F-40D5-8BDE-2587514CA21C}.Release|Any CPU.Build.0 = Release|Any CPU
{71698D71-4C6F-40D5-8BDE-2587514CA21C}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{71698D71-4C6F-40D5-8BDE-2587514CA21C}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{0B8ABFDB-F9CF-4EC6-988E-9C32D9E01C26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0B8ABFDB-F9CF-4EC6-988E-9C32D9E01C26}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0B8ABFDB-F9CF-4EC6-988E-9C32D9E01C26}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{0B8ABFDB-F9CF-4EC6-988E-9C32D9E01C26}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{0B8ABFDB-F9CF-4EC6-988E-9C32D9E01C26}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0B8ABFDB-F9CF-4EC6-988E-9C32D9E01C26}.Release|Any CPU.Build.0 = Release|Any CPU
{0B8ABFDB-F9CF-4EC6-988E-9C32D9E01C26}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{0B8ABFDB-F9CF-4EC6-988E-9C32D9E01C26}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{1B7920E6-5262-4054-B72D-3A8DBBA057D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1B7920E6-5262-4054-B72D-3A8DBBA057D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1B7920E6-5262-4054-B72D-3A8DBBA057D2}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{1B7920E6-5262-4054-B72D-3A8DBBA057D2}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{1B7920E6-5262-4054-B72D-3A8DBBA057D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1B7920E6-5262-4054-B72D-3A8DBBA057D2}.Release|Any CPU.Build.0 = Release|Any CPU
{1B7920E6-5262-4054-B72D-3A8DBBA057D2}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{1B7920E6-5262-4054-B72D-3A8DBBA057D2}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{8FC920C6-E895-4A17-AB2F-452FAAA36CC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8FC920C6-E895-4A17-AB2F-452FAAA36CC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8FC920C6-E895-4A17-AB2F-452FAAA36CC8}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{8FC920C6-E895-4A17-AB2F-452FAAA36CC8}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{8FC920C6-E895-4A17-AB2F-452FAAA36CC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8FC920C6-E895-4A17-AB2F-452FAAA36CC8}.Release|Any CPU.Build.0 = Release|Any CPU
{8FC920C6-E895-4A17-AB2F-452FAAA36CC8}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{8FC920C6-E895-4A17-AB2F-452FAAA36CC8}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{B609DB2C-5C1F-46D1-A0FA-A0FF9216899A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B609DB2C-5C1F-46D1-A0FA-A0FF9216899A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B609DB2C-5C1F-46D1-A0FA-A0FF9216899A}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{B609DB2C-5C1F-46D1-A0FA-A0FF9216899A}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{B609DB2C-5C1F-46D1-A0FA-A0FF9216899A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B609DB2C-5C1F-46D1-A0FA-A0FF9216899A}.Release|Any CPU.Build.0 = Release|Any CPU
{B609DB2C-5C1F-46D1-A0FA-A0FF9216899A}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{B609DB2C-5C1F-46D1-A0FA-A0FF9216899A}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{3F4A0DD8-4D47-4B9C-939A-3146E68C84F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3F4A0DD8-4D47-4B9C-939A-3146E68C84F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F4A0DD8-4D47-4B9C-939A-3146E68C84F7}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{3F4A0DD8-4D47-4B9C-939A-3146E68C84F7}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{3F4A0DD8-4D47-4B9C-939A-3146E68C84F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F4A0DD8-4D47-4B9C-939A-3146E68C84F7}.Release|Any CPU.Build.0 = Release|Any CPU
{3F4A0DD8-4D47-4B9C-939A-3146E68C84F7}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{3F4A0DD8-4D47-4B9C-939A-3146E68C84F7}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{D790A4BB-D8AC-4AAE-B3FE-0CF432CA8031}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D790A4BB-D8AC-4AAE-B3FE-0CF432CA8031}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D790A4BB-D8AC-4AAE-B3FE-0CF432CA8031}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{D790A4BB-D8AC-4AAE-B3FE-0CF432CA8031}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{D790A4BB-D8AC-4AAE-B3FE-0CF432CA8031}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D790A4BB-D8AC-4AAE-B3FE-0CF432CA8031}.Release|Any CPU.Build.0 = Release|Any CPU
{D790A4BB-D8AC-4AAE-B3FE-0CF432CA8031}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{D790A4BB-D8AC-4AAE-B3FE-0CF432CA8031}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{C113E0AE-5564-4389-BA39-183A8D574210}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C113E0AE-5564-4389-BA39-183A8D574210}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C113E0AE-5564-4389-BA39-183A8D574210}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{C113E0AE-5564-4389-BA39-183A8D574210}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{C113E0AE-5564-4389-BA39-183A8D574210}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C113E0AE-5564-4389-BA39-183A8D574210}.Release|Any CPU.Build.0 = Release|Any CPU
{C113E0AE-5564-4389-BA39-183A8D574210}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{C113E0AE-5564-4389-BA39-183A8D574210}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{B9DDC8CB-8EDF-4D98-913A-22F19E642223}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B9DDC8CB-8EDF-4D98-913A-22F19E642223}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B9DDC8CB-8EDF-4D98-913A-22F19E642223}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{B9DDC8CB-8EDF-4D98-913A-22F19E642223}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{B9DDC8CB-8EDF-4D98-913A-22F19E642223}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B9DDC8CB-8EDF-4D98-913A-22F19E642223}.Release|Any CPU.Build.0 = Release|Any CPU
{B9DDC8CB-8EDF-4D98-913A-22F19E642223}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{B9DDC8CB-8EDF-4D98-913A-22F19E642223}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{060F070A-BBFA-490E-BE89-3844C857B771}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{060F070A-BBFA-490E-BE89-3844C857B771}.Debug|Any CPU.Build.0 = Debug|Any CPU
{060F070A-BBFA-490E-BE89-3844C857B771}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{060F070A-BBFA-490E-BE89-3844C857B771}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{060F070A-BBFA-490E-BE89-3844C857B771}.Release|Any CPU.ActiveCfg = Release|Any CPU
{060F070A-BBFA-490E-BE89-3844C857B771}.Release|Any CPU.Build.0 = Release|Any CPU
{060F070A-BBFA-490E-BE89-3844C857B771}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{060F070A-BBFA-490E-BE89-3844C857B771}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{E4E13301-9193-4106-B0E3-41276B478E7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E4E13301-9193-4106-B0E3-41276B478E7C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E4E13301-9193-4106-B0E3-41276B478E7C}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{E4E13301-9193-4106-B0E3-41276B478E7C}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{E4E13301-9193-4106-B0E3-41276B478E7C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E4E13301-9193-4106-B0E3-41276B478E7C}.Release|Any CPU.Build.0 = Release|Any CPU
{E4E13301-9193-4106-B0E3-41276B478E7C}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{E4E13301-9193-4106-B0E3-41276B478E7C}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{76391566-9F22-4994-8B0F-02EFC0E9E228}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{76391566-9F22-4994-8B0F-02EFC0E9E228}.Debug|Any CPU.Build.0 = Debug|Any CPU
{76391566-9F22-4994-8B0F-02EFC0E9E228}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{76391566-9F22-4994-8B0F-02EFC0E9E228}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{76391566-9F22-4994-8B0F-02EFC0E9E228}.Release|Any CPU.ActiveCfg = Release|Any CPU
{76391566-9F22-4994-8B0F-02EFC0E9E228}.Release|Any CPU.Build.0 = Release|Any CPU
{76391566-9F22-4994-8B0F-02EFC0E9E228}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{76391566-9F22-4994-8B0F-02EFC0E9E228}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{1BC05915-044E-4776-8956-B44BBEFF2F84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1BC05915-044E-4776-8956-B44BBEFF2F84}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1BC05915-044E-4776-8956-B44BBEFF2F84}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{1BC05915-044E-4776-8956-B44BBEFF2F84}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{1BC05915-044E-4776-8956-B44BBEFF2F84}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1BC05915-044E-4776-8956-B44BBEFF2F84}.Release|Any CPU.Build.0 = Release|Any CPU
{1BC05915-044E-4776-8956-B44BBEFF2F84}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{1BC05915-044E-4776-8956-B44BBEFF2F84}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{AE3FC7F6-B212-4438-B1D7-C121BB4C15ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AE3FC7F6-B212-4438-B1D7-C121BB4C15ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AE3FC7F6-B212-4438-B1D7-C121BB4C15ED}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{AE3FC7F6-B212-4438-B1D7-C121BB4C15ED}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{AE3FC7F6-B212-4438-B1D7-C121BB4C15ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AE3FC7F6-B212-4438-B1D7-C121BB4C15ED}.Release|Any CPU.Build.0 = Release|Any CPU
{AE3FC7F6-B212-4438-B1D7-C121BB4C15ED}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{AE3FC7F6-B212-4438-B1D7-C121BB4C15ED}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{8DC1257B-7650-40EB-97A2-C1CBA306DA6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8DC1257B-7650-40EB-97A2-C1CBA306DA6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8DC1257B-7650-40EB-97A2-C1CBA306DA6A}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{8DC1257B-7650-40EB-97A2-C1CBA306DA6A}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{8DC1257B-7650-40EB-97A2-C1CBA306DA6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8DC1257B-7650-40EB-97A2-C1CBA306DA6A}.Release|Any CPU.Build.0 = Release|Any CPU
{8DC1257B-7650-40EB-97A2-C1CBA306DA6A}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{8DC1257B-7650-40EB-97A2-C1CBA306DA6A}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{D5E70443-4BA2-42ED-992A-010268440B08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D5E70443-4BA2-42ED-992A-010268440B08}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D5E70443-4BA2-42ED-992A-010268440B08}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{D5E70443-4BA2-42ED-992A-010268440B08}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{D5E70443-4BA2-42ED-992A-010268440B08}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D5E70443-4BA2-42ED-992A-010268440B08}.Release|Any CPU.Build.0 = Release|Any CPU
{D5E70443-4BA2-42ED-992A-010268440B08}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{D5E70443-4BA2-42ED-992A-010268440B08}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{84E3B6A2-42D9-498A-9CD2-1C5F5BE0D526}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{84E3B6A2-42D9-498A-9CD2-1C5F5BE0D526}.Debug|Any CPU.Build.0 = Debug|Any CPU
{84E3B6A2-42D9-498A-9CD2-1C5F5BE0D526}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{84E3B6A2-42D9-498A-9CD2-1C5F5BE0D526}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{84E3B6A2-42D9-498A-9CD2-1C5F5BE0D526}.Release|Any CPU.ActiveCfg = Release|Any CPU
{84E3B6A2-42D9-498A-9CD2-1C5F5BE0D526}.Release|Any CPU.Build.0 = Release|Any CPU
{84E3B6A2-42D9-498A-9CD2-1C5F5BE0D526}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{84E3B6A2-42D9-498A-9CD2-1C5F5BE0D526}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{52CDBBA9-E5AF-433C-80F0-5EF3C8B14946}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{52CDBBA9-E5AF-433C-80F0-5EF3C8B14946}.Debug|Any CPU.Build.0 = Debug|Any CPU
{52CDBBA9-E5AF-433C-80F0-5EF3C8B14946}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{52CDBBA9-E5AF-433C-80F0-5EF3C8B14946}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{52CDBBA9-E5AF-433C-80F0-5EF3C8B14946}.Release|Any CPU.ActiveCfg = Release|Any CPU
{52CDBBA9-E5AF-433C-80F0-5EF3C8B14946}.Release|Any CPU.Build.0 = Release|Any CPU
{52CDBBA9-E5AF-433C-80F0-5EF3C8B14946}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{52CDBBA9-E5AF-433C-80F0-5EF3C8B14946}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{F59673EA-2D0D-441A-BB85-CD343DE6BE45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F59673EA-2D0D-441A-BB85-CD343DE6BE45}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F59673EA-2D0D-441A-BB85-CD343DE6BE45}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{F59673EA-2D0D-441A-BB85-CD343DE6BE45}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{F59673EA-2D0D-441A-BB85-CD343DE6BE45}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F59673EA-2D0D-441A-BB85-CD343DE6BE45}.Release|Any CPU.Build.0 = Release|Any CPU
{F59673EA-2D0D-441A-BB85-CD343DE6BE45}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{F59673EA-2D0D-441A-BB85-CD343DE6BE45}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{72EE26E2-E8BE-4169-82AD-93E021539C34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{72EE26E2-E8BE-4169-82AD-93E021539C34}.Debug|Any CPU.Build.0 = Debug|Any CPU
{72EE26E2-E8BE-4169-82AD-93E021539C34}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{72EE26E2-E8BE-4169-82AD-93E021539C34}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{72EE26E2-E8BE-4169-82AD-93E021539C34}.Release|Any CPU.ActiveCfg = Release|Any CPU
{72EE26E2-E8BE-4169-82AD-93E021539C34}.Release|Any CPU.Build.0 = Release|Any CPU
{72EE26E2-E8BE-4169-82AD-93E021539C34}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{72EE26E2-E8BE-4169-82AD-93E021539C34}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{2454BBCD-77BC-4E3D-B5A6-3562BED898D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2454BBCD-77BC-4E3D-B5A6-3562BED898D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2454BBCD-77BC-4E3D-B5A6-3562BED898D6}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{2454BBCD-77BC-4E3D-B5A6-3562BED898D6}.Debug-Windows|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
{2454BBCD-77BC-4E3D-B5A6-3562BED898D6}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{2454BBCD-77BC-4E3D-B5A6-3562BED898D6}.Release-Windows|Any CPU.Build.0 = Release|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}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{685271A8-6C69-46E4-9B11-89AF9761CE0A}.Debug-Windows|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
{685271A8-6C69-46E4-9B11-89AF9761CE0A}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{685271A8-6C69-46E4-9B11-89AF9761CE0A}.Release-Windows|Any CPU.Build.0 = Release|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}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{474C57B1-C9FC-4B71-A92B-B25BA27FAFA7}.Debug-Windows|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
{474C57B1-C9FC-4B71-A92B-B25BA27FAFA7}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{474C57B1-C9FC-4B71-A92B-B25BA27FAFA7}.Release-Windows|Any CPU.Build.0 = Release|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}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{1D1AD39B-EBCF-4960-930E-84246DEF6AAE}.Debug-Windows|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
{1D1AD39B-EBCF-4960-930E-84246DEF6AAE}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{1D1AD39B-EBCF-4960-930E-84246DEF6AAE}.Release-Windows|Any CPU.Build.0 = Release|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}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{6B51F54F-86E8-4FBC-8FDD-3C386E97D0E1}.Debug-Windows|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
{6B51F54F-86E8-4FBC-8FDD-3C386E97D0E1}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{6B51F54F-86E8-4FBC-8FDD-3C386E97D0E1}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{E0223463-97C6-4108-B5A9-AFBA16B9D5CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E0223463-97C6-4108-B5A9-AFBA16B9D5CC}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{E0223463-97C6-4108-B5A9-AFBA16B9D5CC}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{E0223463-97C6-4108-B5A9-AFBA16B9D5CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E0223463-97C6-4108-B5A9-AFBA16B9D5CC}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{E0223463-97C6-4108-B5A9-AFBA16B9D5CC}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{EE76BD65-FB01-498F-B053-4E1B2693D85F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EE76BD65-FB01-498F-B053-4E1B2693D85F}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{EE76BD65-FB01-498F-B053-4E1B2693D85F}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{EE76BD65-FB01-498F-B053-4E1B2693D85F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EE76BD65-FB01-498F-B053-4E1B2693D85F}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{EE76BD65-FB01-498F-B053-4E1B2693D85F}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{873BCC1F-ED12-424E-93FA-D76F4BA022C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{873BCC1F-ED12-424E-93FA-D76F4BA022C2}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{873BCC1F-ED12-424E-93FA-D76F4BA022C2}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{873BCC1F-ED12-424E-93FA-D76F4BA022C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{873BCC1F-ED12-424E-93FA-D76F4BA022C2}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{873BCC1F-ED12-424E-93FA-D76F4BA022C2}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{2614D290-1345-4A41-BE90-F85F817CEADE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2614D290-1345-4A41-BE90-F85F817CEADE}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{2614D290-1345-4A41-BE90-F85F817CEADE}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{2614D290-1345-4A41-BE90-F85F817CEADE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2614D290-1345-4A41-BE90-F85F817CEADE}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{2614D290-1345-4A41-BE90-F85F817CEADE}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{F3669415-319E-4C4C-A398-89321589B3A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F3669415-319E-4C4C-A398-89321589B3A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F3669415-319E-4C4C-A398-89321589B3A9}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{F3669415-319E-4C4C-A398-89321589B3A9}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{F3669415-319E-4C4C-A398-89321589B3A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F3669415-319E-4C4C-A398-89321589B3A9}.Release|Any CPU.Build.0 = Release|Any CPU
{F3669415-319E-4C4C-A398-89321589B3A9}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{F3669415-319E-4C4C-A398-89321589B3A9}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{9878B523-BD1E-4285-A875-3CAB4127F7E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9878B523-BD1E-4285-A875-3CAB4127F7E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9878B523-BD1E-4285-A875-3CAB4127F7E6}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{9878B523-BD1E-4285-A875-3CAB4127F7E6}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{9878B523-BD1E-4285-A875-3CAB4127F7E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9878B523-BD1E-4285-A875-3CAB4127F7E6}.Release|Any CPU.Build.0 = Release|Any CPU
{9878B523-BD1E-4285-A875-3CAB4127F7E6}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{9878B523-BD1E-4285-A875-3CAB4127F7E6}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{6E39C647-902F-4CFF-A4E4-194B295E3AF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6E39C647-902F-4CFF-A4E4-194B295E3AF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6E39C647-902F-4CFF-A4E4-194B295E3AF4}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{6E39C647-902F-4CFF-A4E4-194B295E3AF4}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{6E39C647-902F-4CFF-A4E4-194B295E3AF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6E39C647-902F-4CFF-A4E4-194B295E3AF4}.Release|Any CPU.Build.0 = Release|Any CPU
{6E39C647-902F-4CFF-A4E4-194B295E3AF4}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{6E39C647-902F-4CFF-A4E4-194B295E3AF4}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{EFAA1D3D-3102-4307-A5DD-EAB4DB9B0386}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EFAA1D3D-3102-4307-A5DD-EAB4DB9B0386}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EFAA1D3D-3102-4307-A5DD-EAB4DB9B0386}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{EFAA1D3D-3102-4307-A5DD-EAB4DB9B0386}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{EFAA1D3D-3102-4307-A5DD-EAB4DB9B0386}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EFAA1D3D-3102-4307-A5DD-EAB4DB9B0386}.Release|Any CPU.Build.0 = Release|Any CPU
{EFAA1D3D-3102-4307-A5DD-EAB4DB9B0386}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{EFAA1D3D-3102-4307-A5DD-EAB4DB9B0386}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{4C82FD14-418F-43E4-AC59-3D926B55CEA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4C82FD14-418F-43E4-AC59-3D926B55CEA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4C82FD14-418F-43E4-AC59-3D926B55CEA3}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{4C82FD14-418F-43E4-AC59-3D926B55CEA3}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{4C82FD14-418F-43E4-AC59-3D926B55CEA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4C82FD14-418F-43E4-AC59-3D926B55CEA3}.Release|Any CPU.Build.0 = Release|Any CPU
{4C82FD14-418F-43E4-AC59-3D926B55CEA3}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{4C82FD14-418F-43E4-AC59-3D926B55CEA3}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{D243AC2D-7823-4177-9D8A-23FDFDA274D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D243AC2D-7823-4177-9D8A-23FDFDA274D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D243AC2D-7823-4177-9D8A-23FDFDA274D2}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{D243AC2D-7823-4177-9D8A-23FDFDA274D2}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{D243AC2D-7823-4177-9D8A-23FDFDA274D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D243AC2D-7823-4177-9D8A-23FDFDA274D2}.Release|Any CPU.Build.0 = Release|Any CPU
{D243AC2D-7823-4177-9D8A-23FDFDA274D2}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{D243AC2D-7823-4177-9D8A-23FDFDA274D2}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{D2D9931A-EBFC-4923-A7BC-EF8BBD76D079}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D2D9931A-EBFC-4923-A7BC-EF8BBD76D079}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D2D9931A-EBFC-4923-A7BC-EF8BBD76D079}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{D2D9931A-EBFC-4923-A7BC-EF8BBD76D079}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{D2D9931A-EBFC-4923-A7BC-EF8BBD76D079}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D2D9931A-EBFC-4923-A7BC-EF8BBD76D079}.Release|Any CPU.Build.0 = Release|Any CPU
{D2D9931A-EBFC-4923-A7BC-EF8BBD76D079}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{D2D9931A-EBFC-4923-A7BC-EF8BBD76D079}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{170EA6A3-26A6-4A1A-A6F9-44BE5208FA06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{170EA6A3-26A6-4A1A-A6F9-44BE5208FA06}.Debug|Any CPU.Build.0 = Debug|Any CPU
{170EA6A3-26A6-4A1A-A6F9-44BE5208FA06}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{170EA6A3-26A6-4A1A-A6F9-44BE5208FA06}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{170EA6A3-26A6-4A1A-A6F9-44BE5208FA06}.Release|Any CPU.ActiveCfg = Release|Any CPU
{170EA6A3-26A6-4A1A-A6F9-44BE5208FA06}.Release|Any CPU.Build.0 = Release|Any CPU
{170EA6A3-26A6-4A1A-A6F9-44BE5208FA06}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{170EA6A3-26A6-4A1A-A6F9-44BE5208FA06}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{195FC24B-C4DA-4055-918D-5ABF50FD805B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{195FC24B-C4DA-4055-918D-5ABF50FD805B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{195FC24B-C4DA-4055-918D-5ABF50FD805B}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{195FC24B-C4DA-4055-918D-5ABF50FD805B}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{195FC24B-C4DA-4055-918D-5ABF50FD805B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{195FC24B-C4DA-4055-918D-5ABF50FD805B}.Release|Any CPU.Build.0 = Release|Any CPU
{195FC24B-C4DA-4055-918D-5ABF50FD805B}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{195FC24B-C4DA-4055-918D-5ABF50FD805B}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{9F7B2BDF-973B-4639-B890-357EB967B2ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9F7B2BDF-973B-4639-B890-357EB967B2ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9F7B2BDF-973B-4639-B890-357EB967B2ED}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{9F7B2BDF-973B-4639-B890-357EB967B2ED}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{9F7B2BDF-973B-4639-B890-357EB967B2ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9F7B2BDF-973B-4639-B890-357EB967B2ED}.Release|Any CPU.Build.0 = Release|Any CPU
{9F7B2BDF-973B-4639-B890-357EB967B2ED}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{9F7B2BDF-973B-4639-B890-357EB967B2ED}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{E372D54C-73AA-41DB-A471-81348AC37F36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E372D54C-73AA-41DB-A471-81348AC37F36}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E372D54C-73AA-41DB-A471-81348AC37F36}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{E372D54C-73AA-41DB-A471-81348AC37F36}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{E372D54C-73AA-41DB-A471-81348AC37F36}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E372D54C-73AA-41DB-A471-81348AC37F36}.Release|Any CPU.Build.0 = Release|Any CPU
{E372D54C-73AA-41DB-A471-81348AC37F36}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{E372D54C-73AA-41DB-A471-81348AC37F36}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{C8E31CD2-89D4-4659-9557-43EC9C99D984}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C8E31CD2-89D4-4659-9557-43EC9C99D984}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C8E31CD2-89D4-4659-9557-43EC9C99D984}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{C8E31CD2-89D4-4659-9557-43EC9C99D984}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{C8E31CD2-89D4-4659-9557-43EC9C99D984}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C8E31CD2-89D4-4659-9557-43EC9C99D984}.Release|Any CPU.Build.0 = Release|Any CPU
{C8E31CD2-89D4-4659-9557-43EC9C99D984}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{C8E31CD2-89D4-4659-9557-43EC9C99D984}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{1D05EFE4-7F25-4D5A-BCEE-1109B9EF25A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1D05EFE4-7F25-4D5A-BCEE-1109B9EF25A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1D05EFE4-7F25-4D5A-BCEE-1109B9EF25A8}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{1D05EFE4-7F25-4D5A-BCEE-1109B9EF25A8}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{1D05EFE4-7F25-4D5A-BCEE-1109B9EF25A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1D05EFE4-7F25-4D5A-BCEE-1109B9EF25A8}.Release|Any CPU.Build.0 = Release|Any CPU
{1D05EFE4-7F25-4D5A-BCEE-1109B9EF25A8}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{1D05EFE4-7F25-4D5A-BCEE-1109B9EF25A8}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{24CCB459-B4F6-484F-8BA4-946A4AB816FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{24CCB459-B4F6-484F-8BA4-946A4AB816FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{24CCB459-B4F6-484F-8BA4-946A4AB816FA}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{24CCB459-B4F6-484F-8BA4-946A4AB816FA}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{24CCB459-B4F6-484F-8BA4-946A4AB816FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{24CCB459-B4F6-484F-8BA4-946A4AB816FA}.Release|Any CPU.Build.0 = Release|Any CPU
{24CCB459-B4F6-484F-8BA4-946A4AB816FA}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{24CCB459-B4F6-484F-8BA4-946A4AB816FA}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{D9242899-AB3F-46BB-BAB4-386CB8EC535C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D9242899-AB3F-46BB-BAB4-386CB8EC535C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D9242899-AB3F-46BB-BAB4-386CB8EC535C}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{D9242899-AB3F-46BB-BAB4-386CB8EC535C}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{D9242899-AB3F-46BB-BAB4-386CB8EC535C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D9242899-AB3F-46BB-BAB4-386CB8EC535C}.Release|Any CPU.Build.0 = Release|Any CPU
{D9242899-AB3F-46BB-BAB4-386CB8EC535C}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{D9242899-AB3F-46BB-BAB4-386CB8EC535C}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{F98A9787-B175-450F-99FC-EC241EB9D581}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F98A9787-B175-450F-99FC-EC241EB9D581}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F98A9787-B175-450F-99FC-EC241EB9D581}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{F98A9787-B175-450F-99FC-EC241EB9D581}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{F98A9787-B175-450F-99FC-EC241EB9D581}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F98A9787-B175-450F-99FC-EC241EB9D581}.Release|Any CPU.Build.0 = Release|Any CPU
{F98A9787-B175-450F-99FC-EC241EB9D581}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{F98A9787-B175-450F-99FC-EC241EB9D581}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{99C466C3-1931-4C0E-AA0A-A8D9D140F56E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{99C466C3-1931-4C0E-AA0A-A8D9D140F56E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{99C466C3-1931-4C0E-AA0A-A8D9D140F56E}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{99C466C3-1931-4C0E-AA0A-A8D9D140F56E}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{99C466C3-1931-4C0E-AA0A-A8D9D140F56E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{99C466C3-1931-4C0E-AA0A-A8D9D140F56E}.Release|Any CPU.Build.0 = Release|Any CPU
{99C466C3-1931-4C0E-AA0A-A8D9D140F56E}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{99C466C3-1931-4C0E-AA0A-A8D9D140F56E}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{2FBA2BB7-73C8-45CD-99B7-D65F817C9051}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2FBA2BB7-73C8-45CD-99B7-D65F817C9051}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2FBA2BB7-73C8-45CD-99B7-D65F817C9051}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{2FBA2BB7-73C8-45CD-99B7-D65F817C9051}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{2FBA2BB7-73C8-45CD-99B7-D65F817C9051}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2FBA2BB7-73C8-45CD-99B7-D65F817C9051}.Release|Any CPU.Build.0 = Release|Any CPU
{2FBA2BB7-73C8-45CD-99B7-D65F817C9051}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{2FBA2BB7-73C8-45CD-99B7-D65F817C9051}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{D921D320-0450-455F-8DF2-70EDAC5CCE68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D921D320-0450-455F-8DF2-70EDAC5CCE68}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D921D320-0450-455F-8DF2-70EDAC5CCE68}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{D921D320-0450-455F-8DF2-70EDAC5CCE68}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{D921D320-0450-455F-8DF2-70EDAC5CCE68}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D921D320-0450-455F-8DF2-70EDAC5CCE68}.Release|Any CPU.Build.0 = Release|Any CPU
{D921D320-0450-455F-8DF2-70EDAC5CCE68}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{D921D320-0450-455F-8DF2-70EDAC5CCE68}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{7F8ED2E7-A4BE-4855-BAF2-95657220E419}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7F8ED2E7-A4BE-4855-BAF2-95657220E419}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7F8ED2E7-A4BE-4855-BAF2-95657220E419}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{7F8ED2E7-A4BE-4855-BAF2-95657220E419}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{7F8ED2E7-A4BE-4855-BAF2-95657220E419}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7F8ED2E7-A4BE-4855-BAF2-95657220E419}.Release|Any CPU.Build.0 = Release|Any CPU
{7F8ED2E7-A4BE-4855-BAF2-95657220E419}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{7F8ED2E7-A4BE-4855-BAF2-95657220E419}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{1958FBA4-BF2D-48D9-A5DB-8915F553EBD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1958FBA4-BF2D-48D9-A5DB-8915F553EBD3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1958FBA4-BF2D-48D9-A5DB-8915F553EBD3}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{1958FBA4-BF2D-48D9-A5DB-8915F553EBD3}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{1958FBA4-BF2D-48D9-A5DB-8915F553EBD3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1958FBA4-BF2D-48D9-A5DB-8915F553EBD3}.Release|Any CPU.Build.0 = Release|Any CPU
{1958FBA4-BF2D-48D9-A5DB-8915F553EBD3}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{1958FBA4-BF2D-48D9-A5DB-8915F553EBD3}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{23BF6935-D648-4874-91C7-1BF4D6FF64E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{23BF6935-D648-4874-91C7-1BF4D6FF64E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{23BF6935-D648-4874-91C7-1BF4D6FF64E3}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{23BF6935-D648-4874-91C7-1BF4D6FF64E3}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{23BF6935-D648-4874-91C7-1BF4D6FF64E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{23BF6935-D648-4874-91C7-1BF4D6FF64E3}.Release|Any CPU.Build.0 = Release|Any CPU
{23BF6935-D648-4874-91C7-1BF4D6FF64E3}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{23BF6935-D648-4874-91C7-1BF4D6FF64E3}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{DD5071A9-BAE1-45EF-814C-D071BE63CD47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DD5071A9-BAE1-45EF-814C-D071BE63CD47}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DD5071A9-BAE1-45EF-814C-D071BE63CD47}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{DD5071A9-BAE1-45EF-814C-D071BE63CD47}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{DD5071A9-BAE1-45EF-814C-D071BE63CD47}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DD5071A9-BAE1-45EF-814C-D071BE63CD47}.Release|Any CPU.Build.0 = Release|Any CPU
{DD5071A9-BAE1-45EF-814C-D071BE63CD47}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{DD5071A9-BAE1-45EF-814C-D071BE63CD47}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{2367D12D-2A9B-4F52-8948-78C1EDE9059A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2367D12D-2A9B-4F52-8948-78C1EDE9059A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2367D12D-2A9B-4F52-8948-78C1EDE9059A}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{2367D12D-2A9B-4F52-8948-78C1EDE9059A}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{2367D12D-2A9B-4F52-8948-78C1EDE9059A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2367D12D-2A9B-4F52-8948-78C1EDE9059A}.Release|Any CPU.Build.0 = Release|Any CPU
{2367D12D-2A9B-4F52-8948-78C1EDE9059A}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{2367D12D-2A9B-4F52-8948-78C1EDE9059A}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{36D6E1D7-2506-4096-8D76-55C3CCD32DFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{36D6E1D7-2506-4096-8D76-55C3CCD32DFE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{36D6E1D7-2506-4096-8D76-55C3CCD32DFE}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{36D6E1D7-2506-4096-8D76-55C3CCD32DFE}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{36D6E1D7-2506-4096-8D76-55C3CCD32DFE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{36D6E1D7-2506-4096-8D76-55C3CCD32DFE}.Release|Any CPU.Build.0 = Release|Any CPU
{36D6E1D7-2506-4096-8D76-55C3CCD32DFE}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{36D6E1D7-2506-4096-8D76-55C3CCD32DFE}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{85463C41-8D08-4A9D-A475-68A83730431C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{85463C41-8D08-4A9D-A475-68A83730431C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{85463C41-8D08-4A9D-A475-68A83730431C}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{85463C41-8D08-4A9D-A475-68A83730431C}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{85463C41-8D08-4A9D-A475-68A83730431C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{85463C41-8D08-4A9D-A475-68A83730431C}.Release|Any CPU.Build.0 = Release|Any CPU
{85463C41-8D08-4A9D-A475-68A83730431C}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{85463C41-8D08-4A9D-A475-68A83730431C}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{19CE18FD-3F99-4994-B88E-7D32F9303547}.Debug|Any CPU.ActiveCfg = Debug|x64
{19CE18FD-3F99-4994-B88E-7D32F9303547}.Debug|Any CPU.Build.0 = Debug|x64
{19CE18FD-3F99-4994-B88E-7D32F9303547}.Debug-Windows|Any CPU.ActiveCfg = Debug|x64
{19CE18FD-3F99-4994-B88E-7D32F9303547}.Debug-Windows|Any CPU.Build.0 = Debug|x64
{19CE18FD-3F99-4994-B88E-7D32F9303547}.Release|Any CPU.ActiveCfg = Release|x64
{19CE18FD-3F99-4994-B88E-7D32F9303547}.Release|Any CPU.Build.0 = Release|x64
{19CE18FD-3F99-4994-B88E-7D32F9303547}.Release-Windows|Any CPU.ActiveCfg = Release|x64
{19CE18FD-3F99-4994-B88E-7D32F9303547}.Release-Windows|Any CPU.Build.0 = Release|x64
{F0033458-4C9D-4D66-B58D-F49ABABE8030}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F0033458-4C9D-4D66-B58D-F49ABABE8030}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F0033458-4C9D-4D66-B58D-F49ABABE8030}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{F0033458-4C9D-4D66-B58D-F49ABABE8030}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{F0033458-4C9D-4D66-B58D-F49ABABE8030}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F0033458-4C9D-4D66-B58D-F49ABABE8030}.Release|Any CPU.Build.0 = Release|Any CPU
{F0033458-4C9D-4D66-B58D-F49ABABE8030}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{F0033458-4C9D-4D66-B58D-F49ABABE8030}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{8EDAA935-DAA1-4E00-9596-7E40D8C0F58C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8EDAA935-DAA1-4E00-9596-7E40D8C0F58C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8EDAA935-DAA1-4E00-9596-7E40D8C0F58C}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{8EDAA935-DAA1-4E00-9596-7E40D8C0F58C}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{8EDAA935-DAA1-4E00-9596-7E40D8C0F58C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8EDAA935-DAA1-4E00-9596-7E40D8C0F58C}.Release|Any CPU.Build.0 = Release|Any CPU
{8EDAA935-DAA1-4E00-9596-7E40D8C0F58C}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{8EDAA935-DAA1-4E00-9596-7E40D8C0F58C}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{36641924-DE75-4EA9-B139-87F5330D4A09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{36641924-DE75-4EA9-B139-87F5330D4A09}.Debug|Any CPU.Build.0 = Debug|Any CPU
{36641924-DE75-4EA9-B139-87F5330D4A09}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
{36641924-DE75-4EA9-B139-87F5330D4A09}.Debug-Windows|Any CPU.Build.0 = Debug|Any CPU
{36641924-DE75-4EA9-B139-87F5330D4A09}.Release|Any CPU.ActiveCfg = Release|Any CPU
{36641924-DE75-4EA9-B139-87F5330D4A09}.Release|Any CPU.Build.0 = Release|Any CPU
{36641924-DE75-4EA9-B139-87F5330D4A09}.Release-Windows|Any CPU.ActiveCfg = Release|Any CPU
{36641924-DE75-4EA9-B139-87F5330D4A09}.Release-Windows|Any CPU.Build.0 = Release|Any CPU
{C395D6D2-DF99-4FD9-B5C5-04E8BB866509}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C395D6D2-DF99-4FD9-B5C5-04E8BB866509}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C395D6D2-DF99-4FD9-B5C5-04E8BB866509}.Debug-Windows|Any CPU.ActiveCfg = Debug|Any CPU
@ -873,93 +64,6 @@ Global
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{3CF175CF-1AF4-4109-96CB-221684DCED7D} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{CA008713-655E-46DA-BBDF-1EF6B8CE7DCA} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{E325A0E2-716A-49E0-9767-5087CF05727C} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{B2C5EED9-21B2-4763-AB92-C1995B76DA3A} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{9BDF7020-A19F-4C6C-B329-E4BFEFF6DE6B} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{6462DA5D-27DC-4CD5-9467-5EFB998FD838} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{C1F54CDC-AD1D-45BB-8F7D-F49E411AFAF1} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{ADA8AB8B-2066-4193-B8F7-985669B23E00} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{BF414C86-DB3B-4022-9B29-DCE8AA954C12} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{276EBE79-A13A-46BD-A566-B01DC0477A9B} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{67AA3C00-E2C5-4D13-BA5E-72EB0E5B8DAA} = {763168FA-A590-482C-84D8-2922F7ADB1A2}
{7BCEBDC1-D57F-4717-9B15-4FACD5473489} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{053BD8B8-B5C1-4C45-81D4-C9BA8D5B3CE2} = {276EBE79-A13A-46BD-A566-B01DC0477A9B}
{EED5F0D3-6F00-4ED1-9108-5ADCB10A3734} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{C93F6192-0123-4121-AD92-374A71E4B0F3} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{A4184239-F13F-4A09-B2D3-0B9532609248} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{D9B2EF5D-0515-460F-948A-2BB70C8DCF62} = {763168FA-A590-482C-84D8-2922F7ADB1A2}
{763168FA-A590-482C-84D8-2922F7ADB1A2} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{0F639EB4-FB64-4909-8A10-FB93E7BE3AFB} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{2F77CA1D-E6F0-4DEA-96BB-8A039F4D0FF8} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{0A0E26B0-7A46-4F1A-8BFE-9A763FDF6CF8} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{62C4DC83-3B07-45D7-856E-224FFB368E5F} = {0A0E26B0-7A46-4F1A-8BFE-9A763FDF6CF8}
{35F9B8A8-1974-4795-930B-5E4980EE85E5} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{71698D71-4C6F-40D5-8BDE-2587514CA21C} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{0B8ABFDB-F9CF-4EC6-988E-9C32D9E01C26} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{1B7920E6-5262-4054-B72D-3A8DBBA057D2} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{8FC920C6-E895-4A17-AB2F-452FAAA36CC8} = {276EBE79-A13A-46BD-A566-B01DC0477A9B}
{B609DB2C-5C1F-46D1-A0FA-A0FF9216899A} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{3F4A0DD8-4D47-4B9C-939A-3146E68C84F7} = {0A0E26B0-7A46-4F1A-8BFE-9A763FDF6CF8}
{D790A4BB-D8AC-4AAE-B3FE-0CF432CA8031} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{C113E0AE-5564-4389-BA39-183A8D574210} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{B9DDC8CB-8EDF-4D98-913A-22F19E642223} = {8667F820-8ADA-4498-91AE-AE95DEE5227E}
{060F070A-BBFA-490E-BE89-3844C857B771} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{E4E13301-9193-4106-B0E3-41276B478E7C} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{76391566-9F22-4994-8B0F-02EFC0E9E228} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{1BC05915-044E-4776-8956-B44BBEFF2F84} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{AE3FC7F6-B212-4438-B1D7-C121BB4C15ED} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{8DC1257B-7650-40EB-97A2-C1CBA306DA6A} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{D5E70443-4BA2-42ED-992A-010268440B08} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{84E3B6A2-42D9-498A-9CD2-1C5F5BE0D526} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{52CDBBA9-E5AF-433C-80F0-5EF3C8B14946} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{F59673EA-2D0D-441A-BB85-CD343DE6BE45} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{72EE26E2-E8BE-4169-82AD-93E021539C34} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{2454BBCD-77BC-4E3D-B5A6-3562BED898D6} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{685271A8-6C69-46E4-9B11-89AF9761CE0A} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{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}
{E0223463-97C6-4108-B5A9-AFBA16B9D5CC} = {276EBE79-A13A-46BD-A566-B01DC0477A9B}
{EE76BD65-FB01-498F-B053-4E1B2693D85F} = {276EBE79-A13A-46BD-A566-B01DC0477A9B}
{873BCC1F-ED12-424E-93FA-D76F4BA022C2} = {0A0E26B0-7A46-4F1A-8BFE-9A763FDF6CF8}
{2614D290-1345-4A41-BE90-F85F817CEADE} = {0A0E26B0-7A46-4F1A-8BFE-9A763FDF6CF8}
{F3669415-319E-4C4C-A398-89321589B3A9} = {6230B915-B238-4E57-AAC4-06B4498F540F}
{9878B523-BD1E-4285-A875-3CAB4127F7E6} = {E8CD434A-306F-41D9-B67D-BFFF3287354D}
{6E39C647-902F-4CFF-A4E4-194B295E3AF4} = {E8CD434A-306F-41D9-B67D-BFFF3287354D}
{EFAA1D3D-3102-4307-A5DD-EAB4DB9B0386} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{4C82FD14-418F-43E4-AC59-3D926B55CEA3} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{D243AC2D-7823-4177-9D8A-23FDFDA274D2} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{D2D9931A-EBFC-4923-A7BC-EF8BBD76D079} = {6230B915-B238-4E57-AAC4-06B4498F540F}
{170EA6A3-26A6-4A1A-A6F9-44BE5208FA06} = {E8CD434A-306F-41D9-B67D-BFFF3287354D}
{195FC24B-C4DA-4055-918D-5ABF50FD805B} = {E8CD434A-306F-41D9-B67D-BFFF3287354D}
{9F7B2BDF-973B-4639-B890-357EB967B2ED} = {E8CD434A-306F-41D9-B67D-BFFF3287354D}
{E372D54C-73AA-41DB-A471-81348AC37F36} = {E8CD434A-306F-41D9-B67D-BFFF3287354D}
{C8E31CD2-89D4-4659-9557-43EC9C99D984} = {6230B915-B238-4E57-AAC4-06B4498F540F}
{1D05EFE4-7F25-4D5A-BCEE-1109B9EF25A8} = {E8CD434A-306F-41D9-B67D-BFFF3287354D}
{24CCB459-B4F6-484F-8BA4-946A4AB816FA} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{D9242899-AB3F-46BB-BAB4-386CB8EC535C} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{F98A9787-B175-450F-99FC-EC241EB9D581} = {E8CD434A-306F-41D9-B67D-BFFF3287354D}
{99C466C3-1931-4C0E-AA0A-A8D9D140F56E} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{2FBA2BB7-73C8-45CD-99B7-D65F817C9051} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{D921D320-0450-455F-8DF2-70EDAC5CCE68} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{54DA838C-8BB8-4038-8BDB-D887C02B2D9A} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{7F8ED2E7-A4BE-4855-BAF2-95657220E419} = {AA545986-D3E6-406D-8BD5-305B809FB399}
{1958FBA4-BF2D-48D9-A5DB-8915F553EBD3} = {AA545986-D3E6-406D-8BD5-305B809FB399}
{23BF6935-D648-4874-91C7-1BF4D6FF64E3} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{DD5071A9-BAE1-45EF-814C-D071BE63CD47} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{AA545986-D3E6-406D-8BD5-305B809FB399} = {54DA838C-8BB8-4038-8BDB-D887C02B2D9A}
{1024840D-8B5A-4E39-8F6D-F7430DD3749E} = {54DA838C-8BB8-4038-8BDB-D887C02B2D9A}
{2367D12D-2A9B-4F52-8948-78C1EDE9059A} = {1024840D-8B5A-4E39-8F6D-F7430DD3749E}
{36D6E1D7-2506-4096-8D76-55C3CCD32DFE} = {1024840D-8B5A-4E39-8F6D-F7430DD3749E}
{85463C41-8D08-4A9D-A475-68A83730431C} = {1024840D-8B5A-4E39-8F6D-F7430DD3749E}
{19CE18FD-3F99-4994-B88E-7D32F9303547} = {763168FA-A590-482C-84D8-2922F7ADB1A2}
{F0033458-4C9D-4D66-B58D-F49ABABE8030} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{8EDAA935-DAA1-4E00-9596-7E40D8C0F58C} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{36641924-DE75-4EA9-B139-87F5330D4A09} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{ABB57A05-A284-40BA-A143-0CFEADE2B630} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{C395D6D2-DF99-4FD9-B5C5-04E8BB866509} = {ABB57A05-A284-40BA-A143-0CFEADE2B630}
{37BA1593-FE3D-4CB9-A040-6CAB7B2D134A} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}

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

@ -1,270 +0,0 @@
// 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.Net;
using System.Security.Authentication;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents;
using Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Handover;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook
{
/// <summary>
/// BotAdapter to allow for handling Facebook App payloads and responses via the Facebook API.
/// </summary>
public class FacebookAdapter : BotAdapter, IBotFrameworkHttpAdapter
{
private const string HubModeSubscribe = "subscribe";
/// <summary>
/// An instance of the FacebookClientWrapper class.
/// </summary>
private readonly FacebookClientWrapper _facebookClient;
private readonly ILogger _logger;
private readonly FacebookAdapterOptions _options;
/// <summary>
/// Initializes a new instance of the <see cref="FacebookAdapter"/> class using configuration settings.
/// </summary>
/// <param name="configuration">An <see cref="IConfiguration"/> instance.</param>
/// <remarks>
/// The adapter uses these configuration keys:
/// - `VerifyToken`, the token to respond to the initial verification request.
/// - `AppSecret`, the secret used to validate incoming webhooks.
/// - `AccessToken`, an access token for the bot.
/// </remarks>
/// <param name="options">An instance of <see cref="FacebookAdapterOptions"/>.</param>
/// <param name="logger">The logger this adapter should use.</param>
public FacebookAdapter(IConfiguration configuration, FacebookAdapterOptions options = null, ILogger logger = null)
: this(new FacebookClientWrapper(new FacebookClientWrapperOptions(configuration["FacebookVerifyToken"], configuration["FacebookAppSecret"], configuration["FacebookAccessToken"])), options, logger)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="FacebookAdapter"/> class using an existing Facebook client.
/// </summary>
/// /// <param name="facebookClient">Client used to interact with the Facebook API.</param>
/// <param name="options">Options for the Facebook Adapter.</param>
/// <param name="logger">The logger this adapter should use.</param>
/// <exception cref="ArgumentNullException"><paramref name="options"/> is null.</exception>
public FacebookAdapter(FacebookClientWrapper facebookClient, FacebookAdapterOptions options, ILogger logger = null)
{
_options = options ?? new FacebookAdapterOptions();
_facebookClient = facebookClient ?? throw new ArgumentNullException(nameof(facebookClient));
_logger = logger ?? NullLogger.Instance;
}
/// <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 IDs that
/// the receiving channel assigned to the activities.</remarks>
public override async Task<ResourceResponse[]> SendActivitiesAsync(ITurnContext turnContext, Activity[] activities, CancellationToken cancellationToken)
{
var responses = new List<ResourceResponse>();
foreach (var activity in activities)
{
if (activity.Type != ActivityTypes.Message && activity.Type != ActivityTypes.Event)
{
_logger.LogTrace($"Unsupported Activity Type: '{activity.Type}'. Only Activities of type 'Message' or 'Event' are supported.");
}
else
{
var message = FacebookHelper.ActivityToFacebook(activity);
if (message.Message?.Attachment != null)
{
message.Message.Text = null;
}
var res = await _facebookClient.SendMessageAsync("/me/messages", message, null, cancellationToken)
.ConfigureAwait(false);
if (activity.Type == ActivityTypes.Event)
{
if (activity.Name.Equals(HandoverConstants.PassThreadControl, StringComparison.Ordinal))
{
var recipient = (string)activity.Value == "inbox" ? HandoverConstants.PageInboxId : (string)activity.Value;
await _facebookClient.PassThreadControlAsync(recipient, activity.Conversation.Id, HandoverConstants.MetadataPassThreadControl, cancellationToken).ConfigureAwait(false);
}
else if (activity.Name.Equals(HandoverConstants.TakeThreadControl, StringComparison.Ordinal))
{
await _facebookClient.TakeThreadControlAsync(activity.Conversation.Id, HandoverConstants.MetadataTakeThreadControl, cancellationToken).ConfigureAwait(false);
}
else if (activity.Name.Equals(HandoverConstants.RequestThreadControl, StringComparison.Ordinal))
{
await _facebookClient.RequestThreadControlAsync(activity.Conversation.Id, HandoverConstants.MetadataRequestThreadControl, cancellationToken).ConfigureAwait(false);
}
}
var response = new ResourceResponse()
{
Id = res,
};
responses.Add(response);
}
}
return responses.ToArray();
}
/// <summary>
/// Throws a <see cref="NotImplementedException"/> exception in all cases.
/// </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>
public override Task<ResourceResponse> UpdateActivityAsync(ITurnContext turnContext, Activity activity, CancellationToken cancellationToken)
{
return Task.FromException<ResourceResponse>(new NotImplementedException("Facebook adapter does not support updateActivity."));
}
/// <summary>
/// Throws a <see cref="NotImplementedException"/> exception in all cases.
/// </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>
public override Task DeleteActivityAsync(ITurnContext turnContext, ConversationReference reference, CancellationToken cancellationToken)
{
return Task.FromException(new NotImplementedException("Facebook adapter does not support deleteActivity."));
}
/// <summary>
/// Sends a proactive message to a conversation using a conversation reference.
/// </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.</remarks>
/// <exception cref="ArgumentNullException"><paramref name="logic"/> or
/// <paramref name="reference"/> is null.</exception>
public async Task ContinueConversationAsync(ConversationReference reference, BotCallbackHandler logic, CancellationToken cancellationToken)
{
if (reference == null)
{
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);
}
}
/// <summary>
/// Sends a proactive message from the bot to a conversation.
/// </summary>
/// <param name="claimsIdentity">A <see cref="ClaimsIdentity"/> for the conversation.</param>
/// <param name="reference">A reference to the conversation to continue.</param>
/// <param name="callback">The method to call for the resulting bot turn.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>Call this method to proactively send a message to a conversation.
/// <para>This method registers the following services for the turn.<list type="bullet">
/// <item><description><see cref="IIdentity"/> (key = "BotIdentity"), a claims claimsIdentity for the bot.
/// </description></item>
/// </list></para>
/// </remarks>
/// <seealso cref="BotAdapter.RunPipelineAsync(ITurnContext, BotCallbackHandler, CancellationToken)"/>
public override async Task ContinueConversationAsync(ClaimsIdentity claimsIdentity, ConversationReference reference, BotCallbackHandler callback, CancellationToken cancellationToken)
{
using (var context = new TurnContext(this, reference.GetContinuationActivity()))
{
context.TurnState.Add<IIdentity>(BotIdentityKey, claimsIdentity);
context.TurnState.Add<BotCallbackHandler>(callback);
await RunPipelineAsync(context, callback, cancellationToken).ConfigureAwait(false);
}
}
/// <summary>
/// Accepts an incoming webhook request, creates a turn context,
/// and runs the middleware pipeline for an incoming TRUSTED activity.
/// </summary>
/// <param name="httpRequest">Represents the incoming side of an HTTP request.</param>
/// <param name="httpResponse">Represents the outgoing side of an HTTP request.</param>
/// <param name="bot">The code to run at the end of the adapter's middleware pipeline.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <exception cref="AuthenticationException">The webhook receives message with invalid signature.</exception>
public async Task ProcessAsync(HttpRequest httpRequest, HttpResponse httpResponse, IBot bot, CancellationToken cancellationToken = default)
{
if (httpRequest.Query["hub.mode"] == HubModeSubscribe && _options.VerifyIncomingRequests)
{
await _facebookClient.VerifyWebhookAsync(httpRequest, httpResponse, cancellationToken).ConfigureAwait(false);
return;
}
string stringifiedBody;
using (var sr = new StreamReader(httpRequest.Body))
{
stringifiedBody = await sr.ReadToEndAsync().ConfigureAwait(false);
}
if (!_facebookClient.VerifySignature(httpRequest, stringifiedBody) && _options.VerifyIncomingRequests)
{
await FacebookHelper.WriteAsync(httpResponse, HttpStatusCode.Unauthorized, string.Empty, Encoding.UTF8, cancellationToken).ConfigureAwait(false);
throw new AuthenticationException("Webhook received message with invalid signature. Potential malicious behavior!");
}
FacebookResponseEvent facebookResponseEvent = null;
facebookResponseEvent = JsonConvert.DeserializeObject<FacebookResponseEvent>(stringifiedBody);
foreach (var entry in facebookResponseEvent.Entry)
{
var payload = entry.Changes.Count > 0 ? entry.Changes : entry.Messaging.Count > 0 ? entry.Messaging : entry.Standby.Count > 0 ? entry.Standby : new List<FacebookMessage>();
foreach (var message in payload)
{
message.IsStandby = entry.Standby.Count > 0;
var activity = FacebookHelper.ProcessSingleMessage(message);
using (var context = new TurnContext(this, activity))
{
await RunPipelineAsync(context, bot.OnTurnAsync, cancellationToken).ConfigureAwait(false);
}
}
}
}
}
}

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

@ -1,18 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Bot.Builder.Adapters.Facebook
{
/// <summary>
/// Options class for Facebook Adapter.
/// </summary>
public class FacebookAdapterOptions
{
/// <summary>
/// Gets or sets a value indicating whether incoming requests should be verified.
/// Should be set to true in Production but can be set to false for testing or development purposes.
/// </summary>
/// <value>The flag to indicate if incoming requests should be verified.</value>
public bool VerifyIncomingRequests { get; set; } = true;
}
}

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

@ -1,282 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents;
using Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Handover;
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook
{
/// <summary>
/// Client for interacting with the Facebook API.
/// </summary>
public class FacebookClientWrapper
{
/// <summary>
/// An instance of the FacebookClientWrapperOptions class.
/// </summary>
private readonly FacebookClientWrapperOptions _options;
/// <summary>
/// Initializes a new instance of the <see cref="FacebookClientWrapper"/> class.
/// </summary>
/// <param name="options">An object containing API credentials, a webhook verification token, and other options.</param>
public FacebookClientWrapper(FacebookClientWrapperOptions options)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
}
/// <summary>
/// Sends a REST message to Facebook.
/// </summary>
/// <param name="path">Path to the API endpoint, for example `/me/messages`.</param>
/// <param name="payload">An object to be sent as parameters to the API call.</param>
/// <param name="method">The HTTP method, for example POST, GET, DELETE or PUT.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <exception cref="ArgumentNullException"><paramref name="path"/> or <paramref name="payload"/> is null.</exception>
public virtual async Task<string> SendMessageAsync(string path, FacebookMessage payload, HttpMethod method = null, CancellationToken cancellationToken = default)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
if (payload == null)
{
throw new ArgumentNullException(nameof(payload));
}
var proof = GetAppSecretProof();
if (method == null)
{
method = HttpMethod.Post;
}
using (var request = new HttpRequestMessage())
{
request.RequestUri = new Uri($"https://{_options.FacebookApiHost}/{_options.FacebookApiVersion + path}?access_token={_options.FacebookAccessToken}&appsecret_proof={proof.ToLowerInvariant()}");
request.Method = method;
var json = JsonConvert.SerializeObject(
payload,
Formatting.None,
new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
});
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
using (var client = new HttpClient())
{
var res = await client.SendAsync(request, cancellationToken).ConfigureAwait(false);
if (res.IsSuccessStatusCode)
{
var responseBody = await res.Content.ReadAsStringAsync().ConfigureAwait(false);
var stringResponse = JsonConvert.DeserializeObject<FacebookResponseOk>(responseBody);
return stringResponse.MessageId;
}
else
{
// In Azure view this exception via Application Insights/Failures.
throw new HttpRequestException($"SendMessageAsync(): {res.ToString()}");
}
}
}
}
/// <summary>
/// Verifies the SHA1 signature of the raw request payload before bodyParser parses it will abort parsing if signature is invalid, and pass a generic error to response.
/// </summary>
/// <param name="request">Represents the incoming side of an HTTP request.</param>
/// <param name="payload">The request body.</param>
/// <returns>The result of the comparison between the signature in the request and hashed body.</returns>
/// <exception cref="ArgumentNullException"><paramref name="request"/> is null.</exception>
public virtual bool VerifySignature(HttpRequest request, string payload)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
var expected = request.Headers["x-hub-signature"].ToString().ToUpperInvariant();
#pragma warning disable CA5350 // Facebook uses SHA1 as cryptographic algorithm.
using (var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(_options.FacebookAppSecret)))
{
hmac.Initialize();
var hashArray = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
var hash = $"SHA1={BitConverter.ToString(hashArray).Replace("-", string.Empty)}";
return expected == hash;
}
#pragma warning restore CA5350 // Facebook uses SHA1 as cryptographic algorithm.
}
/// <summary>
/// Generates the app secret proof used to increase security on calls to the Graph API.
/// </summary>
/// <returns>The app secret proof.</returns>
public virtual string GetAppSecretProof()
{
using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(_options.FacebookAppSecret)))
{
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(_options.FacebookAccessToken));
return BitConverter.ToString(hash).Replace("-", string.Empty);
}
}
/// <summary>
/// Verifies the verify token from the message. If the token matches the one configured, sends back the challenge.
/// </summary>
/// <param name="request">Represents the incoming side of an HTTP request.</param>
/// <param name="response">Represents the outgoing side of an HTTP request.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <exception cref="ArgumentNullException"><paramref name="request"/> or <paramref name="response"/> is null.</exception>
public virtual async Task VerifyWebhookAsync(HttpRequest request, HttpResponse response, CancellationToken cancellationToken)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (response == null)
{
throw new ArgumentNullException(nameof(response));
}
var challenge = string.Empty;
HttpStatusCode statusCode;
if (request.Query["hub.verify_token"].Equals(_options.FacebookVerifyToken))
{
challenge = request.Query["hub.challenge"];
statusCode = HttpStatusCode.OK;
}
else
{
statusCode = HttpStatusCode.Unauthorized;
}
await FacebookHelper.WriteAsync(response, statusCode, challenge, Encoding.UTF8, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Posts webhook control events to Facebook.
/// </summary>
/// <param name="postType">The REST post type (GET, PUT, POST, etc).</param>
/// <param name="content">The string content to be posted to Facebook.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>`true` if the operation succeeded; otherwise, `false`.</returns>
/// <exception cref="ArgumentNullException"><paramref name="postType"/> or <paramref name="content"/> is null.</exception>
public virtual async Task<bool> PostToFacebookApiAsync(string postType, string content, CancellationToken cancellationToken)
{
if (postType == null)
{
throw new ArgumentNullException(nameof(postType));
}
if (content == null)
{
throw new ArgumentNullException(nameof(content));
}
var graphApiBaseUrl = $"https://{_options.FacebookApiHost}/{_options.FacebookApiVersion + postType}?access_token={_options.FacebookAccessToken}";
var requestPath = string.Format(System.Globalization.CultureInfo.InvariantCulture, graphApiBaseUrl, postType, _options.FacebookAccessToken);
var stringContent = new StringContent(content, Encoding.UTF8, "application/json");
using (var requestMessage = new HttpRequestMessage())
{
requestMessage.Method = new HttpMethod("POST");
requestMessage.RequestUri = new Uri(requestPath);
requestMessage.Content = stringContent;
requestMessage.Content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json; charset=utf-8");
using (var client = new HttpClient())
{
var res = await client.SendAsync(requestMessage, cancellationToken).ConfigureAwait(false);
return res.IsSuccessStatusCode;
}
}
}
/// <summary>
/// Sends the request_thread_control webhook event to Facebook.
/// </summary>
/// <param name="userId">The sender user ID.</param>
/// <param name="message">An optional message for the metadata parameter.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>`true` if the operation succeeded; otherwise, `false`.</returns>
/// <exception cref="ArgumentNullException"><paramref name="userId"/> is null.</exception>
public virtual async Task<bool> RequestThreadControlAsync(string userId, string message, CancellationToken cancellationToken)
{
if (userId == null)
{
throw new ArgumentNullException(nameof(userId));
}
var content = new { recipient = new { id = userId }, metadata = message };
return await PostToFacebookApiAsync($"/me/{HandoverConstants.RequestThreadControl}", JsonConvert.SerializeObject(content), cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Sends the take_thread_control webhook event to Facebook.
/// </summary>
/// <param name="userId">The sender user ID.</param>
/// <param name="message">An optional message for the metadata parameter.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>`true` if the operation succeeded; otherwise, `false`.</returns>
/// <exception cref="ArgumentNullException"><paramref name="userId"/> is null.</exception>
public virtual async Task<bool> TakeThreadControlAsync(string userId, string message, CancellationToken cancellationToken)
{
if (userId == null)
{
throw new ArgumentNullException(nameof(userId));
}
var content = new { recipient = new { id = userId }, metadata = message };
return await PostToFacebookApiAsync($"/me/{HandoverConstants.TakeThreadControl}", JsonConvert.SerializeObject(content), cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Sends the pass_thread_control webhook event to Facebook.
/// </summary>
/// <param name="targetAppId">The ID of the target app to pass control to.</param>
/// <param name="userId">The sender user ID.</param>
/// <param name="message">An optional message for the metadata parameter.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>`true` if the operation succeeded; otherwise, `false`.</returns>
/// <exception cref="ArgumentNullException"><paramref name="targetAppId"/> or <paramref name="userId"/> is null.</exception>
public virtual async Task<bool> PassThreadControlAsync(string targetAppId, string userId, string message, CancellationToken cancellationToken)
{
if (targetAppId == null)
{
throw new ArgumentNullException(nameof(targetAppId));
}
if (userId == null)
{
throw new ArgumentNullException(nameof(userId));
}
var content = new { recipient = new { id = userId }, target_app_id = targetAppId, metadata = message };
return await PostToFacebookApiAsync($"/me/{HandoverConstants.PassThreadControl}", JsonConvert.SerializeObject(content), cancellationToken).ConfigureAwait(false);
}
}
}

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

@ -1,62 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Threading.Tasks;
namespace Microsoft.Bot.Builder.Adapters.Facebook
{
/// <summary>
/// Options class for Facebook Adapter.
/// </summary>
public class FacebookClientWrapperOptions
{
/// <summary>
/// Initializes a new instance of the <see cref="FacebookClientWrapperOptions"/> class.
/// </summary>
/// <param name="verifyToken">The token used to validate that incoming webhooks are originated from Facebook.</param>
/// <param name="appSecret">The app secret.</param>
/// <param name="accessToken">The Facebook access token.</param>
/// <param name="apiHost">A token for validating the origin of incoming webhooks.</param>
/// <param name="apiVersion">A token for a bot to work on a single workspace.</param>
public FacebookClientWrapperOptions(string verifyToken, string appSecret, string accessToken, string apiHost = "graph.facebook.com", string apiVersion = "v3.2")
{
FacebookVerifyToken = verifyToken;
FacebookAppSecret = appSecret;
FacebookAccessToken = accessToken;
FacebookApiHost = apiHost;
FacebookApiVersion = apiVersion;
}
/// <summary>
/// Gets or sets the alternate root URL used to construct calls to Facebook's API. Defaults to "graph.facebook.com" but can be changed (for mocking, proxy, etc).
/// </summary>
/// <value>The API host.</value>
public string FacebookApiHost { get; set; }
/// <summary>
/// Gets or sets the alternate API version used to construct calls to Facebook's API. Defaults to "v3.2".
/// </summary>
/// <value>The API version.</value>
public string FacebookApiVersion { get; set; }
/// <summary>
/// Gets or sets the verify token used to initially create and verify the webhooks subscription settings on Facebook's developer portal.
/// </summary>
/// <value>The verify token.</value>
public string FacebookVerifyToken { get; set; }
/// <summary>
/// Gets or sets the app secret from the **Basic Settings** page from your app's configuration in the Facebook developer portal.
/// </summary>
/// <value>The app secret.</value>
public string FacebookAppSecret { get; set; }
/// <summary>
/// Gets or sets the access token.
/// When bound to a single page, use `access_token` to specify the "page access token" provided in the Facebook developer portal's "Access Tokens" widget of the "Messenger Settings" page.
/// </summary>
/// <value>The access token.</value>
public string FacebookAccessToken { get; set; }
}
}

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

@ -1,92 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Templates;
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
{
/// <summary>
/// Inner Facebook Attachment payload that can be sent as part of a Facebook Attachment on a Facebook message.
/// </summary>
public class AttachmentPayload
{
/// <summary>
/// Gets or sets the URL of the attachment.
/// </summary>
/// <value>The URL of the attachment.</value>
[JsonProperty(PropertyName = "url")]
public Uri Url { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the attachment is reusable or not. Default false.
/// </summary>
/// <value>Indicates whether the attachment is reusable.</value>
[JsonProperty(PropertyName = "is_reusable")]
public bool IsReusable { get; set; }
/// <summary>
/// Gets or sets the ID of the attachment (for reusable attachments).
/// </summary>
/// <value>The ID of the saved attachment.</value>
[JsonProperty(PropertyName = "attachment_id")]
public string AttachmentId { get; set; }
/// <summary>
/// Gets or sets the type of the template attached.
/// </summary>
/// <value>The type of template attached.</value>
[JsonProperty(PropertyName = "template_type")]
public string TemplateType { get; set; }
/// <summary>
/// Gets or sets the text of the template.
/// </summary>
/// <value>The text of the template.</value>
[JsonProperty(PropertyName = "text")]
public string Text { get; set; }
/// <summary>
/// Gets a list of buttons of the template.
/// </summary>
/// <value>The list of buttons of the template.</value>
[JsonProperty(PropertyName = "buttons")]
public List<Button> Buttons { get; } = new List<Button>();
/// <summary>
/// Gets a list of media elements of the template.
/// </summary>
/// <value>The list of media elements of the template.</value>
[JsonProperty(PropertyName = "elements")]
public List<Element> Elements { get; } = new List<Element>();
/// <summary>
/// Newtonsoft JSON method for conditionally serializing the <see cref="IsReusable"/> property.
/// </summary>
/// <returns>`true` to serialize the property; otherwise, `false`.</returns>
public bool ShouldSerializeIsReusable()
{
return IsReusable;
}
/// <summary>
/// Newtonsoft JSON method for conditionally serializing the <see cref="Buttons"/> property.
/// </summary>
/// <returns>`true` to serialize the property; otherwise, `false`.</returns>
public bool ShouldSerializeButtons()
{
return Buttons.Count > 0;
}
/// <summary>
/// Newtonsoft JSON method for conditionally serializing the <see cref="Elements"/> property.
/// </summary>
/// <returns>`true` to serialize the property; otherwise, `false`.</returns>
public bool ShouldSerializeElements()
{
return Elements.Count > 0;
}
}
}

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

@ -1,28 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
{
/// <summary>
/// Facebook Attachment object that can be sent as part of a Facebook message.
/// </summary>
public class FacebookAttachment
{
/// <summary>
/// Gets or sets the type of the attachment.
/// </summary>
/// <value>The type of attachment.
/// May be "image", "audio", "video", "file", or "template".</value>
[JsonProperty(PropertyName = "type")]
public string Type { get; set; }
/// <summary>
/// Gets or sets the payload of the attachment.
/// </summary>
/// <value>The payload of the attachment.</value>
[JsonProperty(PropertyName = "payload")]
public AttachmentPayload Payload { get; set; }
}
}

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

@ -1,20 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
{
/// <summary>
/// Represents a Facebook Bot User object.
/// </summary>
public class FacebookBotUser
{
/// <summary>
/// Gets or sets the ID of the bot user.
/// </summary>
/// <value>The ID of the bot user.</value>
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
}
}

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

@ -1,71 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections.Generic;
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
{
/// <summary>
/// Represents a Facebook Message Entry.
/// </summary>
public class FacebookEntry
{
/// <summary>
/// Gets or sets the page ID.
/// </summary>
/// <value>The ID of the page.</value>
public string Id { get; set; }
/// <summary>
/// Gets or sets the time of the update.
/// </summary>
/// <value>The time of update (epoch time in milliseconds).</value>
public long Time { get; set; }
/// <summary>
/// Gets the messaging list.
/// </summary>
/// <value>List containing one messaging object.</value>
/// <remarks>Note that even though this is an enumerable, it will only contain one object.</remarks>
public List<FacebookMessage> Messaging { get; } = new List<FacebookMessage>();
/// <summary>
/// Gets the changes list.
/// </summary>
/// <value>The list of changes.</value>
public List<FacebookMessage> Changes { get; } = new List<FacebookMessage>();
/// <summary>
/// Gets the standby messages list.
/// </summary>
/// <value>List containing the messages sent while in standby mode.</value>
public List<FacebookMessage> Standby { get; } = new List<FacebookMessage>();
/// <summary>
/// Newtonsoft JSON method for conditionally serializing the <see cref="Messaging"/> property.
/// </summary>
/// <returns>`true` to serialize the property; otherwise, `false`.</returns>
public bool ShouldSerializeMessaging()
{
return Messaging.Count > 0;
}
/// <summary>
/// Newtonsoft JSON method for conditionally serializing the <see cref="Standby"/> property.
/// </summary>
/// <returns>`true` to serialize the property; otherwise, `false`.</returns>
public bool ShouldSerializeStandby()
{
return Standby.Count > 0;
}
/// <summary>
/// Newtonsoft JSON method for conditionally serializing the <see cref="Changes"/> property.
/// </summary>
/// <returns>`true` to serialize the property; otherwise, `false`.</returns>
public bool ShouldSerializeChanges()
{
return Changes.Count > 0;
}
}
}

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

@ -1,34 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
{
/// <summary>
/// Represents a Facebook Post Back.
/// </summary>
public class FacebookPostBack
{
/// <summary>
/// Gets or sets the title of the post back message.
/// </summary>
/// <value>The title of the post back message.</value>
[JsonProperty(PropertyName = "title")]
public string Title { get; set; }
/// <summary>
/// Gets or sets the string to send back to the webhook.
/// </summary>
/// <value>The string to post back to the webhook.</value>
[JsonProperty(PropertyName = "payload")]
public string Payload { get; set; }
/// <summary>
/// Gets or sets the referral of the post back message.
/// </summary>
/// <value>The referral of the post back message.</value>
[JsonProperty(PropertyName = "referral")]
public string Referral { get; set; }
}
}

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

@ -1,46 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
{
/// <summary>
/// Facebook Quick Reply object that can be sent as part of a Facebook message.
/// </summary>
public class FacebookQuickReply
{
/// <summary>
/// Gets or sets the content type of the reply. Can be:
/// - "text", which sends a text button.
/// - "user_phone_number", which sends a button allowing the recipient to send the phone number associated with their account.
/// - "user_email", which sends a button allowing the recipient to send the email associated with their account.
/// </summary>
/// <value>The content type.</value>
[JsonProperty(PropertyName = "content_type")]
public string ContentType { get; set; }
/// <summary>
/// Gets or sets the title of the reply. Required if content_type is "text".
/// </summary>
/// <value>The title of the reply. 20 character limit. </value>
[JsonProperty(PropertyName = "title")]
public string Title { get; set; }
/// <summary>
/// Gets or sets the payload of the reply. May be set to an empty string if the <see cref="ImageUrl"/> property is set.
/// </summary>
/// <value>The payload. Can be either a string or a long.</value>
[JsonProperty(PropertyName = "payload")]
public object Payload { get; set; }
/// <summary>
/// Gets or sets the optional URL of the image to display on the quick reply button for text quick replies.
/// Required if the <see cref="Title"/> property is empty.
/// </summary>
/// <value>The optional URL of the image to display.</value>
[JsonProperty(PropertyName = "image_url")]
public Uri ImageUrl { get; set; }
}
}

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

@ -1,20 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
{
/// <summary>A Facebook read message, including watermark of messages that were read.</summary>
public class FacebookRead
{
/// <summary>
/// Gets or sets the timestamp were messages were read.
/// </summary>
/// <value>
/// All messages that were sent before or at this timestamp were read.
/// </value>
[JsonProperty("watermark")]
public long Watermark { get; set; }
}
}

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

@ -1,41 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
{
/// <summary>
/// Facebook Recipient object used as part of a Facebook message.
/// </summary>
public class FacebookRecipient
{
/// <summary>
/// Gets or sets the message recipient ID.
/// </summary>
/// <value>The Page-scoped ID (PSID) of the message recipient.</value>
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
/// <summary>
/// Gets or sets the value for the `user_ref` parameter of the checkbox plugin.
/// </summary>
/// <value>Used for the checkbox plugin.</value>
[JsonProperty(PropertyName = "user_ref")]
public string UserRef { get; set; }
/// <summary>
/// Gets or sets the post ID.
/// </summary>
/// <value>Used for private replies to reference the visitor post to reply to.</value>
[JsonProperty(PropertyName = "post_id")]
public string PostId { get; set; }
/// <summary>
/// Gets or sets the comment ID.
/// </summary>
/// <value>Used for private replies to reference the visitor comment to reply to.</value>
[JsonProperty(PropertyName = "comment_id")]
public string CommentId { get; set; }
}
}

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

@ -1,36 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
{
/// <summary>
/// Represents the incoming object received from Facebook and processed by the adapter.
/// </summary>
public class FacebookResponseEvent
{
/// <summary>
/// Gets or sets the object property.
/// </summary>
/// <value>A string with different values.</value>
[JsonProperty(PropertyName = "object")]
public string ResponseObject { get; set; }
/// <summary>
/// Gets the entry property.
/// </summary>
/// <value>Array containing event data.</value>
public List<FacebookEntry> Entry { get; } = new List<FacebookEntry>();
/// <summary>
/// Gets the flag to determine if the Entry property should be serialized.
/// </summary>
/// <returns>True if Entry count is greater than zero.</returns>
public bool ShouldSerializeEntry()
{
return Entry.Count > 0;
}
}
}

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

@ -1,27 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents
{
/// <summary>
/// Represents the response object received from Facebook API when a message is sent.
/// </summary>
public class FacebookResponseOk
{
/// <summary>
/// Gets or sets the recipient ID.
/// </summary>
/// <value>The ID of the recipient.</value>
[JsonProperty(PropertyName = "recipient_id")]
public string RecipientId { get; set; }
/// <summary>
/// Gets or sets the message ID.
/// </summary>
/// <value>The message ID.</value>
[JsonProperty(PropertyName = "message_id")]
public string MessageId { get; set; }
}
}

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

@ -1,37 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Handover
{
/// <summary>
/// Event object sent to Facebook when requesting to pass thread control to another app.
/// </summary>
public class FacebookPassThreadControl : FacebookThreadControl
{
/// <summary>
/// Gets or sets the app ID of the new owner.
/// </summary>
/// <remarks>
/// 263902037430900 for the page inbox.
/// </remarks>
/// <value>The app ID of the new owner.</value>
[JsonProperty("new_owner_app_id")]
public string NewOwnerAppId { get; set; }
/// <summary>
/// Gets or sets the app ID of the previous owner.
/// </summary>
/// <value>The app ID of the previous owner.</value>
[JsonProperty("previous_owner_app_id")]
public string PreviousOwnerAppId { get; set; }
/// <summary>
/// Gets or sets the app ID of the requested owner.
/// </summary>
/// <value>The app ID of the requested owner.</value>
[JsonProperty("requested_owner_app_id")]
public string RequestedOwnerAppId { get; set; }
}
}

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

@ -1,24 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Handover
{
/// <summary>A Facebook thread control message, including app ID of requested thread owner and an optional message
/// to send with the request.</summary>
public class FacebookRequestThreadControl : FacebookThreadControl
{
/// <summary>
/// Gets or sets the app ID of the requested owner.
/// </summary>
/// <remarks>
/// 263902037430900 for the page inbox.
/// </remarks>
/// <value>
/// the app ID of the requested owner.
/// </value>
[JsonProperty("requested_owner_app_id")]
public string RequestedOwnerAppId { get; set; }
}
}

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

@ -1,25 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Handover
{
/// <summary>
/// Event object sent to Facebook when requesting to take thread control from another app.
/// </summary>
public class FacebookTakeThreadControl : FacebookThreadControl
{
/// <summary>
/// Gets or sets the app ID of the previous owner.
/// </summary>
/// <remarks>
/// 263902037430900 for the page inbox.
/// </remarks>
/// <value>
/// The app ID of the previous owner.
/// </value>
[JsonProperty("previous_owner_app_id")]
public string PreviousOwnerAppId { get; set; }
}
}

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

@ -1,24 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Handover
{
/// <summary>
/// Base event object for thread control request payloads.
/// </summary>
public class FacebookThreadControl
{
/// <summary>
/// Gets or sets the message sent from the requester.
/// </summary>
/// <remarks>
/// Example: "All yours!".
/// </remarks>
/// <value>
/// Message sent from the requester.
/// </value>
[JsonProperty("metadata")]
public string Metadata { get; set; }
}
}

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

@ -1,46 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Handover
{
/// <summary>
/// Constants used as part of the Facebook handover protocol.
/// </summary>
public static class HandoverConstants
{
/// <summary>
/// Constant for the pass_thread_control webhook event.
/// </summary>
public const string PassThreadControl = "pass_thread_control";
/// <summary>
/// Constant for the request_thread_control webhook event.
/// </summary>
public const string RequestThreadControl = "request_thread_control";
/// <summary>
/// Constant for the take_thread_control webhook event.
/// </summary>
public const string TakeThreadControl = "take_thread_control";
/// <summary>
/// Constant value for the inbox ID of any page in Facebook.
/// </summary>
public const string PageInboxId = "263902037430900";
/// <summary>
/// Constant for passing thread control to be shared between all bots using the handover protocol.
/// </summary>
public const string MetadataPassThreadControl = "PassThreadControl";
/// <summary>
/// Constant for requesting thread control to be shared between all bots using the handover protocol.
/// </summary>
public const string MetadataRequestThreadControl = "RequestThreadControl";
/// <summary>
/// Constant for taking thread control to be shared between all bots using the handover protocol.
/// </summary>
public const string MetadataTakeThreadControl = "TakeThreadControl";
}
}

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

@ -1,42 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Templates
{
/// <summary>
/// Facebook button object.
/// </summary>
public class Button
{
/// <summary>
/// Gets or sets the type of button.
/// </summary>
/// <value>The type of the button.</value>
[JsonProperty(PropertyName = "type")]
public string Type { get; set; }
/// <summary>
/// Gets or sets the URL of the button.
/// </summary>
/// <value>The URL of the button.</value>
[JsonProperty(PropertyName = "url")]
public Uri Url { get; set; }
/// <summary>
/// Gets or sets the title of the button.
/// </summary>
/// <value>The title of the button.</value>
[JsonProperty(PropertyName = "title")]
public string Title { get; set; }
/// <summary>
/// Gets or sets the string sent to webhook.
/// </summary>
/// <value>The string sent to webhook.</value>
[JsonProperty(PropertyName = "payload")]
public string Payload { get; set; }
}
}

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

@ -1,42 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Templates
{
/// <summary>
/// Default action template.
/// </summary>
public class DefaultAction
{
/// <summary>
/// Gets or sets the type of action.
/// </summary>
/// <value>The type of action.</value>
[JsonProperty(PropertyName = "type")]
public string ActionType { get; set; }
/// <summary>
/// Gets or sets the default URL to open.
/// </summary>
/// <value>The URL of the action.</value>
[JsonProperty(PropertyName = "url")]
public Uri ActionUrl { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the action has extensions or not.
/// </summary>
/// <value>Indicates whether the action has extensions or not.</value>
[JsonProperty(PropertyName = "messenger_extensions")]
public bool MessengerExtensions { get; set; }
/// <summary>
/// Gets or sets the height ratio for the WebView. It can be "COMPACT", "TALL", or "FULL".
/// </summary>
/// <value>The height ratio for the WebView.</value>
[JsonProperty(PropertyName = "webview_height_ratio")]
public string WebviewHeightRatio { get; set; }
}
}

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

@ -1,73 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Templates
{
/// <summary>
/// Represents an element of a template message.
/// </summary>
public class Element
{
/// <summary>
/// Gets or sets the type of the media element.
/// </summary>
/// <value>The type of the media element.</value>
[JsonProperty(PropertyName = "media_type")]
public string MediaType { get; set; }
/// <summary>
/// Gets or sets the URL of the media element.
/// </summary>
/// <value>The URL of the media element.</value>
[JsonProperty(PropertyName = "url")]
public Uri Url { get; set; }
/// <summary>
/// Gets or sets the title of the element.
/// </summary>
/// <value>The title of the element.</value>
[JsonProperty(PropertyName = "title")]
public string Title { get; set; }
/// <summary>
/// Gets or sets the subtitle text of the element.
/// </summary>
/// <value>The subtitle of the element.</value>
[JsonProperty(PropertyName = "subtitle")]
public string Subtitle { get; set; }
/// <summary>
/// Gets or sets the URL of the image.
/// </summary>
/// <value>The URL of the image.</value>
[JsonProperty(PropertyName = "image_url")]
public Uri ImageUrl { get; set; }
/// <summary>
/// Gets or sets the default action for the element.
/// </summary>
/// <value>The default action for the element.</value>
[JsonProperty(PropertyName = "default_action")]
public DefaultAction DefaultAction { get; set; }
/// <summary>
/// Gets a list of buttons for the element.
/// </summary>
/// <value>The list of buttons for the element.</value>
[JsonProperty(PropertyName = "buttons")]
public List<Button> Buttons { get; } = new List<Button>();
/// <summary>
/// Newtonsoft JSON method for conditionally serializing the <see cref="Buttons"/> property.
/// </summary>
/// <returns>`true` to serialize the property; otherwise, `false`.</returns>
public bool ShouldSerializeButtons()
{
return Buttons.Count > 0;
}
}
}

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

@ -1,248 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents;
using Microsoft.Bot.Schema;
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook
{
/// <summary>
/// Helper class for converting between Bot Framework objects and Facebook API objects.
/// </summary>
public static class FacebookHelper
{
/// <summary>
/// Converts a Bot Framework activity to a Facebook messenger outbound message ready for the API.
/// </summary>
/// <param name="activity">The activity to be converted to Facebook message.</param>
/// <returns>The resulting message.</returns>
/// <exception cref="ArgumentNullException"><paramref name="activity"/> is null.</exception>
public static FacebookMessage ActivityToFacebook(Activity activity)
{
if (activity == null)
{
throw new ArgumentNullException(nameof(activity));
}
var facebookMessage = new FacebookMessage(activity.Conversation.Id, new Message(), "RESPONSE");
facebookMessage.Message.Text = activity.Text;
if (activity.ChannelData != null)
{
facebookMessage = activity.GetChannelData<FacebookMessage>();
if (facebookMessage.SenderAction != null)
{
facebookMessage.Message = null;
}
else
{
// make sure the quick reply has a type
if (facebookMessage.Message.QuickReplies.Any())
{
foreach (var reply in facebookMessage.Message.QuickReplies)
{
if (string.IsNullOrWhiteSpace(reply.ContentType))
{
reply.ContentType = "text";
}
}
}
}
}
if (activity.Attachments != null && activity.Attachments.Count > 0 && facebookMessage.Message != null)
{
var payload = JsonConvert.DeserializeObject<AttachmentPayload>(JsonConvert.SerializeObject(
activity.Attachments[0].Content,
Formatting.None,
new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
}));
var facebookAttachment = new FacebookAttachment
{
Type = activity.Attachments[0].ContentType,
Payload = payload,
};
facebookMessage.Message.Attachment = facebookAttachment;
}
return facebookMessage;
}
/// <summary>
/// Converts a single Facebook messenger message to a Bot Framework activity.
/// </summary>
/// <param name="message">The message to be processed.</param>
/// <returns>An Activity with the result.</returns>
/// <exception cref="ArgumentNullException"><paramref name="message"/> is null.</exception>
/// <remarks>A webhook call may deliver more than one message at a time.</remarks>
public static Activity ProcessSingleMessage(FacebookMessage message)
{
if (message == null)
{
throw new ArgumentNullException(nameof(message));
}
if (message.Sender == null && message.OptIn?.UserRef != null)
{
message.Sender = new FacebookBotUser { Id = message.OptIn?.UserRef };
}
var activity = new Activity()
{
ChannelId = "facebook",
Timestamp = default,
Conversation = new ConversationAccount()
{
Id = message.Sender?.Id,
},
From = new ChannelAccount()
{
Id = message.Sender?.Id,
Name = message.Sender?.Id,
},
Recipient = new ChannelAccount()
{
Id = message.Recipient.Id,
Name = message.Recipient.Id,
},
ChannelData = message,
Type = ActivityTypes.Event,
Text = null,
};
if (message.PassThreadControl != null)
{
activity.Value = message.PassThreadControl;
}
else if (message.RequestThreadControl != null)
{
activity.Value = message.RequestThreadControl;
}
else if (message.TakeThreadControl != null)
{
activity.Value = message.TakeThreadControl;
}
if (message.Message != null)
{
activity.Text = message.Message.Text;
activity.Type = activity.GetChannelData<FacebookMessage>().Message.IsEcho ? ActivityTypes.Event : ActivityTypes.Message;
if (message.Message.Attachments != null && message.Message.Attachments.Count > 0)
{
activity.Attachments = HandleMessageAttachments(message.Message);
}
}
else if (message.PostBack != null)
{
activity.Type = ActivityTypes.Message;
activity.Text = message.PostBack.Payload;
}
return activity;
}
/// <summary>
/// Extracts attachments from a Facebook message.
/// </summary>
/// <param name="message">The message to get attachments from.</param>
/// <returns>A List of the attachments contained within the message.</returns>
public static List<Attachment> HandleMessageAttachments(Message message)
{
var attachmentsList = new List<Attachment>();
foreach (var facebookAttachment in message.Attachments)
{
var attachment = new Attachment
{
Content = facebookAttachment.Payload,
ContentType = facebookAttachment.Type,
};
attachmentsList.Add(attachment);
}
return attachmentsList;
}
/// <summary>
/// Writes an HTTP response payload.
/// </summary>
/// <param name="response">The HTTP response to write to.</param>
/// <param name="code">The status code to apply.</param>
/// <param name="text">The text to be written.</param>
/// <param name="encoding">The encoding for the text.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <exception cref="ArgumentNullException"><paramref name="response"/>, <paramref name="text"/>,
/// or <paramref name="encoding"/> is null.</exception>
public static async Task WriteAsync(HttpResponse response, HttpStatusCode code, string text, Encoding encoding, CancellationToken cancellationToken)
{
if (response == null)
{
throw new ArgumentNullException(nameof(response));
}
if (text == null)
{
throw new ArgumentNullException(nameof(text));
}
if (encoding == null)
{
throw new ArgumentNullException(nameof(encoding));
}
response.ContentType = "text/plain";
response.StatusCode = (int)code;
var data = encoding.GetBytes(text);
await response.Body.WriteAsync(data, 0, data.Length, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Generates an activity that displays a typing indicator.
/// </summary>
/// <param name="recipientId">The ID of the recipient of the message.</param>
/// <returns>An activity with sender_action equal to "typing_on".</returns>
public static Activity GenerateTypingActivity(string recipientId)
{
var activity = new Activity()
{
ChannelId = "facebook",
Conversation = new ConversationAccount()
{
Id = recipientId,
},
ChannelData = new FacebookMessage(recipientId, null, string.Empty),
Type = ActivityTypes.Message,
Text = null,
};
// we need only the sender action (and the recipient id) to be present in the message
var message = activity.GetChannelData<FacebookMessage>();
message.SenderAction = "typing_on";
message.MessagingType = null;
message.Sender = null;
return activity;
}
}
}

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

@ -1,153 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents;
using Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents.Handover;
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook
{
/// <summary>
/// Represents information associated with a Facebook webhook event. For more information, see the Facebook
/// [Webhook Events Reference](https://developers.facebook.com/docs/messenger-platform/reference/webhook-events).
/// </summary>
public class FacebookMessage
{
/// <summary>
/// Initializes a new instance of the <see cref="FacebookMessage"/> class.
/// </summary>
/// <param name="recipientId">Contents of the recipient ID field.</param>
/// <param name="message">Contents of the message field.</param>
/// <param name="messagingType">The type of webhook event. For more information, see the Facebook
/// [List of Webhook Events](https://developers.facebook.com/docs/messenger-platform/reference/webhook-events#event_list).</param>
/// <param name="tag">The optional message tag string. See https://developers.facebook.com/docs/messenger-platform/send-messages/message-tags.</param>
/// <param name="notificationType">The optional notification type: REGULAR (default value), SILENT_PUSH, NO_PUSH.</param>
/// <param name="personaId">The persona ID.</param>
/// <param name="senderAction">Message state to display to the user: typing_on, typing_off, mark_seen. Cannot be sent with 'message'. When used, 'recipient' should be the only other property set in the request.</param>
/// <param name="senderId">The sender ID.</param>
public FacebookMessage(string recipientId, Message message, string messagingType, string tag = null, string notificationType = null, string personaId = null, string senderAction = null, string senderId = null)
{
Recipient.Id = recipientId;
Message = message;
MessagingType = messagingType;
Tag = tag;
NotificationType = notificationType;
PersonaId = personaId;
SenderAction = senderAction;
Sender.Id = senderId;
}
/// <summary>
/// Gets or sets the ID of the recipient.
/// </summary>
/// <value>The ID of the recipient.</value>
[JsonProperty(PropertyName = "recipient")]
public FacebookBotUser Recipient { get; set; } = new FacebookBotUser();
/// <summary>
/// Gets or sets the ID of the sender.
/// </summary>
/// <value>The ID of the sender.</value>
[JsonProperty(PropertyName = "sender")]
public FacebookBotUser Sender { get; set; } = new FacebookBotUser();
/// <summary>
/// Gets or sets the message to be sent.
/// </summary>
/// <value>The message.</value>
[JsonProperty(PropertyName = "message")]
public Message Message { get; set; }
/// <summary>
/// Gets or sets the messaging type.
/// </summary>
/// <value>The messaging type.</value>
[JsonProperty(PropertyName = "messaging_type")]
public string MessagingType { get; set; }
/// <summary>
/// Gets or sets a tag to the message.
/// </summary>
/// <value>The tag.</value>
[JsonProperty(PropertyName = "tag")]
public string Tag { get; set; }
/// <summary>
/// Gets or sets the notification type.
/// </summary>
/// <value>The notification type.</value>
[JsonProperty(PropertyName = "notification_type")]
public string NotificationType { get; set; }
/// <summary>
/// Gets or sets the persona ID.
/// </summary>
/// <value>The persona ID.</value>
[JsonProperty(PropertyName = "persona_id")]
public string PersonaId { get; set; }
/// <summary>
/// Gets or sets the sender action.
/// </summary>
/// <value>The sender action (typing_on, typing_off, mark_seen).</value>
[JsonProperty(PropertyName = "sender_action")]
public string SenderAction { get; set; }
/// <summary>
/// Gets or sets the time-stamp.
/// </summary>
/// <value>Time-stamp.</value>
[JsonProperty(PropertyName = "timestamp")]
public long TimeStamp { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the message was received while in Standby mode.
/// </summary>
/// <value>Value indicating whether the message was received while in Standby mode.</value>
[JsonIgnore]
public bool IsStandby { get; set; }
/// <summary>
/// Gets or sets the value of the postback property.
/// </summary>
/// <value>The postback payload. A postback occurs when a postback button, **Get Started** button, or persistent menu item is tapped.</value>
[JsonProperty(PropertyName = "postback")]
public FacebookPostBack PostBack { get; set; }
/// <summary>
/// Gets or sets the value of the optin property.
/// </summary>
/// <value>The optin field. See https://developers.facebook.com/docs/messenger-platform/reference/webhook-events/messaging_optins. </value>
[JsonProperty(PropertyName = "optin")]
public FacebookRecipient OptIn { get; set; }
/// <summary>
/// Gets or sets the contents of the pass_thread_control property.
/// </summary>
/// <value>A <see cref="FacebookPassThreadControl"/> holding the contents of the pass_thread_control property.</value>.
[JsonProperty(PropertyName = "pass_thread_control")]
public FacebookPassThreadControl PassThreadControl { get; set; }
/// <summary>
/// Gets or sets the contents of the take_thread_control property.
/// </summary>
/// <value>A <see cref="FacebookTakeThreadControl"/> holding the contents of the pass_thread_control property.</value>.
[JsonProperty(PropertyName = "take_thread_control")]
public FacebookTakeThreadControl TakeThreadControl { get; set; }
/// <summary>
/// Gets or sets the contents of the request_thread_control property.
/// </summary>
/// <value>A <see cref="FacebookRequestThreadControl"/> holding the contents of the pass_thread_control property.</value>.
[JsonProperty(PropertyName = "request_thread_control")]
public FacebookRequestThreadControl RequestThreadControl { get; set; }
/// <summary>
/// Gets or sets the contents of the message_reads property.
/// </summary>
/// <value>A <see cref="FacebookRead"/> holding the contents of the message_reads property.
/// See https://developers.facebook.com/docs/messenger-platform/reference/webhook-events/message-reads. </value>.
[JsonProperty(PropertyName = "read")]
public FacebookRead Reads { get; set; }
}
}

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

@ -1,98 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections.Generic;
using Microsoft.Bot.Builder.Adapters.Facebook.FacebookEvents;
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Facebook
{
/// <summary>
/// Facebook message object used when sending messages via the Facebook API.
/// </summary>
public class Message
{
/// <summary>
/// Gets or sets the text of the message.
/// </summary>
/// <value>The text of the message.</value>
[JsonProperty(PropertyName = "text")]
public string Text { get; set; }
/// <summary>
/// Gets or sets the sticker ID.
/// </summary>
/// <value>The sticker ID.</value>
[JsonProperty(PropertyName = "sticker_id")]
public string StickerId { get; set; }
/// <summary>
/// Gets a list of attachments.
/// </summary>
/// <value>Attachments that could come with a Facebook message.</value>
[JsonProperty(PropertyName = "attachments")]
public List<FacebookAttachment> Attachments { get; } = new List<FacebookAttachment>();
/// <summary>
/// Gets or sets the attachment.
/// </summary>
/// <value>Single attachment that will be sent back to Facebook.</value>
[JsonProperty(PropertyName = "attachment")]
public FacebookAttachment Attachment { get; set; }
/// <summary>
/// Gets or sets the metadata.
/// </summary>
/// <value>Custom string that is delivered as a message echo. 1000 character limit.</value>
[JsonProperty(PropertyName = "metadata")]
public string Metadata { get; set; }
/// <summary>
/// Gets the quick replies.
/// </summary>
/// <value>The quick replies array.</value>
[JsonProperty(PropertyName = "quick_replies")]
public List<FacebookQuickReply> QuickReplies { get; } = new List<FacebookQuickReply>();
/// <summary>
/// Gets or sets a value indicating whether the message was sent from the page itself.
/// </summary>
/// <value>`true` if the message was sent from the page itself; otherwise, `false`.</value>
[JsonProperty(PropertyName = "is_echo")]
public bool IsEcho { get; set; }
/// <summary>
/// Gets or sets the message ID.
/// </summary>
/// <value>The message ID.</value>
[JsonProperty(PropertyName = "mid")]
public string Mid { get; set; }
/// <summary>
/// Newtonsoft JSON method for conditionally serializing the <see cref="QuickReplies"/> property.
/// </summary>
/// <returns>`true` to serialize the property; otherwise, `false`.</returns>
public bool ShouldSerializeQuickReplies()
{
return QuickReplies.Count > 0;
}
/// <summary>
/// Newtonsoft JSON method for conditionally serializing the <see cref="IsEcho"/> property.
/// </summary>
/// <returns>`true` to serialize the property; otherwise, `false`.</returns>
public bool ShouldSerializeIsEcho()
{
return IsEcho;
}
/// <summary>
/// Newtonsoft JSON method for conditionally serializing the <see cref="Attachments"/> property.
/// </summary>
/// <returns>`true` to serialize the property; otherwise, `false`.</returns>
public bool ShouldSerializeAttachments()
{
return Attachments.Count > 0;
}
}
}

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

@ -1,32 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version Condition=" '$(IsBuildServer)' == '' ">$(LocalPackageVersion)</Version>
<Version Condition=" '$(IsBuildServer)' != '' ">$(ReleasePackageVersion)</Version>
<PackageVersion Condition=" '$(IsBuildServer)' == '' ">$(LocalPackageVersion)</PackageVersion>
<PackageVersion Condition=" '$(IsBuildServer)' != '' ">$(ReleasePackageVersion)</PackageVersion>
<Configurations>Debug;Release</Configurations>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\Microsoft.Bot.Builder.Adapters.Facebook.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Description>Library for connecting bots with Facebook.</Description>
<Summary>This library implements C# classes for the Facebook adapter.</Summary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>Full</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Condition=" '$(IsBuildServer)' == '' " Version="$(LocalPackageVersion)" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Condition=" '$(IsBuildServer)' != '' " Version="$(ReleasePackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\integration\Microsoft.Bot.Builder.Integration.AspNet.Core\Microsoft.Bot.Builder.Integration.AspNet.Core.csproj" />
</ItemGroup>
</Project>

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

@ -1,65 +0,0 @@
# Microsoft Bot Framework Facebook Adapter for .NET
This package contains an adapter that communicates directly with the Facebook API, and translates messages to and from a standard format used by your bot.
<br>Includes support for Facebook Handover Protocol.
## How to Install
````
PM> Install-Package Microsoft.Bot.Builder.Adapters.Facebook
````
## How to Use
### Set the Facebook Credentials
To authenticate the requests, you'll need to configure the Adapter with the Verify Token, the App Secret and a Access Token.
You could create in the project an `appsettings.json` file to set the Facebook credentials as follows:
```json
{
"FacebookVerifyToken": "",
"FacebookAppSecret": "",
"FacebookAccessToken": ""
}
```
### Use FacebookAdapter in your App
FacebookAdapter provides a translation layer for BotBuilder so that bot developers can connect to Facebook and have access to the Facebook API.
To add the Facebook Adapter to a bot, for example, an `EchoBot`, in the `Startup` class you should add:
```C#
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
// Create the Bot Framework Facebook Adapter.
services.AddSingleton<IBotFrameworkHttpAdapter, FacebookAdapter>();
// Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
services.AddTransient<IBot, EchoBot>();
}
```
## Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.microsoft.com.
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
## License
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the [MIT](https://github.com/Microsoft/vscode/blob/master/LICENSE.txt) License.

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 1.9 KiB

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

@ -1,29 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Microsoft.Bot.Schema;
namespace Microsoft.Bot.Builder.Adapters.Slack
{
/// <summary>
/// Extends ResourceResponse with ActivityId and Conversation properties.
/// </summary>
public class ActivityResourceResponse : ResourceResponse
{
/// <summary>
/// Gets or sets the Activity Id.
/// </summary>
/// <value>
/// The Activity Id.
/// </value>
public string ActivityId { get; set; }
/// <summary>
/// Gets or sets a Conversation Account.
/// </summary>
/// <value>
/// Conversation Account.
/// </value>
public ConversationAccount Conversation { get; set; }
}
}

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

@ -1,40 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version Condition=" '$(IsBuildServer)' == '' ">$(LocalPackageVersion)</Version>
<Version Condition=" '$(IsBuildServer)' != '' ">$(ReleasePackageVersion)</Version>
<PackageVersion Condition=" '$(IsBuildServer)' == '' ">$(LocalPackageVersion)</PackageVersion>
<PackageVersion Condition=" '$(IsBuildServer)' != '' ">$(ReleasePackageVersion)</PackageVersion>
<Configurations>Debug;Release;</Configurations>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\Microsoft.Bot.Builder.Adapters.Slack.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Description>Library for connecting bots with Slack.</Description>
<Summary>This library implements C# classes for the Slack adapter.</Summary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>Full</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<PropertyGroup>
<!-- CS8002: The SlackAPI package isn't signed, so supress the warning. There seems to not be a way to supress this for ONLY SlackAPI. -->
<!-- CS1591: These documentation warnings excludes should be removed as part of https://github.com/microsoft/botbuilder-dotnet/issues/4052 -->
<NoWarn>$(NoWarn);CS8002;CS1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="SlackAPI" Version="1.1.2" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Condition=" '$(IsBuildServer)' == '' " Version="$(LocalPackageVersion)" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Condition=" '$(IsBuildServer)' != '' " Version="$(ReleasePackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\integration\Microsoft.Bot.Builder.Integration.AspNet.Core\Microsoft.Bot.Builder.Integration.AspNet.Core.csproj" />
</ItemGroup>
</Project>

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

@ -1,43 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Bot.Builder.Adapters.Slack.Model
{
/// <summary>
/// Represents a Slack Command request https://api.slack.com/interactivity/slash-commands.
/// </summary>
public class CommandPayload
{
[JsonProperty(PropertyName = "token")]
public string Token { get; set; }
[JsonProperty(PropertyName = "team_id")]
public string TeamId { get; set; }
[JsonProperty(PropertyName = "trigger_id")]
public string TriggerId { get; set; }
[JsonProperty(PropertyName = "channel_id")]
public string ChannelId { get; set; }
[JsonProperty(PropertyName = "user_id")]
public string UserId { get; set; }
[JsonProperty(PropertyName = "text")]
public string Text { get; set; }
[JsonProperty(PropertyName = "command")]
public string Command { get; set; }
[JsonProperty(PropertyName = "response_url")]
public Uri ResponseUrl { get; set; }
[JsonExtensionData(ReadData = true, WriteData = true)]
public IDictionary<string, JToken> AdditionalProperties { get; } = new Dictionary<string, JToken>();
}
}

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

@ -1,42 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Bot.Builder.Adapters.Slack.Model
{
/// <summary>
/// Represents an incoming request from the Event API https://api.slack.com/events-api#receiving_events.
/// </summary>
public class EventRequest
{
[JsonProperty(PropertyName = "token")]
public string Token { get; set; }
[JsonProperty(PropertyName = "team_id")]
public string TeamId { get; set; }
[JsonProperty(PropertyName = "api_app_id")]
public string ApiAppId { get; set; }
[JsonProperty(PropertyName = "event")]
public EventType Event { get; set; }
[JsonProperty(PropertyName = "type")]
public string Type { get; set; }
[JsonProperty(PropertyName = "authed_users")]
public List<string> AuthedUsers { get; } = new List<string>();
[JsonProperty(PropertyName = "event_id")]
public string EventId { get; set; }
[JsonProperty(PropertyName = "event_time")]
public string EventTime { get; set; }
[JsonExtensionData(ReadData = true, WriteData = true)]
public IDictionary<string, JToken> AdditionalProperties { get; } = new Dictionary<string, JToken>();
}
}

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

@ -1,41 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Bot.Builder.Adapters.Slack.Model
{
/// <summary>
/// Represents a Slack Event Type object https://api.slack.com/events-api#receiving_events.
/// </summary>
public class EventType
{
public string Type { get; set; }
[JsonProperty(PropertyName = "user")]
public string User { get; set; }
public string Ts { get; set; }
public string Item { get; set; }
[JsonProperty(PropertyName = "event_ts")]
public string EventTs { get; set; }
public string Channel { get; set; }
[JsonProperty(PropertyName = "channel_id")]
public string ChannelId { get; set; }
[JsonProperty(PropertyName = "bot_id")]
public string BotId { get; set; }
[JsonProperty(PropertyName = "thread_ts")]
public string ThreadTs { get; set; }
[JsonExtensionData(ReadData = true, WriteData = true)]
public IDictionary<string, JToken> AdditionalProperties { get; } = new Dictionary<string, JToken>();
}
}

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

@ -1,22 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Bot.Builder.Adapters.Slack.Model.Events
{
/// <summary>
/// Represents a Slack Message event https://api.slack.com/events/message.
/// </summary>
public class MessageEvent : EventType
{
public string Text { get; set; }
[JsonProperty(PropertyName = "channel_type")]
public string ChannelType { get; set; }
public string SubType { get; set; }
}
}

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

@ -1,17 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Bot.Builder.Adapters.Slack.Model.Events
{
/// <summary>
/// Represents a Slack Url Verification event https://api.slack.com/events/url_verification.
/// </summary>
public class UrlVerificationEvent
{
public string Type { get; set; }
public string Challenge { get; set; }
public string Token { get; set; }
}
}

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

@ -1,66 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using Microsoft.Bot.Builder.Adapters.Slack.Model.Events;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SlackAPI;
namespace Microsoft.Bot.Builder.Adapters.Slack.Model
{
/// <summary>
/// Union class to represent various user interaction payloads https://api.slack.com/interactivity/handling#payloads.
/// </summary>
public class InteractionPayload
{
[JsonProperty(PropertyName = "type")]
public string Type { get; set; }
[JsonProperty(PropertyName = "token")]
public string Token { get; set; }
[JsonProperty(PropertyName = "channel")]
public Channel Channel { get; set; }
[JsonProperty(PropertyName = "thread_ts")]
public string ThreadTs { get; set; }
[JsonProperty(PropertyName = "team")]
public Team Team { get; set; }
[JsonProperty(PropertyName = "message")]
public MessageEvent Message { get; set; }
[JsonProperty(PropertyName = "user")]
public User User { get; set; }
[JsonProperty(PropertyName = "actions")]
public List<SlackAction> Actions { get; } = new List<SlackAction>();
[JsonProperty(PropertyName = "trigger_id")]
public string TriggerId { get; set; }
[JsonProperty(PropertyName = "action_ts")]
public string ActionTs { get; set; }
[JsonProperty(PropertyName = "submission")]
public JObject Submission { get; } = new JObject();
[JsonProperty(PropertyName = "callback_id")]
public string CallbackId { get; set; }
[JsonProperty(PropertyName = "state")]
public string State { get; set; }
[JsonProperty(PropertyName = "response_url")]
public Uri ResponseUrl { get; set; }
[JsonProperty(PropertyName = "view")]
public ModalView View { get; set; }
[JsonExtensionData(ReadData = true, WriteData = true)]
public IDictionary<string, JToken> AdditionalProperties { get; } = new Dictionary<string, JToken>();
}
}

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

@ -1,23 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Slack.Model
{
public class ModalBlock
{
[JsonProperty(PropertyName = "type")]
public string Type { get; set; }
[JsonProperty(PropertyName = "value")]
public string Value { get; set; }
[JsonProperty(PropertyName = "selected_option")]
public SelectOption SelectedOption { get; set; }
[JsonProperty(PropertyName = "selected_options")]
public List<SelectOption> SelectedOptions { get; } = new List<SelectOption>();
}
}

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

@ -1,22 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Slack.Model
{
public class ModalView
{
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "state")]
public ModelViewState State { get; set; }
[JsonProperty(PropertyName = "callback_id")]
public string ModalIndentifier { get; set; }
[JsonProperty(PropertyName = "private_metadata")]
public string PrivateMetadata { get; set; }
}
}

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

@ -1,14 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Slack.Model
{
public class ModelViewState
{
[JsonProperty(PropertyName = "values")]
public Dictionary<string, Dictionary<string, ModalBlock>> Values { get; } = new Dictionary<string, Dictionary<string, ModalBlock>>();
}
}

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

@ -1,74 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections.Generic;
namespace Microsoft.Bot.Builder.Adapters.Slack.Model
{
/// <summary>
/// Message to send to Slack.
/// </summary>
public class NewSlackMessage
{
/// <summary>
/// Gets or Sets the message as ephemeral, it means, only the recipient can see it.
/// </summary>
/// <value>The ephemeral indicator of the message.</value>
public string Ephemeral { get; set; }
/// <summary>
/// Gets or Sets the timestamp of the thread.
/// </summary>
/// <value>Provide another message's timestamp value to make this message a reply.</value>
public string ThreadTs { get; set; }
/// <summary>
/// Gets or sets the user.
/// </summary>
/// <value>The user who sent the message.</value>
public string User { get; set; }
/// <summary>
/// Gets or sets the channel.
/// </summary>
/// <value>The channel, private group, or IM channel to send message to.</value>
public string Channel { get; set; }
/// <summary>
/// Gets or sets the text.
/// </summary>
/// <value>The text of the message.</value>
public string Text { get; set; }
/// <summary>
/// Gets or sets the timestamp.
/// </summary>
/// <value>The timestamp for the message.</value>
public string Ts { get; set; }
/// <summary>
/// Gets or sets the username.
/// </summary>
/// <value>Your bot's user name. Must be used in conjunction with as_user set to false, otherwise ignored.</value>
public string Username { get; set; }
/// <summary>
/// Gets or sets the bot id.
/// </summary>
/// <value>The bot id.</value>
public string BotId { get; set; }
/// <summary>
/// Gets or sets the blocks that could come with the message.
/// </summary>
/// <value>The blocks that could come with the message.</value>
public object Blocks { get; set; }
/// <summary>
/// Gets or sets the attachments that could come with the message.
/// </summary>
/// <value>The attachments that could come with the message.</value>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "it needs to be set in ActivityToSlack method")]
public List<SlackAttachment> Attachments { get; set; } = new List<SlackAttachment>();
}
}

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

@ -1,16 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Slack.Model
{
public class SelectOption
{
[JsonProperty(PropertyName = "text")]
public object Text { get; set; }
[JsonProperty(PropertyName = "value")]
public string Value { get; set; }
}
}

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

@ -1,38 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Slack.Model
{
public class SlackAction
{
[JsonProperty(PropertyName = "action_id")]
public string ActionId { get; set; }
[JsonProperty(PropertyName = "block_id")]
public string BlockId { get; set; }
[JsonProperty(PropertyName = "text")]
public object Text { get; set; }
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "value")]
public string Value { get; set; }
[JsonProperty(PropertyName = "type")]
public string Type { get; set; }
[JsonProperty(PropertyName = "action_ts")]
public string ActionTs { get; set; }
[JsonProperty(PropertyName = "selected_option")]
public SelectOption SelectedOption { get; set; }
[JsonProperty(PropertyName = "selected_options")]
public List<SelectOption> SelectedOptions { get; } = new List<SelectOption>();
}
}

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

@ -1,58 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using SlackAPI;
namespace Microsoft.Bot.Builder.Adapters.Slack.Model
{
public class SlackAttachment
{
[JsonProperty(PropertyName = "callback_id")]
public string CallbackId { get; set; }
public string Footer { get; set; }
public List<AttachmentAction> Actions { get; } = new List<AttachmentAction>();
[JsonProperty(PropertyName = "mrkdwn_in")]
public List<string> MrkDwnIn { get; } = new List<string>();
[JsonProperty(PropertyName = "thumb_url")]
public Uri ThumbUrl { get; set; }
[JsonProperty(PropertyName = "image_url")]
public Uri ImageUrl { get; set; }
public List<IBlock> Blocks { get; } = new List<IBlock>();
public List<Field> Fields { get; } = new List<Field>();
[JsonProperty(PropertyName = "footer_icon")]
public string FooterIcon { get; set; }
public string Text { get; set; }
public string Title { get; set; }
[JsonProperty(PropertyName = "author_icon")]
public string AuthorIcon { get; set; }
[JsonProperty(PropertyName = "author_link")]
public string AuthorLink { get; set; }
[JsonProperty(PropertyName = "author_name")]
public string AuthorName { get; set; }
public string Pretext { get; set; }
public string Color { get; set; }
public string Fallback { get; set; }
[JsonProperty(PropertyName = "title_link")]
public string TitleLink { get; set; }
}
}

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

@ -1,37 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Microsoft.Bot.Builder.Adapters.Slack.Model.Events;
namespace Microsoft.Bot.Builder.Adapters.Slack.Model
{
/// <summary>
/// SlackResponse class.
/// </summary>
public class SlackResponse
{
/// <summary>
/// Gets or sets a value indicating whether the Ok status is true or false.
/// </summary>
/// <value>The ok status of the response.</value>
public bool Ok { get; set; }
/// <summary>
/// Gets or sets the Channel property.
/// </summary>
/// <value>The channel.</value>
public string Channel { get; set; }
/// <summary>
/// Gets or sets the Ts property.
/// </summary>
/// <value>The timestamp.</value>
public string Ts { get; set; }
/// <summary>
/// Gets or sets the Message property.
/// </summary>
/// <value>The message.</value>
public MessageEvent Message { get; set; }
}
}

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

@ -1,12 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Bot.Builder.Adapters.Slack.Model.Model
{
public class SlackTeam
{
public string Id { get; set; }
public string Domain { get; set; }
}
}

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

@ -1,17 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Slack.Model
{
public class SlackUser
{
public string Id { get; set; }
public string Username { get; set; }
[JsonProperty(PropertyName = "team_id")]
public string TeamId { get; set; }
}
}

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

@ -1,64 +0,0 @@
# Microsoft Bot Framework SlackAdapter for .NET
This package contains an adapter that communicates directly with the Slack API, and translates messages to and from a standard format used by your bot.
## How to Install
````
PM> Install-Package Microsoft.Bot.Builder.Adapters.Slack
````
## How to Use
### Set the Slack Credentials
To authenticate the requests, you'll need to configure the Adapter with the Verification Token, the Bot Token and a Client Signing Secret.
You could create in the project an `appsettings.json` file to set the Slack credentials as follows:
```json
{
"SlackVerificationToken": "",
"SlackBotToken": "",
"SlackClientSigningSecret": ""
}
```
### Use SlackAdapter in your App
SlackAdapter provides a translation layer for BotBuilder so that bot developers can connect to Slack and have access to the Slack API.
To add the Slack Adapter to a bot, for example, an `EchoBot`, in the `Startup` class you should add:
```C#
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
// Create the Bot Framework Slack Adapter.
services.AddSingleton<IBotFrameworkHttpAdapter, SlackAdapter>();
// Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
services.AddTransient<IBot, EchoBot>();
}
```
## Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.microsoft.com.
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
## License
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the [MIT](https://github.com/Microsoft/vscode/blob/master/LICENSE.txt) License.

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

@ -1,353 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Security.Authentication;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Bot.Builder.Adapters.Slack.Model;
using Microsoft.Bot.Builder.Adapters.Slack.Model.Events;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Bot.Builder.Adapters.Slack
{
public class SlackAdapter : BotAdapter, IBotFrameworkHttpAdapter
{
private readonly SlackClientWrapper _slackClient;
private readonly ILogger _logger;
private readonly SlackAdapterOptions _options;
/// <summary>
/// Initializes a new instance of the <see cref="SlackAdapter"/> class using configuration settings.
/// </summary>
/// <param name="configuration">An <see cref="IConfiguration"/> instance.</param>
/// <remarks>
/// The configuration keys are:
/// SlackVerificationToken: A token for validating the origin of incoming webhooks.
/// SlackBotToken: A token for a bot to work on a single workspace.
/// SlackClientSigningSecret: The token used to validate that incoming webhooks are originated from Slack.
/// </remarks>
/// <param name="options">An instance of <see cref="SlackAdapterOptions"/>.</param>
/// <param name="logger">The ILogger implementation this adapter should use.</param>
public SlackAdapter(IConfiguration configuration, SlackAdapterOptions options = null, ILogger logger = null)
: this(new SlackClientWrapper(new SlackClientWrapperOptions(configuration["SlackVerificationToken"], configuration["SlackBotToken"], configuration["SlackClientSigningSecret"])), options, logger)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SlackAdapter"/> class.
/// Creates a Slack adapter.
/// </summary>
/// <param name="adapterOptions">The adapter options to be used when connecting to the Slack API.</param>
/// <param name="logger">The ILogger implementation this adapter should use.</param>
/// <param name="slackClient">The SlackClientWrapper used to connect to the Slack API.</param>
public SlackAdapter(SlackClientWrapper slackClient, SlackAdapterOptions adapterOptions, ILogger logger = null)
{
_slackClient = slackClient ?? throw new ArgumentNullException(nameof(adapterOptions));
_logger = logger ?? NullLogger.Instance;
_options = adapterOptions ?? new SlackAdapterOptions();
}
/// <summary>
/// Standard BotBuilder adapter method to send a message from the bot to the messaging API.
/// </summary>
/// <param name="turnContext">A TurnContext representing the current incoming message and environment.</param>
/// <param name="activities">An array of outgoing activities to be sent back to the messaging API.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>An array of <see cref="ResourceResponse"/> objects containing the IDs that Slack assigned to the sent messages.</returns>
public override async Task<ResourceResponse[]> SendActivitiesAsync(ITurnContext turnContext, Activity[] activities, CancellationToken cancellationToken)
{
if (turnContext == null)
{
throw new ArgumentNullException(nameof(turnContext));
}
if (activities == null)
{
throw new ArgumentNullException(nameof(activities));
}
var responses = new List<ResourceResponse>();
foreach (var activity in activities)
{
if (activity.Type != ActivityTypes.Message)
{
_logger.LogTrace($"Unsupported Activity Type: '{activity.Type}'. Only Activities of type 'Message' are supported.");
}
else
{
var message = SlackHelper.ActivityToSlack(activity);
var slackResponse = await _slackClient.PostMessageAsync(message, cancellationToken)
.ConfigureAwait(false);
if (slackResponse != null && slackResponse.Ok)
{
var resourceResponse = new ActivityResourceResponse()
{
Id = slackResponse.Ts,
ActivityId = slackResponse.Ts,
Conversation = new ConversationAccount() { Id = slackResponse.Channel, },
};
responses.Add(resourceResponse);
}
}
}
return responses.ToArray();
}
/// <summary>
/// Standard BotBuilder adapter method to update a previous message with new content.
/// </summary>
/// <param name="turnContext">A TurnContext representing the current incoming message and environment.</param>
/// <param name="activity">The updated activity in the form '{id: `id of activity to update`, ...}'.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>A resource response with the Id of the updated activity.</returns>
public override async Task<ResourceResponse> UpdateActivityAsync(ITurnContext turnContext, Activity activity, CancellationToken cancellationToken)
{
if (turnContext == null)
{
throw new ArgumentNullException(nameof(turnContext));
}
if (activity == null)
{
throw new ArgumentNullException(nameof(activity));
}
if (activity.Id == null)
{
throw new ArgumentException(nameof(activity.Timestamp));
}
if (activity.Conversation == null)
{
throw new ArgumentException(nameof(activity.ChannelId));
}
var message = SlackHelper.ActivityToSlack(activity);
var results = await _slackClient.UpdateAsync(message.Ts, message.Channel, message.Text, cancellationToken: cancellationToken).ConfigureAwait(false);
if (!results.Ok)
{
throw new InvalidOperationException($"Error updating activity on Slack:{results}");
}
return new ResourceResponse()
{
Id = activity.Id,
};
}
/// <summary>
/// Standard BotBuilder adapter method to delete a previous message.
/// </summary>
/// <param name="turnContext">A TurnContext representing the current incoming message and environment.</param>
/// <param name="reference">An object in the form "{activityId: `id of message to delete`, conversation: { id: `id of slack channel`}}".</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public override async Task DeleteActivityAsync(ITurnContext turnContext, ConversationReference reference, CancellationToken cancellationToken)
{
if (turnContext == null)
{
throw new ArgumentNullException(nameof(turnContext));
}
if (reference == null)
{
throw new ArgumentNullException(nameof(reference));
}
if (reference.ChannelId == null)
{
throw new ArgumentException(nameof(reference.ChannelId));
}
if (turnContext.Activity.Timestamp == null)
{
throw new ArgumentException(nameof(turnContext.Activity.Timestamp));
}
await _slackClient.DeleteMessageAsync(reference.ChannelId, turnContext.Activity.Timestamp.Value.DateTime, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Standard BotBuilder adapter method for continuing an existing conversation based on a conversation reference.
/// </summary>
/// <param name="reference">A conversation reference to be applied to future messages.</param>
/// <param name="logic">A bot logic function that will perform continuing action in the form 'async(context) => { ... }'.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
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);
}
}
/// <summary>
/// Sends a proactive message from the bot to a conversation.
/// </summary>
/// <param name="claimsIdentity">A <see cref="ClaimsIdentity"/> for the conversation.</param>
/// <param name="reference">A reference to the conversation to continue.</param>
/// <param name="callback">The method to call for the resulting bot turn.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>Call this method to proactively send a message to a conversation.
/// Most _channels require a user to initialize a conversation with a bot
/// before the bot can send activities to the user.
/// <para>This method registers the following services for the turn.<list type="bullet">
/// <item><description><see cref="IIdentity"/> (key = "BotIdentity"), a claims claimsIdentity for the bot.
/// </description></item>
/// </list></para>
/// </remarks>
/// <seealso cref="BotAdapter.RunPipelineAsync(ITurnContext, BotCallbackHandler, CancellationToken)"/>
public override async Task ContinueConversationAsync(ClaimsIdentity claimsIdentity, ConversationReference reference, BotCallbackHandler callback, CancellationToken cancellationToken)
{
using (var context = new TurnContext(this, reference.GetContinuationActivity()))
{
context.TurnState.Add<IIdentity>(BotIdentityKey, claimsIdentity);
context.TurnState.Add<BotCallbackHandler>(callback);
await RunPipelineAsync(context, callback, cancellationToken).ConfigureAwait(false);
}
}
/// <summary>
/// Accept an incoming webhook request and convert it into a TurnContext which can be processed by the bot's logic.
/// </summary>
/// <param name="request">The incoming HTTP request.</param>
/// <param name="response">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 for the task.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task ProcessAsync(HttpRequest request, HttpResponse response, IBot bot, CancellationToken cancellationToken)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (response == null)
{
throw new ArgumentNullException(nameof(response));
}
if (bot == null)
{
throw new ArgumentNullException(nameof(bot));
}
string body;
Activity activity = null;
using (var sr = new StreamReader(request.Body))
{
body = await sr.ReadToEndAsync().ConfigureAwait(false);
}
if (_options.VerifyIncomingRequests && !_slackClient.VerifySignature(request, body))
{
const string text = "Rejected due to mismatched header signature";
await SlackHelper.WriteAsync(response, HttpStatusCode.Unauthorized, text, Encoding.UTF8, cancellationToken).ConfigureAwait(false);
throw new AuthenticationException(text);
}
var requestContentType = request.Headers["Content-Type"].ToString();
if (requestContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase))
{
var postValues = SlackHelper.QueryStringToDictionary(body);
if (postValues.ContainsKey("payload"))
{
var payload = JsonConvert.DeserializeObject<InteractionPayload>(postValues["payload"]);
activity = SlackHelper.PayloadToActivity(payload);
}
else if (postValues.ContainsKey("command"))
{
var serializedPayload = JsonConvert.SerializeObject(postValues);
var payload = JsonConvert.DeserializeObject<CommandPayload>(serializedPayload);
activity = SlackHelper.CommandToActivity(payload, _slackClient);
}
}
else if (requestContentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
{
var bodyObject = JObject.Parse(body);
if (bodyObject["type"]?.ToString() == "url_verification")
{
var verificationEvent = bodyObject.ToObject<UrlVerificationEvent>();
await SlackHelper.WriteAsync(response, HttpStatusCode.OK, verificationEvent.Challenge, Encoding.UTF8, cancellationToken).ConfigureAwait(false);
return;
}
if (!string.IsNullOrWhiteSpace(_slackClient.Options.SlackVerificationToken) && bodyObject["token"]?.ToString() != _slackClient.Options.SlackVerificationToken)
{
var text = $"Rejected due to mismatched verificationToken:{bodyObject["token"]}";
await SlackHelper.WriteAsync(response, HttpStatusCode.Forbidden, text, Encoding.UTF8, cancellationToken).ConfigureAwait(false);
throw new AuthenticationException(text);
}
if (bodyObject["type"]?.ToString() == "event_callback")
{
// this is an event api post
var eventRequest = bodyObject.ToObject<EventRequest>();
activity = SlackHelper.EventToActivity(eventRequest, _slackClient);
}
}
// As per official Slack API docs, some additional request types may be receieved that can be ignored
// but we should respond with a 200 status code
// https://api.slack.com/interactivity/slash-commands
if (activity == null)
{
await SlackHelper.WriteAsync(response, HttpStatusCode.OK, "Unable to transform request / payload into Activity. Possible unrecognized request type", Encoding.UTF8, cancellationToken).ConfigureAwait(false);
}
else
{
using (var context = new TurnContext(this, activity))
{
context.TurnState.Add("httpStatus", ((int)HttpStatusCode.OK).ToString(System.Globalization.CultureInfo.InvariantCulture));
await RunPipelineAsync(context, bot.OnTurnAsync, cancellationToken).ConfigureAwait(false);
var code = Convert.ToInt32(context.TurnState.Get<string>("httpStatus"), System.Globalization.CultureInfo.InvariantCulture);
var statusCode = (HttpStatusCode)code;
var text = context.TurnState.Get<object>("httpBody") != null ? context.TurnState.Get<object>("httpBody").ToString() : string.Empty;
await SlackHelper.WriteAsync(response, statusCode, text, Encoding.UTF8, cancellationToken).ConfigureAwait(false);
}
}
}
}
}

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

@ -1,19 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Bot.Builder.Adapters.Slack
{
/// <summary>
/// Class for defining implementation of the SlackAdapter Options.
/// </summary>
public class SlackAdapterOptions
{
/// <summary>
/// Gets or sets a value indicating whether the signatures of incoming requests should be verified.
/// </summary>
/// <value>
/// A value indicating whether the signatures of incoming requests should be verified.
/// </value>
public bool VerifyIncomingRequests { get; set; } = true;
}
}

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

@ -1,229 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Specialized;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Bot.Builder.Adapters.Slack.Model;
using Microsoft.Bot.Builder.Adapters.Slack.Model.Events;
using Microsoft.Bot.Schema;
using Newtonsoft.Json;
using SlackAPI;
using Attachment = SlackAPI.Attachment;
namespace Microsoft.Bot.Builder.Adapters.Slack
{
public class SlackClientWrapper
{
private const string PostMessageUrl = "https://slack.com/api/chat.postMessage";
private const string PostEphemeralMessageUrl = "https://slack.com/api/chat.postEphemeral";
private readonly SlackTaskClient _api;
/// <summary>
/// Initializes a new instance of the <see cref="SlackClientWrapper"/> class.
/// Creates a Slack client by supplying the access token.
/// </summary>
/// <param name="options">An object containing API credentials, a webhook verification token and other options.</param>
public SlackClientWrapper(SlackClientWrapperOptions options)
{
Options = options ?? throw new ArgumentNullException(nameof(options));
if (string.IsNullOrWhiteSpace(options.SlackVerificationToken) && string.IsNullOrWhiteSpace(options.SlackClientSigningSecret))
{
const string message = "****************************************************************************************" +
"* WARNING: Your bot is operating without recommended security mechanisms in place. *" +
"* Initialize your adapter with a clientSigningSecret parameter to enable *" +
"* verification that all incoming webhooks originate with Slack: *" +
"* *" +
"* var adapter = new SlackAdapter({clientSigningSecret: <my secret from slack>}); *" +
"* *" +
"****************************************************************************************" +
">> Slack docs: https://api.slack.com/docs/verifying-requests-from-slack";
throw new InvalidOperationException(message + Environment.NewLine + "Required: include a verificationToken or clientSigningSecret to verify incoming Events API webhooks");
}
_api = new SlackTaskClient(options.SlackBotToken);
LoginWithSlackAsync(default).Wait();
}
/// <summary>
/// Gets the <see cref="SlackClientWrapperOptions"/>.
/// </summary>
/// <value>
/// An object containing API credentials, a webhook verification token and other options.
/// </value>
public SlackClientWrapperOptions Options { get; }
/// <summary>
/// Gets the user identity.
/// </summary>
/// <value>
/// A string containing the user identity.
/// </value>
public string Identity { get; private set; }
/// <summary>
/// Wraps Slack API's DeleteMessageAsync method.
/// </summary>
/// <param name="channelId">The channel to delete the message from.</param>
/// <param name="ts">The timestamp of the message.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>A <see cref="DeletedResponse"/> representing the response to deleting the message.</returns>
public virtual async Task<DeletedResponse> DeleteMessageAsync(string channelId, DateTime ts, CancellationToken cancellationToken)
{
return await _api.DeleteMessageAsync(channelId, ts).ConfigureAwait(false);
}
/// <summary>
/// Wraps Slack API's TestAuthAsync method.
/// </summary>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>The user Id.</returns>
public virtual async Task<string> TestAuthAsync(CancellationToken cancellationToken)
{
var auth = await _api.TestAuthAsync().ConfigureAwait(false);
return auth.user_id;
}
/// <summary>
/// Wraps Slack API's UpdateAsync method.
/// </summary>
/// <param name="ts">The timestamp of the message.</param>
/// <param name="channelId">The channel to delete the message from.</param>
/// <param name="text">The text to update with.</param>
/// <param name="botName">The optional bot name.</param>
/// <param name="parse">Change how messages are treated.Defaults to 'none'. See https://api.slack.com/methods/chat.postMessage#formatting. </param>
/// <param name="linkNames">If to find and link channel names and username.</param>
/// <param name="attachments">The attachments, if any.</param>
/// <param name="asUser">If the message is being sent as user instead of as a bot.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>A <see cref="UpdateResponse"/> representing the response to the operation.</returns>
public virtual async Task<SlackResponse> UpdateAsync(string ts, string channelId, string text, string botName = null, string parse = null, bool linkNames = false, Attachment[] attachments = null, bool asUser = false, CancellationToken cancellationToken = default)
{
var updateResponse = await _api.UpdateAsync(ts, channelId, text, botName, parse, linkNames, attachments, asUser).ConfigureAwait(false);
return new SlackResponse()
{
Ok = updateResponse.ok,
Message = new MessageEvent()
{
User = updateResponse.message.user,
Type = updateResponse.message.type,
Text = updateResponse.message.text
},
Channel = updateResponse.channel,
Ts = updateResponse.ts
};
}
/// <summary>
/// Validates the local secret against the one obtained from the request header.
/// </summary>
/// <param name="request">The <see cref="HttpRequest"/> with the signature.</param>
/// <param name="body">The raw body of the request.</param>
/// <returns>The result of the comparison between the signature in the request and hashed secret.</returns>
public virtual bool VerifySignature(HttpRequest request, string body)
{
if (request == null || string.IsNullOrWhiteSpace(body))
{
return false;
}
var timestamp = request.Headers["X-Slack-Request-Timestamp"];
object[] signature = { "v0", timestamp.ToString(), body };
var baseString = string.Join(":", signature);
using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(Options.SlackClientSigningSecret)))
{
var hashArray = hmac.ComputeHash(Encoding.UTF8.GetBytes(baseString));
var hash = string.Concat("v0=", BitConverter.ToString(hashArray).Replace("-", string.Empty)).ToUpperInvariant();
var retrievedSignature = request.Headers["X-Slack-Signature"].ToString().ToUpperInvariant();
return hash == retrievedSignature;
}
}
/// <summary>
/// Posts a message to Slack.
/// </summary>
/// <param name="message">The message to be posted.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>The <see cref="SlackResponse"/> to the posting operation.</returns>
public virtual async Task<SlackResponse> PostMessageAsync(NewSlackMessage message, CancellationToken cancellationToken)
{
if (message == null)
{
return null;
}
var data = new NameValueCollection
{
["token"] = Options.SlackBotToken,
["channel"] = message.Channel,
["text"] = message.Text,
["thread_ts"] = message.ThreadTs,
};
if (message.Blocks != null)
{
data["blocks"] = JsonConvert.SerializeObject(message.Blocks, new JsonSerializerSettings()
{
NullValueHandling = NullValueHandling.Ignore,
});
}
byte[] response;
using (var client = new WebClient())
{
var url = !string.IsNullOrWhiteSpace(message.Ephemeral)
? PostEphemeralMessageUrl
: PostMessageUrl;
response = await client.UploadValuesTaskAsync(url, "POST", data).ConfigureAwait(false);
}
return JsonConvert.DeserializeObject<SlackResponse>(Encoding.UTF8.GetString(response));
}
/// <summary>
/// Get the bot user id associated with the team on which an incoming activity originated. This is used internally by the SlackMessageTypeMiddleware to identify direct_mention and mention events.
/// In single-team mode, this will pull the information from the Slack API at launch.
/// In multi-team mode, this will use the `getBotUserByTeam` method passed to the constructor to pull the information from a developer-defined source.
/// </summary>
/// <param name="activity">An Activity.</param>
/// <returns>The identity of the bot's user.</returns>
public virtual string GetBotUserIdentity(Activity activity)
{
return Identity;
}
/// <summary>
/// Manages the login to Slack with the given credentials.
/// </summary>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>A Task representing the asynchronous operation.</returns>
public async Task LoginWithSlackAsync(CancellationToken cancellationToken)
{
if (Options.SlackBotToken != null)
{
Identity = await TestAuthAsync(cancellationToken).ConfigureAwait(false);
}
else if (string.IsNullOrWhiteSpace(Options.SlackClientId) ||
string.IsNullOrWhiteSpace(Options.SlackClientSecret) ||
Options.SlackRedirectUri == null ||
Options.SlackScopes.Count == 0)
{
throw new InvalidOperationException("Missing Slack API credentials! Provide SlackClientId, SlackClientSecret, scopes and SlackRedirectUri as part of the SlackAdapter options.");
}
}
}
}

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

@ -1,72 +0,0 @@
// 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;
namespace Microsoft.Bot.Builder.Adapters.Slack
{
/// <summary>
/// Class for defining implementation of the SlackClientWrapperOptions Options.
/// </summary>
public class SlackClientWrapperOptions
{
/// <summary>
/// Initializes a new instance of the <see cref="SlackClientWrapperOptions"/> class.
/// </summary>
/// <param name="slackVerificationToken">A token for validating the origin of incoming webhooks.</param>
/// <param name="slackBotToken">A token for a bot to work on a single workspace.</param>
/// <param name="slackClientSigningSecret">The token used to validate that incoming webhooks are originated from Slack.</param>
public SlackClientWrapperOptions(string slackVerificationToken, string slackBotToken, string slackClientSigningSecret)
{
SlackVerificationToken = slackVerificationToken;
SlackBotToken = slackBotToken;
SlackClientSigningSecret = slackClientSigningSecret;
}
/// <summary>
/// Gets or Sets the token for validating the origin of incoming webhooks.
/// </summary>
/// <value>The verification token.</value>
public string SlackVerificationToken { get; set; }
/// <summary>
/// Gets or Sets a token used to validate that incoming webhooks are originated from Slack.
/// </summary>
/// <value>The Client Signing Secret.</value>
public string SlackClientSigningSecret { get; set; }
/// <summary>
/// Gets or Sets a token (provided by Slack) for a bot to work on a single workspace.
/// </summary>
/// <value>The Bot token.</value>
public string SlackBotToken { get; set; }
/// <summary>
/// Gets or Sets the oauth client id provided by Slack for multi-team apps.
/// </summary>
/// <value>The Client Id.</value>
public string SlackClientId { get; set; }
/// <summary>
/// Gets or Sets the oauth client secret provided by Slack for multi-team apps.
/// </summary>
/// <value>The Client Secret.</value>
public string SlackClientSecret { get; set; }
/// <summary>
/// Gets or Sets the URI users will be redirected to after an oauth flow. In most cases, should be `https://mydomain.com/install/auth`.
/// </summary>
/// <value>The Redirect URI.</value>
public Uri SlackRedirectUri { get; set; }
/// <summary>
/// Gets an array of scope names that are being requested during the oauth process. Must match the scopes defined at api.slack.com.
/// </summary>
/// <returns>The scopes array.</returns>
/// <value>An array of scope names that are being requested during the oauth process.</value>
public List<string> SlackScopes { get; } = new List<string>();
}
}

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

@ -1,334 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Bot.Builder.Adapters.Slack.Model;
using Microsoft.Bot.Builder.Adapters.Slack.Model.Events;
using Microsoft.Bot.Schema;
using Newtonsoft.Json.Linq;
#if SIGNASSEMBLY
[assembly: InternalsVisibleTo("Microsoft.Bot.Builder.Adapters.Slack.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
#else
[assembly: InternalsVisibleTo("Microsoft.Bot.Builder.Adapters.Slack.Tests")]
#endif
namespace Microsoft.Bot.Builder.Adapters.Slack
{
internal static class SlackHelper
{
/// <summary>
/// Formats a BotBuilder activity into an outgoing Slack message.
/// </summary>
/// <param name="activity">A BotBuilder Activity object.</param>
/// <returns>A Slack message object with {text, attachments, channel, thread ts} as well as any fields found in activity.channelData.</returns>
public static NewSlackMessage ActivityToSlack(Activity activity)
{
if (activity == null)
{
throw new ArgumentNullException(nameof(activity));
}
var message = new NewSlackMessage();
if (activity.Timestamp != null)
{
message.Ts = activity.Timestamp.Value.DateTime.ToString(CultureInfo.InvariantCulture);
}
message.Text = activity.Text;
if (activity.Attachments != null)
{
var attachments = new List<SlackAttachment>();
foreach (var att in activity.Attachments)
{
if (att.Name == "blocks")
{
message.Blocks = att.Content;
}
else
{
var newAttachment = new SlackAttachment()
{
AuthorName = att.Name,
ThumbUrl = new Uri(att.ThumbnailUrl),
};
attachments.Add(newAttachment);
}
}
if (attachments.Count > 0)
{
message.Attachments = attachments;
}
}
message.Channel = activity.Conversation.Id;
if (!string.IsNullOrWhiteSpace(activity.Conversation.Properties["thread_ts"]?.ToString()))
{
message.ThreadTs = activity.Conversation.Properties["thread_ts"].ToString();
}
// if channelData is specified, overwrite any fields in message object
if (activity.ChannelData != null)
{
message = activity.GetChannelData<NewSlackMessage>();
}
// should this message be sent as an ephemeral message
if (!string.IsNullOrWhiteSpace(message.Ephemeral))
{
message.User = activity.Recipient.Id;
}
return message;
}
/// <summary>
/// Writes the HttpResponse.
/// </summary>
/// <param name="response">The httpResponse.</param>
/// <param name="code">The status code to be written.</param>
/// <param name="text">The text to be written.</param>
/// <param name="encoding">The encoding for the text.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static async Task WriteAsync(HttpResponse response, HttpStatusCode code, string text, Encoding encoding, CancellationToken cancellationToken = default)
{
if (response == null)
{
throw new ArgumentNullException(nameof(response));
}
if (text == null)
{
throw new ArgumentNullException(nameof(text));
}
if (encoding == null)
{
throw new ArgumentNullException(nameof(encoding));
}
response.ContentType = "text/plain";
response.StatusCode = (int)code;
var data = encoding.GetBytes(text);
await response.Body.WriteAsync(data, 0, data.Length, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Creates an activity based on the slack event payload.
/// </summary>
/// <param name="slackPayload">The payload of the slack event.</param>
/// <returns>An activity containing the event data.</returns>
public static Activity PayloadToActivity(InteractionPayload slackPayload)
{
if (slackPayload == null)
{
throw new ArgumentNullException(nameof(slackPayload));
}
var activity = new Activity()
{
Timestamp = default,
ChannelId = "slack",
Conversation = new ConversationAccount()
{
Id = slackPayload.Channel.id,
},
From = new ChannelAccount()
{
Id = slackPayload.Message?.BotId ?? slackPayload.User.id,
},
Recipient = new ChannelAccount()
{
Id = null,
},
ChannelData = slackPayload,
Text = null,
Type = ActivityTypes.Event,
Value = slackPayload
};
if (slackPayload.ThreadTs != null)
{
activity.Conversation.Properties["thread_ts"] = slackPayload.ThreadTs;
}
if (slackPayload.Actions != null && slackPayload.Actions.Any())
{
var action = slackPayload.Actions[0];
switch (action.Type)
{
case "button":
activity.Text = action.Value;
break;
case "select":
activity.Text = slackPayload.Actions[0].SelectedOptions[0]?.Value ?? slackPayload.Actions[0].SelectedOption?.Value;
break;
case "static_select":
activity.Text = slackPayload.Actions[0].SelectedOption.Value;
break;
default:
break;
}
if (!string.IsNullOrEmpty(activity.Text))
{
activity.Type = ActivityTypes.Message;
}
}
return activity;
}
/// <summary>
/// Creates an activity based on the slack event data.
/// </summary>
/// <param name="eventRequest">The data of the slack event.</param>
/// <param name="client">The Slack client.</param>
/// <returns>An activity containing the event data.</returns>
public static Activity EventToActivity(EventRequest eventRequest, SlackClientWrapper client)
{
if (eventRequest == null)
{
throw new ArgumentNullException(nameof(eventRequest));
}
var innerEvent = eventRequest.Event;
var activity = new Activity
{
Id = innerEvent.EventTs,
Timestamp = default,
ChannelId = "slack",
Conversation =
new ConversationAccount()
{
Id = innerEvent.Channel ?? innerEvent.ChannelId ?? eventRequest.TeamId
},
From = new ChannelAccount()
{
Id = innerEvent.User ?? innerEvent.BotId ?? eventRequest.TeamId
},
ChannelData = eventRequest,
Type = ActivityTypes.Event
};
activity.Recipient = new ChannelAccount()
{
Id = client.GetBotUserIdentity(activity)
};
if (!string.IsNullOrEmpty(innerEvent.ThreadTs))
{
activity.Conversation.Properties["thread_ts"] = innerEvent.ThreadTs;
}
if (innerEvent.Type == "message" && innerEvent.BotId == null)
{
var message = JObject.FromObject(innerEvent).ToObject<MessageEvent>();
if (message.SubType == null)
{
activity.Type = ActivityTypes.Message;
activity.Text = message.Text;
}
activity.Conversation.Properties["channel_type"] = message.ChannelType;
activity.Value = innerEvent;
}
else
{
activity.Name = innerEvent.Type;
activity.Value = innerEvent;
}
return activity;
}
/// <summary>
/// Creates an activity based on a slack event related to a slash command.
/// </summary>
/// <param name="commandRequest">The data of the slack command request.</param>
/// <param name="client">The Slack client.</param>
/// <returns>An activity containing the event data.</returns>
public static Activity CommandToActivity(CommandPayload commandRequest, SlackClientWrapper client)
{
if (commandRequest == null)
{
throw new ArgumentNullException(nameof(commandRequest));
}
var activity = new Activity()
{
Id = commandRequest.TriggerId,
Timestamp = default,
ChannelId = "slack",
Conversation = new ConversationAccount()
{
Id = commandRequest.ChannelId,
},
From = new ChannelAccount()
{
Id = commandRequest.UserId,
},
ChannelData = commandRequest,
Type = ActivityTypes.Event,
Name = "Command",
Value = commandRequest.Command
};
activity.Recipient = new ChannelAccount()
{
Id = client.GetBotUserIdentity(activity)
};
activity.Conversation.Properties["team"] = commandRequest.TeamId;
return activity;
}
/// <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>
public 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;
}
}
}

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 1.9 KiB

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

@ -1,39 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version Condition=" '$(IsBuildServer)' == '' ">$(LocalPackageVersion)</Version>
<Version Condition=" '$(IsBuildServer)' != '' ">$(ReleasePackageVersion)</Version>
<PackageVersion Condition=" '$(IsBuildServer)' == '' ">$(LocalPackageVersion)</PackageVersion>
<PackageVersion Condition=" '$(IsBuildServer)' != '' ">$(ReleasePackageVersion)</PackageVersion>
<Configurations>Debug;Release</Configurations>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\Microsoft.Bot.Builder.Adapters.Twilio.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Description>Library for connecting bots with Twilio SMS API.</Description>
<Summary>This library implements C# classes for Twilio adapter.</Summary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>Full</DebugType>
<DebugSymbols>true</DebugSymbols>
</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>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Twilio" Version="5.37.2" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Condition=" '$(IsBuildServer)' == '' " Version="$(LocalPackageVersion)" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Condition=" '$(IsBuildServer)' != '' " Version="$(ReleasePackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\integration\Microsoft.Bot.Builder.Integration.AspNet.Core\Microsoft.Bot.Builder.Integration.AspNet.Core.csproj" />
</ItemGroup>
</Project>

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

@ -1,66 +0,0 @@
# Microsoft Bot Framework TwilioAdapter for .NET
This package contains an adapter that communicates directly with the Twilio API, and translates messages to and from a standard format used by your bot.
## How to Install
````
PM> Install-Package Microsoft.Bot.Builder.Adapters.Twilio
````
## How to Use
### Set the Twilio Credentials
To authenticate the requests, you'll need to configure the Adapter with the Twilio Number, the Account Sid, an Auth Token and a Validation Url.
You could create in the project an `appsettings.json` file to set the Twilio credentials as follows:
```json
{
"TwilioNumber": "",
"TwilioAccountSid": "",
"TwilioAuthToken": "",
"TwilioValidationUrl": ""
}
```
### Use TwilioAdapter in your App
TwilioAdapter provides a translation layer for BotBuilder so that bot developers can connect to Twilio SMS and have access to the Twilio API.
To add the Twilio Adapter to a bot, for example, an `EchoBot`, in the `Startup` class you should add:
```C#
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
// Create the Bot Framework Twilio Adapter.
services.AddSingleton<IBotFrameworkHttpAdapter, TwilioAdapter>();
// Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
services.AddTransient<IBot, EchoBot>();
}
```
## Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.microsoft.com.
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
## License
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the [MIT](https://github.com/Microsoft/vscode/blob/master/LICENSE.txt) License.

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

@ -1,259 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Security.Authentication;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
namespace Microsoft.Bot.Builder.Adapters.Twilio
{
/// <summary>
/// A <see cref="BotAdapter"/> that can connect to Twilio's SMS service.
/// </summary>
public class TwilioAdapter : BotAdapter, IBotFrameworkHttpAdapter
{
private readonly TwilioClientWrapper _twilioClient;
private readonly ILogger _logger;
private readonly TwilioAdapterOptions _options;
/// <summary>
/// Initializes a new instance of the <see cref="TwilioAdapter"/> class using configuration settings.
/// </summary>
/// <param name="configuration">An <see cref="IConfiguration"/> instance.</param>
/// <remarks>
/// The configuration keys are:
/// TwilioNumber: The phone number associated with the Twilio account.
/// TwilioAccountSid: The string identifier of the account. See https://www.twilio.com/docs/glossary/what-is-a-sid
/// TwilioAuthToken: The authentication token for the account.
/// TwilioValidationUrl: The validation URL for incoming requests.
/// </remarks>
/// <param name="adapterOptions">Options for the <see cref="TwilioAdapter"/>.</param>
/// <param name="logger">The ILogger implementation this adapter should use.</param>
public TwilioAdapter(IConfiguration configuration, TwilioAdapterOptions adapterOptions = null, ILogger logger = null)
: this(
new TwilioClientWrapper(new TwilioClientWrapperOptions(configuration["TwilioNumber"], configuration["TwilioAccountSid"], configuration["TwilioAuthToken"], new Uri(configuration["TwilioValidationUrl"]))), adapterOptions, logger)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TwilioAdapter"/> class.
/// </summary>
/// <param name="twilioClient">The Twilio client to connect to.</param>
/// <param name="adapterOptions">Options for the <see cref="TwilioAdapter"/>.</param>
/// <param name="logger">The ILogger implementation this adapter should use.</param>
public TwilioAdapter(TwilioClientWrapper twilioClient, TwilioAdapterOptions adapterOptions, ILogger logger = null)
{
_twilioClient = twilioClient ?? throw new ArgumentNullException(nameof(twilioClient));
_logger = logger ?? NullLogger.Instance;
_options = adapterOptions ?? new TwilioAdapterOptions();
}
/// <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)
{
_logger.LogTrace(
$"Unsupported Activity Type: '{activity.Type}'. Only Activities of type 'Message' are supported.");
}
else
{
var messageOptions = TwilioHelper.ActivityToTwilio(activity, _twilioClient.Options.TwilioNumber);
var res = await _twilioClient.SendMessageAsync(messageOptions, cancellationToken)
.ConfigureAwait(false);
var response = new ResourceResponse()
{
Id = res,
};
responses.Add(response);
}
}
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)
{
if (httpRequest == null)
{
throw new ArgumentNullException(nameof(httpRequest));
}
if (httpResponse == null)
{
throw new ArgumentNullException(nameof(httpResponse));
}
if (bot == null)
{
throw new ArgumentNullException(nameof(bot));
}
Dictionary<string, string> bodyDictionary;
using (var bodyStream = new StreamReader(httpRequest.Body))
{
bodyDictionary =
TwilioHelper.QueryStringToDictionary(await bodyStream.ReadToEndAsync().ConfigureAwait(false));
}
if (_options.ValidateIncomingRequests && !_twilioClient.ValidateSignature(httpRequest, bodyDictionary))
{
throw new AuthenticationException("WARNING: Webhook received message with invalid signature. Potential malicious behavior!");
}
var activity = TwilioHelper.PayloadToActivity(bodyDictionary);
// 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);
var statusCode = Convert.ToInt32(context.TurnState.Get<string>("httpStatus"), CultureInfo.InvariantCulture);
var text = context.TurnState.Get<object>("httpBody") != null
? context.TurnState.Get<object>("httpBody").ToString()
: string.Empty;
await TwilioHelper.WriteAsync(httpResponse, statusCode, text, Encoding.UTF8, 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)
{
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)
{
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);
}
}
/// <summary>
/// Sends a proactive message from the bot to a conversation.
/// </summary>
/// <param name="claimsIdentity">A <see cref="ClaimsIdentity"/> for the conversation.</param>
/// <param name="reference">A reference to the conversation to continue.</param>
/// <param name="callback">The method to call for the resulting bot turn.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>Call this method to proactively send a message to a conversation.
/// Most _channels require a user to initialize a conversation with a bot
/// before the bot can send activities to the user.
/// <para>This method registers the following services for the turn.<list type="bullet">
/// <item><description><see cref="IIdentity"/> (key = "BotIdentity"), a claims claimsIdentity for the bot.
/// </description></item>
/// </list></para>
/// </remarks>
/// <seealso cref="BotAdapter.RunPipelineAsync(ITurnContext, BotCallbackHandler, CancellationToken)"/>
public override async Task ContinueConversationAsync(ClaimsIdentity claimsIdentity, ConversationReference reference, BotCallbackHandler callback, CancellationToken cancellationToken)
{
using (var context = new TurnContext(this, reference.GetContinuationActivity()))
{
context.TurnState.Add<IIdentity>(BotIdentityKey, claimsIdentity);
context.TurnState.Add<BotCallbackHandler>(callback);
await RunPipelineAsync(context, callback, cancellationToken).ConfigureAwait(false);
}
}
}
}

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

@ -1,19 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace Microsoft.Bot.Builder.Adapters.Twilio
{
/// <summary>
/// Options for the <see cref="TwilioAdapter"/>.
/// </summary>
public class TwilioAdapterOptions
{
/// <summary>
/// Gets or sets a value indicating whether incoming requests should be validated as coming from Twilio.
/// </summary>
/// <value>
/// A value indicating whether incoming requests should be validated as coming from Twilio.
/// </value>
public bool ValidateIncomingRequests { get; set; } = true;
}
}

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

@ -1,90 +0,0 @@
// 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.AspNetCore.Http;
using Twilio;
using Twilio.Rest.Api.V2010.Account;
using Twilio.Security;
namespace Microsoft.Bot.Builder.Adapters.Twilio
{
/// <summary>
/// Wrapper class for the Twilio API.
/// </summary>
public class TwilioClientWrapper
{
private const string TwilioSignature = "x-twilio-signature";
private const string TwilioHeader = "x-forwarded-proto";
/// <summary>
/// Initializes a new instance of the <see cref="TwilioClientWrapper"/> class.
/// </summary>
/// <param name="options">An object containing API credentials, a webhook verification token and other options.</param>
public TwilioClientWrapper(TwilioClientWrapperOptions options)
{
Options = options ?? throw new ArgumentNullException(nameof(options));
TwilioClient.Init(Options.TwilioAccountSid, Options.TwilioAuthToken);
}
/// <summary>
/// Gets the <see cref="TwilioClientWrapperOptions"/> for the wrapper.
/// </summary>
/// <value>
/// The <see cref="TwilioClientWrapperOptions"/> for the wrapper.
/// </value>
public TwilioClientWrapperOptions Options { get; }
/// <summary>
/// Sends a Twilio SMS message.
/// </summary>
/// <param name="messageOptions">An object containing the parameters for the message to send.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>The SID of the Twilio message sent.</returns>
public virtual async Task<string> SendMessageAsync(TwilioMessageOptions messageOptions, CancellationToken cancellationToken)
{
var createMessageOptions = new CreateMessageOptions(messageOptions.To)
{
ApplicationSid = messageOptions.ApplicationSid,
MediaUrl = messageOptions.MediaUrl,
Body = messageOptions.Body,
From = messageOptions.From
};
var messageResource = await MessageResource.CreateAsync(createMessageOptions).ConfigureAwait(false);
return messageResource.Sid;
}
/// <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>
/// <returns>The result of the comparison between the signature in the request and the hashed body.</returns>
public virtual bool ValidateSignature(HttpRequest httpRequest, Dictionary<string, string> body)
{
var urlString = Options.TwilioValidationUrl?.ToString();
var twilioSignature = httpRequest.Headers.ContainsKey(TwilioSignature)
? httpRequest.Headers[TwilioSignature].ToString()
: throw new Exception($"HttpRequest is missing \"{TwilioSignature}\"");
if (string.IsNullOrWhiteSpace(urlString))
{
urlString = httpRequest.Headers[TwilioHeader][0];
if (string.IsNullOrWhiteSpace(urlString))
{
urlString = $"{httpRequest.Protocol}://{httpRequest.Host + httpRequest.Path}";
}
}
var requestValidator = new RequestValidator(Options.TwilioAuthToken);
return requestValidator.Validate(urlString, body, twilioSignature);
}
}
}

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

@ -1,79 +0,0 @@
// Copyright(c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
namespace Microsoft.Bot.Builder.Adapters.Twilio
{
/// <summary>
/// Defines options for a <see cref="TwilioClientWrapper"/>.
/// </summary>
public class TwilioClientWrapperOptions
{
/// <summary>
/// Initializes a new instance of the <see cref="TwilioClientWrapperOptions"/> class.
/// </summary>
/// <param name="twilioNumber">The twilio phone number.</param>
/// <param name="twilioAccountSid">The account id.</param>
/// <param name="twilioAuthToken">The authentication token.</param>
/// <param name="twilioValidationUrl">The validation URL for incoming requests.</param>
public TwilioClientWrapperOptions(string twilioNumber, string twilioAccountSid, string twilioAuthToken, Uri twilioValidationUrl = null)
{
if (string.IsNullOrWhiteSpace(twilioNumber))
{
throw new ArgumentException($"The {nameof(twilioNumber)} property for {nameof(TwilioAdapterOptions)} can't be empty.", nameof(twilioNumber));
}
if (string.IsNullOrWhiteSpace(twilioAccountSid))
{
throw new ArgumentException($"The {nameof(twilioNumber)} property for {nameof(TwilioAdapterOptions)} can't be empty.", nameof(twilioAccountSid));
}
if (string.IsNullOrWhiteSpace(twilioAuthToken))
{
throw new ArgumentException($"The {nameof(twilioAuthToken)} property for {nameof(TwilioAdapterOptions)} can't be empty.", nameof(twilioAuthToken));
}
TwilioNumber = twilioNumber;
TwilioAccountSid = twilioAccountSid;
TwilioAuthToken = twilioAuthToken;
TwilioValidationUrl = twilioValidationUrl;
}
/// <summary>
/// Gets or sets the phone number associated with this Twilio app.
/// </summary>
/// <value>
/// The phone number.
/// </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 TwilioAccountSid { get; set; }
/// <summary>
/// Gets or sets the API auth token associated with the Twilio account.
/// </summary>
/// <value>The authentication token.</value>
public string TwilioAuthToken { 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 Uri TwilioValidationUrl { get; set; }
/// <summary>
/// Gets or sets a value indicating whether incoming requests are validated as being genuine requests from Twilio.
/// </summary>
/// <value>
/// A flag indicating whether incoming requests are validated as being genuine requests from Twilio.
/// </value>
public bool ValidateIncomingRequests { get; set; } = true;
}
}

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

@ -1,192 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Bot.Connector;
using Microsoft.Bot.Schema;
using Newtonsoft.Json;
#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 TwilioMessageOptions ActivityToTwilio(Activity activity, string twilioNumber)
{
if (activity == null)
{
throw new ArgumentNullException(nameof(activity));
}
if (string.IsNullOrWhiteSpace(twilioNumber))
{
throw new ArgumentNullException(nameof(twilioNumber));
}
var mediaUrls = new List<Uri>();
if (activity.Attachments != null)
{
mediaUrls.AddRange(activity.Attachments.Select(attachment => new Uri(attachment.ContentUrl)));
}
var messageOptions = new TwilioMessageOptions()
{
To = activity.Conversation.Id,
ApplicationSid = activity.Conversation.Id,
From = twilioNumber,
Body = activity.Text
};
messageOptions.MediaUrl.AddRange(mediaUrls);
return messageOptions;
}
/// <summary>
/// Writes the HttpResponse.
/// </summary>
/// <param name="response">The httpResponse.</param>
/// <param name="code">The status code to be written.</param>
/// <param name="text">The text to be written.</param>
/// <param name="encoding">The encoding for the text.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public static async Task WriteAsync(HttpResponse response, int code, string text, Encoding encoding, CancellationToken cancellationToken)
{
if (response == null)
{
throw new ArgumentNullException(nameof(response));
}
if (text == null)
{
throw new ArgumentNullException(nameof(text));
}
if (encoding == null)
{
throw new ArgumentNullException(nameof(encoding));
}
response.ContentType = "text/plain";
response.StatusCode = code;
var data = encoding.GetBytes(text);
await response.Body.WriteAsync(data, 0, data.Length, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Creates a Bot Framework <see cref="Activity"/> from an HTTP request that contains a Twilio message.
/// </summary>
/// <param name="payload">The HTTP request.</param>
/// <returns>The activity object.</returns>
public static Activity PayloadToActivity(Dictionary<string, string> payload)
{
if (payload == null)
{
throw new ArgumentNullException(nameof(payload));
}
var twilioMessage = JsonConvert.DeserializeObject<TwilioMessage>(JsonConvert.SerializeObject(payload));
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, payload) : null,
};
}
/// <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>
public 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>
public 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;
}
}
}

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

@ -1,159 +0,0 @@
// 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 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; } = new List<Uri>();
/// <summary>
/// Gets 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; } = new List<string>();
/// <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; }
}
}

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

@ -1,54 +0,0 @@
// 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>
/// Represents an outgoing message content and options for Twilio.
/// </summary>
public class TwilioMessageOptions
{
/// <summary>
/// Gets or sets the destination phone number.
/// </summary>
/// <value>
/// The destination phone number.
/// </value>
public string To { get; set; }
/// <summary>
/// Gets or sets the phone number that initiated the message.
/// </summary>
/// <value>
/// The phone number that initiated the message.
/// </value>
public string From { get; set; }
/// <summary>
/// Gets or sets the text of the message you want to send. Can be up to 1,600 characters in length.
/// </summary>
/// <value>
/// The text of the message you want to send. Can be up to 1,600 characters in length.
/// </value>
public string Body { get; set; }
/// <summary>
/// Gets the URL of the media to send with the message.
/// </summary>
/// <value>
/// The URL of the media to send with the message.
/// </value>
public List<Uri> MediaUrl { get; } = new List<Uri>();
/// <summary>
/// Gets or sets the application to use for callbacks.
/// </summary>
/// <value>
/// The application to use for callbacks.
/// </value>
public string ApplicationSid { get; set; }
}
}

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 1.9 KiB

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

@ -1,63 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections.Generic;
namespace Microsoft.Bot.Builder.Adapters.Webex
{
/// <summary>
/// Represents an Attachment Action - Users create attachment actions by interacting with
/// message attachments such as clicking on a submit button in a card.
/// https://developer.webex.com/docs/api/v1/attachment-actions.
/// </summary>
public class AttachmentActionData
{
/// <summary>
/// Gets or sets the unique identifier for the action.
/// </summary>
/// <value>
/// The unique identifier for the action.
/// </value>
public string Id { get; set; }
/// <summary>
/// Gets or sets the type of action performed.
/// </summary>
/// <value>
/// The type of action performed.
/// </value>
public string Type { get; set; }
/// <summary>
/// Gets or sets the parent message the attachment action was performed on.
/// </summary>
/// <value>
/// The parent message the attachment action was performed on.
/// </value>
public string MessageId { get; set; }
/// <summary>
/// Gets or sets the ID of the person who performed the action.
/// </summary>
/// <value>
/// The ID of the person who performed the action.
/// </value>
public string PersonId { get; set; }
/// <summary>
/// Gets or sets the date and time the action was created.
/// </summary>
/// <value>
/// The date and time the action was created.
/// </value>
public string Created { get; set; }
/// <summary>
/// Gets the action's inputs.
/// </summary>
/// <value>
/// The action's inputs.
/// </value>
public Dictionary<string, string> Inputs { get; } = new Dictionary<string, string>();
}
}

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

@ -1,38 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version Condition=" '$(IsBuildServer)' == '' ">$(LocalPackageVersion)</Version>
<Version Condition=" '$(IsBuildServer)' != '' ">$(ReleasePackageVersion)</Version>
<PackageVersion Condition=" '$(IsBuildServer)' == '' ">$(LocalPackageVersion)</PackageVersion>
<PackageVersion Condition=" '$(IsBuildServer)' != '' ">$(ReleasePackageVersion)</PackageVersion>
<Configurations>Debug;Release;</Configurations>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\Microsoft.Bot.Builder.Adapters.Webex.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Description>Library for connecting bots with Webex Teams API.</Description>
<Summary>This library implements C# classes for the Webex Adapter</Summary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>Full</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<PropertyGroup>
<!-- CS8002: The Thrzn41.WebexTeams package isn't signed, so supress the warning. There seems to not be a way to supress this for ONLY Thrzn41.WebexTeams. -->
<NoWarn>$(NoWarn);CS8002;</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Thrzn41.WebexTeams" Version="1.6.2" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Condition=" '$(IsBuildServer)' == '' " Version="$(LocalPackageVersion)" />
<PackageReference Include="Microsoft.Bot.Builder.Integration.AspNet.Core" Condition=" '$(IsBuildServer)' != '' " Version="$(ReleasePackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\integration\Microsoft.Bot.Builder.Integration.AspNet.Core\Microsoft.Bot.Builder.Integration.AspNet.Core.csproj" />
</ItemGroup>
</Project>

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

@ -1,67 +0,0 @@
# Microsoft Bot Framework WebexAdapter for .NET
This package contains an adapter that communicates directly with the Webex Teams API, and translates messages to and from a standard format used by your bot.
## How to Install
````
PM> Install-Package Microsoft.Bot.Builder.Adapters.Webex
````
## How to Use
### Set the Webex Credentials
When your bot sends a request to Webex API, it must include information that Webex can use to verify its identity.
To authenticate the requests, you'll need to configure the Adapter with the Public Address, the Access Token, a Secret and an optional Webhook Name.
You could create in the project an `appsettings.json` file to set the Webex credentials as follows:
```json
{
"WebexPublicAddress": "",
"WebexAccessToken": "",
"WebexSecret": "",
"WebexWebhookName": ""
}
```
### Use WebexAdapter in your App
WebexAdapter provides a translation layer for BotBuilder so that bot developers can connect to Webex Teams and have access to Webex's API.
To add the WebexAdapter to a bot, for example, an `EchoBot`, in the `Startup` class you should add:
```C#
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
// Create the Bot Framework Webex Adapter.
services.AddSingleton<IBotFrameworkHttpAdapter, WebexAdapter>();
// Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
services.AddTransient<IBot, EchoBot>();
}
```
## Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.microsoft.com.
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
## License
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the [MIT](https://github.com/Microsoft/vscode/blob/master/LICENSE.txt) License.

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

@ -1,290 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Authentication;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Newtonsoft.Json;
using Thrzn41.WebexTeams.Version1;
namespace Microsoft.Bot.Builder.Adapters.Webex
{
/// <summary>
/// BotAdapter to allow for handling Webex Teams app payloads and responses via the Webex Teams API.
/// </summary>
public class WebexAdapter : BotAdapter, IBotFrameworkHttpAdapter
{
private readonly WebexClientWrapper _webexClient;
private readonly ILogger _logger;
private readonly WebexAdapterOptions _options;
/// <summary>
/// Initializes a new instance of the <see cref="WebexAdapter"/> class using configuration settings.
/// </summary>
/// <param name="configuration">An <see cref="IConfiguration"/> instance.</param>
/// <remarks>
/// The configuration keys are:
/// WebexAccessToken: An access token for the bot.
/// WebexPublicAddress: The root URL of the bot application.
/// WebexSecret: The secret used to validate incoming webhooks.
/// WebexWebhookName: A name for the webhook subscription.
/// </remarks>
/// <param name="options">An instance of <see cref="WebexAdapterOptions"/>.</param>
/// <param name="logger">The ILogger implementation this adapter should use.</param>
public WebexAdapter(IConfiguration configuration, WebexAdapterOptions options = null, ILogger logger = null)
: this(new WebexClientWrapper(new WebexClientWrapperOptions(configuration["WebexAccessToken"], new Uri(configuration["WebexPublicAddress"]), configuration["WebexSecret"], configuration["WebexWebhookName"])), options, logger)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WebexAdapter"/> class.
/// Creates a Webex adapter.
/// </summary>
/// <param name="webexClient">A Webex API interface.</param>
/// <param name="options">An instance of <see cref="WebexAdapterOptions"/>.</param>
/// <param name="logger">The ILogger implementation this adapter should use.</param>
public WebexAdapter(WebexClientWrapper webexClient, WebexAdapterOptions options, ILogger logger = null)
{
_webexClient = webexClient ?? throw new ArgumentNullException(nameof(webexClient));
_options = options ?? new WebexAdapterOptions();
_logger = logger ?? NullLogger.Instance;
}
/// <summary>
/// Standard BotBuilder adapter method to send a message from the bot to the messaging API.
/// </summary>
/// <param name="turnContext">A TurnContext representing the current incoming message and environment.</param>
/// <param name="activities">An array of outgoing activities to be sent back to the messaging API.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
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)
{
_logger.LogTrace($"Unsupported Activity Type: '{activity.Type}'. Only Activities of type 'Message' are supported.");
}
else
{
// transform activity into the webex message format
string recipientId;
var target = MessageTarget.PersonId;
if (activity.Conversation?.Id != null)
{
recipientId = activity.Conversation.Id;
target = MessageTarget.SpaceId;
}
else if (activity.Conversation == null && activity.Recipient?.Id != null)
{
recipientId = activity.Recipient.Id;
}
else if (activity.GetChannelData<WebhookEventData>()?.MessageData.PersonEmail != null)
{
recipientId = activity.GetChannelData<WebhookEventData>()?.MessageData.PersonEmail;
}
else
{
throw new InvalidOperationException("No Person, Email or Room to send the message");
}
string responseId;
if (activity.Attachments != null && activity.Attachments.Count > 0)
{
if (activity.Attachments[0].ContentType == "application/vnd.microsoft.card.adaptive")
{
responseId = await _webexClient.CreateMessageWithAttachmentsAsync(recipientId, activity.Text, activity.Attachments, MessageTextType.Text, target, cancellationToken).ConfigureAwait(false);
}
else
{
var files = new List<Uri>();
foreach (var attachment in activity.Attachments)
{
var file = new Uri(attachment.ContentUrl);
files.Add(file);
}
responseId = await _webexClient.CreateMessageAsync(recipientId, activity.Text, files.Count > 0 ? files : null, MessageTextType.Text, target, cancellationToken).ConfigureAwait(false);
}
}
else
{
responseId = await _webexClient
.CreateMessageAsync(recipientId, activity.Text, target: target, cancellationToken: cancellationToken)
.ConfigureAwait(false);
}
responses.Add(new ResourceResponse(responseId));
}
}
return responses.ToArray();
}
/// <summary>
/// Standard BotBuilder adapter method to update a previous message.
/// </summary>
/// <param name="turnContext">A TurnContext representing the current incoming message and environment.</param>
/// <param name="activity">An activity to be sent back to the messaging API.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public override Task<ResourceResponse> UpdateActivityAsync(ITurnContext turnContext, Activity activity, CancellationToken cancellationToken)
{
return Task.FromException<ResourceResponse>(new NotSupportedException("Webex adapter does not support updateActivity."));
}
/// <summary>
/// Standard BotBuilder adapter method to delete a previous message.
/// </summary>
/// <param name="turnContext">A <see cref="ITurnContext"/> representing the current incoming message and environment.</param>
/// <param name="reference">A <see cref="ConversationReference"/> object.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public override async Task DeleteActivityAsync(ITurnContext turnContext, ConversationReference reference, CancellationToken cancellationToken)
{
if (!string.IsNullOrWhiteSpace(reference.ActivityId))
{
await _webexClient.DeleteMessageAsync(reference.ActivityId, cancellationToken).ConfigureAwait(false);
}
}
/// <summary>
/// Standard BotBuilder adapter method for continuing an existing conversation based on a conversation reference.
/// </summary>
/// <param name="reference">A <see cref="ConversationReference"/> to be applied to future messages.</param>
/// <param name="logic">A bot logic function that will perform continuing action.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
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);
}
}
/// <summary>
/// Sends a proactive message from the bot to a conversation.
/// </summary>
/// <param name="claimsIdentity">A <see cref="ClaimsIdentity"/> for the conversation.</param>
/// <param name="reference">A reference to the conversation to continue.</param>
/// <param name="callback">The method to call for the resulting bot turn.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task that represents the work queued to execute.</returns>
/// <remarks>Call this method to proactively send a message to a conversation.
/// Most _channels require a user to initialize a conversation with a bot
/// before the bot can send activities to the user.
/// <para>This method registers the following services for the turn.<list type="bullet">
/// <item><description><see cref="IIdentity"/> (key = "BotIdentity"), a claims claimsIdentity for the bot.
/// </description></item>
/// </list></para>
/// </remarks>
/// <seealso cref="BotAdapter.RunPipelineAsync(ITurnContext, BotCallbackHandler, CancellationToken)"/>
public override async Task ContinueConversationAsync(ClaimsIdentity claimsIdentity, ConversationReference reference, BotCallbackHandler callback, CancellationToken cancellationToken)
{
using (var context = new TurnContext(this, reference.GetContinuationActivity()))
{
context.TurnState.Add<IIdentity>(BotIdentityKey, claimsIdentity);
context.TurnState.Add<BotCallbackHandler>(callback);
await RunPipelineAsync(context, callback, cancellationToken).ConfigureAwait(false);
}
}
/// <summary>
/// Accept an incoming webhook <see cref="HttpRequest"/> and convert it into a <see cref="TurnContext"/> which can be processed by the bot's logic.
/// </summary>
/// <param name="request">The incoming <see cref="HttpRequest"/>.</param>
/// <param name="response">When this method completes, the <see cref="HttpResponse"/> to send.</param>
/// <param name="bot">The bot that will handle the incoming activity.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task ProcessAsync(HttpRequest request, HttpResponse response, IBot bot, CancellationToken cancellationToken)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
if (response == null)
{
throw new ArgumentNullException(nameof(response));
}
if (bot == null)
{
throw new ArgumentNullException(nameof(bot));
}
var identity = await _webexClient.GetMeAsync(cancellationToken).ConfigureAwait(false);
WebhookEventData payload;
string json;
using (var bodyStream = new StreamReader(request.Body))
{
json = await bodyStream.ReadToEndAsync().ConfigureAwait(false);
payload = JsonConvert.DeserializeObject<WebhookEventData>(json);
}
if (_options.ValidateIncomingRequests && !_webexClient.ValidateSignature(request, json))
{
throw new AuthenticationException("Webhook received message with invalid signature. Potential malicious behavior!");
}
Activity activity;
if (payload.Resource == EventResource.Message && payload.EventType == EventType.Created)
{
var decryptedMessage = await WebexHelper.GetDecryptedMessageAsync(payload, _webexClient.GetMessageAsync, cancellationToken).ConfigureAwait(false);
activity = WebexHelper.DecryptedMessageToActivity(decryptedMessage, identity);
}
else if (payload.Resource.Name == "attachmentActions" && payload.EventType == EventType.Created)
{
var extraData = payload.GetResourceData<TeamsData>();
var data = JsonConvert.SerializeObject(extraData);
var jsonData = JsonConvert.DeserializeObject<AttachmentActionData>(data);
var decryptedMessage = await _webexClient.GetAttachmentActionAsync(jsonData.Id, cancellationToken).ConfigureAwait(false);
activity = WebexHelper.AttachmentActionToActivity(decryptedMessage, identity);
}
else
{
activity = WebexHelper.PayloadToActivity(payload, identity);
}
using (var context = new TurnContext(this, activity))
{
await RunPipelineAsync(context, bot.OnTurnAsync, cancellationToken).ConfigureAwait(false);
}
}
}
}

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

@ -1,21 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
namespace Microsoft.Bot.Builder.Adapters.Webex
{
/// <summary>
/// Options class for the <see cref="WebexAdapter" />.
/// </summary>
public class WebexAdapterOptions
{
/// <summary>
/// Gets or sets a value indicating whether the signature on incoming requests should be validated as originating from Webex.
/// </summary>
/// <value>
/// A value indicating if the signature on incoming requests should be validated as originating from Webex.
/// </value>
public bool ValidateIncomingRequests { get; set; } = true;
}
}

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

@ -1,221 +0,0 @@
// Copyright (c) Microsoft Corporation.All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Bot.Schema;
using Newtonsoft.Json;
using Thrzn41.WebexTeams;
using Thrzn41.WebexTeams.Version1;
namespace Microsoft.Bot.Builder.Adapters.Webex
{
/// <summary>
/// A client for interacting with the Webex Teams API.
/// </summary>
public class WebexClientWrapper
{
private const string MessageUrl = "https://api.ciscospark.com/v1/messages";
private const string ActionsUrl = "https://api.ciscospark.com/v1/attachment/actions";
private const string SparkSignature = "x-spark-signature";
private readonly TeamsAPIClient _api;
/// <summary>
/// Initializes a new instance of the <see cref="WebexClientWrapper"/> class.
/// Creates a Webex Client Wrapper. See <see cref="WebexClientWrapperOptions"/> for a full definition of the allowed parameters.
/// </summary>
/// <param name="options">An object containing API credentials, a webhook verification token and other options.</param>
public WebexClientWrapper(WebexClientWrapperOptions options)
{
Options = options ?? throw new ArgumentNullException(nameof(options));
if (string.IsNullOrWhiteSpace(Options.WebexAccessToken))
{
throw new ArgumentException(nameof(options.WebexAccessToken));
}
if (Options.WebexPublicAddress == null)
{
throw new ArgumentException(nameof(options.WebexPublicAddress));
}
_api = TeamsAPI.CreateVersion1Client(Options.WebexAccessToken);
}
/// <summary>
/// Gets the options collection for the adapter.
/// </summary>
/// <value>A WebexClientWrapperOptions class exposing properties for each of the available options.</value>
public WebexClientWrapperOptions Options { get; }
/// <summary>
/// Validates the local secret against the one obtained from the request header.
/// </summary>
/// <param name="request">The <see cref="HttpRequest"/> with the signature.</param>
/// <param name="jsonPayload">The serialized payload to be use for comparison.</param>
/// <returns>The result of the comparison between the signature in the request and hashed json.</returns>
public virtual bool ValidateSignature(HttpRequest request, string jsonPayload)
{
var signature = request.Headers.ContainsKey(SparkSignature)
? request.Headers[SparkSignature].ToString().ToUpperInvariant()
: throw new InvalidOperationException($"HttpRequest is missing \"{SparkSignature}\"");
#pragma warning disable CA5350 // Webex API uses SHA1 as cryptographic algorithm.
using (var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(Options.WebexSecret)))
{
var hashArray = hmac.ComputeHash(Encoding.UTF8.GetBytes(jsonPayload));
var hash = BitConverter.ToString(hashArray).Replace("-", string.Empty).ToUpperInvariant();
return signature == hash;
}
#pragma warning restore CA5350 // Webex API uses SHA1 as cryptographic algorithm.
}
/// <summary>
/// Wraps Webex API's CreateMessageAsync method.
/// </summary>
/// <param name="recipient">Target id of the message.</param>
/// <param name="text">Text of the message.</param>
/// <param name="files">List of files attached to the message.</param>
/// <param name="messageType">Type of message. It can be Text or Markdown.</param>
/// <param name="target">Target for the message.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>The created message id.</returns>
public virtual async Task<string> CreateMessageAsync(string recipient, string text, IList<Uri> files = null, MessageTextType messageType = MessageTextType.Text, MessageTarget target = MessageTarget.PersonId, CancellationToken cancellationToken = default)
{
var webexResponse = await _api.CreateMessageAsync(recipient, text, files, target, messageType, cancellationToken: cancellationToken).ConfigureAwait(false);
return webexResponse.Data.Id;
}
/// <summary>
/// Wraps Webex API's DeleteMessageAsync method.
/// </summary>
/// <param name="messageId">The id of the message to be deleted.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public virtual async Task DeleteMessageAsync(string messageId, CancellationToken cancellationToken)
{
await _api.DeleteMessageAsync(messageId, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Creates a message with attachments.
/// </summary>
/// <param name="recipient">PersonId, email or roomId of the message.</param>
/// <param name="text">Text of the message.</param>
/// <param name="attachments">List of attachments attached to the message.</param>
/// <param name="messageType">Type of the message. It can be Text or Markdown.</param>
/// <param name="target">Target for the message.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>The created message id.</returns>
public virtual async Task<string> CreateMessageWithAttachmentsAsync(string recipient, string text, IList<Attachment> attachments, MessageTextType messageType = MessageTextType.Text, MessageTarget target = MessageTarget.PersonId, CancellationToken cancellationToken = default)
{
Message result;
var attachmentsContent = new List<object>();
foreach (var attach in attachments)
{
attachmentsContent.Add(attach.Content);
}
var request = new WebexMessageRequest
{
RoomId = target == MessageTarget.SpaceId ? recipient : null,
ToPersonId = target == MessageTarget.SpaceId ? null : recipient,
Text = text ?? string.Empty,
Attachments = attachmentsContent.Count > 0 ? attachmentsContent : null,
};
var http = (HttpWebRequest)WebRequest.Create(new Uri(MessageUrl));
http.PreAuthenticate = true;
http.Headers.Add("Authorization", "Bearer " + Options.WebexAccessToken);
http.Accept = "application/json";
http.ContentType = "application/json";
http.Method = "POST";
var parsedContent = JsonConvert.SerializeObject(request);
var encoding = new ASCIIEncoding();
var bytes = encoding.GetBytes(parsedContent);
var newStream = http.GetRequestStream();
newStream.Write(bytes, 0, bytes.Length);
newStream.Close();
var response = await http.GetResponseAsync().ConfigureAwait(false);
var stream = response.GetResponseStream();
using (var sr = new StreamReader(stream))
{
var content = await sr.ReadToEndAsync().ConfigureAwait(false);
result = JsonConvert.DeserializeObject<Message>(content);
}
return result.Id;
}
/// <summary>
/// Shows details for an attachment action, by ID.
/// </summary>
/// <param name="actionId">An unique identifier for the attachment action.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>The attachment action details.</returns>
public virtual async Task<Message> GetAttachmentActionAsync(string actionId, CancellationToken cancellationToken)
{
Message result;
var url = $"{ActionsUrl}/{actionId}";
var http = (HttpWebRequest)WebRequest.Create(new Uri(url));
http.PreAuthenticate = true;
http.Headers.Add("Authorization", "Bearer " + Options.WebexAccessToken);
http.Method = "GET";
var response = await http.GetResponseAsync().ConfigureAwait(false);
var stream = response.GetResponseStream();
using (var sr = new StreamReader(stream))
{
var content = await sr.ReadToEndAsync().ConfigureAwait(false);
result = JsonConvert.DeserializeObject<Message>(content);
}
return result;
}
/// <summary>
/// Wraps Webex API's GetMeAsync method.
/// </summary>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>The <see cref="Person"/> object associated with the bot.</returns>
public virtual async Task<Person> GetMeAsync(CancellationToken cancellationToken)
{
var resultPerson = await _api.GetMeAsync(cancellationToken).ConfigureAwait(false);
return resultPerson.GetData(false);
}
/// <summary>
/// Wraps Webex API's GetMessageAsync method.
/// </summary>
/// <param name="messageId">Id of the message to be recovered.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>The message's data.</returns>
public virtual async Task<Message> GetMessageAsync(string messageId, CancellationToken cancellationToken)
{
var message = await _api.GetMessageAsync(messageId, cancellationToken).ConfigureAwait(false);
return message.GetData(false);
}
}
}

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

@ -1,52 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
namespace Microsoft.Bot.Builder.Adapters.Webex
{
/// <summary>
/// Defines implementation of the WebexAdapter Options.
/// </summary>
public class WebexClientWrapperOptions
{
/// <summary>
/// Initializes a new instance of the <see cref="WebexClientWrapperOptions"/> class.
/// </summary>
/// <param name="webexAccessToken">An access token for the bot.</param>
/// <param name="webexPublicAddress">The root URL of the bot application.</param>
/// <param name="webexSecret">The secret used to validate incoming webhooks.</param>
/// <param name="webexWebhookName">A name for the webhook subscription.</param>
public WebexClientWrapperOptions(string webexAccessToken, Uri webexPublicAddress, string webexSecret, string webexWebhookName = null)
{
WebexAccessToken = webexAccessToken;
WebexPublicAddress = webexPublicAddress;
WebexSecret = webexSecret;
WebexWebhookName = webexWebhookName;
}
/// <summary>
/// Gets or sets an access token for the bot.
/// </summary>
/// <value>An access token for the bot. Get one from 'https://developer.webex.com/'.</value>
public string WebexAccessToken { get; set; }
/// <summary>
/// Gets or sets the secret used to validate incoming webhooks.
/// </summary>
/// <value>The secret used to validate incoming webhooks. You can define this yourself.</value>
public string WebexSecret { get; set; }
/// <summary>
/// Gets or sets the root URI of your bot application. Something like 'https://mybot.com/'.
/// </summary>
/// <value>the root URI of your bot application.</value>
public Uri WebexPublicAddress { get; set; }
/// <summary>
/// Gets or sets a name for the webhook subscription that will be created to tell Webex to send your bot webhooks.
/// </summary>
/// <value>A name for the webhook subscription.</value>
public string WebexWebhookName { get; set; }
}
}

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

@ -1,230 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Schema;
using Newtonsoft.Json;
using Thrzn41.WebexTeams.Version1;
#if SIGNASSEMBLY
[assembly: InternalsVisibleTo("Microsoft.Bot.Builder.Adapters.Webex.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
#else
[assembly: InternalsVisibleTo("Microsoft.Bot.Builder.Adapters.Webex.Tests")]
#endif
namespace Microsoft.Bot.Builder.Adapters.Webex
{
internal static class WebexHelper
{
/// <summary>
/// Creates an <see cref="Activity"/> using the body of a request.
/// </summary>
/// <param name="payload">The payload obtained from the body of the request.</param>
/// <param name="identity">The identity of the bot.</param>
/// <returns>An <see cref="Activity"/> object.</returns>
public static Activity PayloadToActivity(WebhookEventData payload, Person identity)
{
if (payload == null)
{
throw new ArgumentNullException(nameof(payload));
}
var activity = new Activity
{
Id = payload.Id,
Timestamp = default,
ChannelId = "webex",
Conversation = new ConversationAccount
{
Id = payload.SpaceMembershipData.SpaceId,
},
From = new ChannelAccount
{
Id = payload.ActorId,
},
Recipient = new ChannelAccount
{
Id = identity.Id,
},
ChannelData = payload,
Type = ActivityTypes.Event,
};
if (payload.MessageData.FileCount > 0)
{
activity.Attachments = HandleMessageAttachments(payload.MessageData);
}
return activity;
}
/// <summary>
/// Gets a decrypted <see cref="Message"/> by its Id.
/// </summary>
/// <param name="payload">The payload obtained from the body of the request.</param>
/// <param name="decrypterFunc">The function used to decrypt the message.</param>
/// <param name="cancellationToken">A cancellation token for the task.</param>
/// <returns>A <see cref="Message"/> object.</returns>
public static async Task<Message> GetDecryptedMessageAsync(WebhookEventData payload, Func<string, CancellationToken, Task<Message>> decrypterFunc, CancellationToken cancellationToken)
{
if (payload == null)
{
return null;
}
return await decrypterFunc(payload.MessageData.Id, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Converts a decrypted <see cref="Message"/> into an <see cref="Activity"/>.
/// </summary>
/// <param name="decryptedMessage">The decrypted message obtained from the body of the request.</param>
/// <param name="identity">The identity of the bot.</param>
/// <returns>An <see cref="Activity"/> object.</returns>
public static Activity DecryptedMessageToActivity(Message decryptedMessage, Person identity)
{
if (decryptedMessage == null)
{
return null;
}
var activity = new Activity
{
Id = decryptedMessage.Id,
Timestamp = default,
ChannelId = "webex",
Conversation = new ConversationAccount
{
Id = decryptedMessage.SpaceId,
},
From = new ChannelAccount
{
Id = decryptedMessage.PersonId,
Name = decryptedMessage.PersonEmail,
},
Recipient = new ChannelAccount
{
Id = identity.Id,
},
Text = !string.IsNullOrEmpty(decryptedMessage.Text) ? decryptedMessage.Text : string.Empty,
ChannelData = decryptedMessage,
Type = ActivityTypes.Message,
};
// this is the bot speaking
if (activity.From.Id == identity.Id)
{
activity.Type = ActivityTypes.Event;
activity.Name = "self_message";
}
if (decryptedMessage.HasHtml)
{
// strip the mention & HTML from the message
var pattern = new Regex($"^(<p>|<div>)?<spark-mention .*?data-object-id=\"{identity.Id}\".*?>.*?</spark-mention>", RegexOptions.IgnoreCase | RegexOptions.Multiline);
if (!pattern.IsMatch(decryptedMessage.Html))
{
var encodedId = $"{identity.Id}=";
var buffer = Convert.FromBase64String(encodedId);
var decodedId = Encoding.ASCII.GetString(buffer);
// this should look like ciscospark://us/PEOPLE/<id string>
var match = Regex.Match(decodedId, "ciscospark://.*/(.*)", RegexOptions.IgnoreCase | RegexOptions.Multiline);
if (match.Captures.Count > 0)
{
pattern = new Regex(
$"^(<p>|<div>)?<spark-mention .*?data-object-id=\"{match.Groups[1]}\".*?>.*?</spark-mention>", RegexOptions.IgnoreCase | RegexOptions.Multiline);
}
}
var action = pattern.Replace(decryptedMessage.Html, string.Empty);
// Strip the remaining HTML tags and replace the message text with the HTML version
var remainingHtml = new Regex("<.*?>", RegexOptions.IgnoreCase | RegexOptions.Multiline);
activity.Text = remainingHtml.Replace(action, string.Empty).Trim();
}
else
{
var pattern = new Regex("^" + identity.DisplayName + "\\s+");
activity.Text = pattern.Replace(activity.Text, string.Empty);
}
if (decryptedMessage.FileCount > 0)
{
activity.Attachments = HandleMessageAttachments(decryptedMessage);
}
return activity;
}
/// <summary>
/// Converts a decrypted <see cref="Message"/> related to an attachment action into an <see cref="Activity"/>.
/// </summary>
/// <param name="decryptedMessage">The decrypted message obtained from the body of the request.</param>
/// <param name="identity">The identity of the bot.</param>
/// <returns>An <see cref="Activity"/> object.</returns>
public static Activity AttachmentActionToActivity(Message decryptedMessage, Person identity)
{
if (decryptedMessage == null)
{
return null;
}
var data = JsonConvert.SerializeObject(decryptedMessage);
var messageExtraData = JsonConvert.DeserializeObject<AttachmentActionData>(data);
var activity = new Activity
{
Id = decryptedMessage.Id,
Timestamp = default,
ChannelId = "webex",
Conversation = new ConversationAccount
{
Id = decryptedMessage.SpaceId,
},
From = new ChannelAccount
{
Id = decryptedMessage.PersonId,
Name = decryptedMessage.PersonEmail,
},
Recipient = new ChannelAccount
{
Id = identity.Id,
},
Text = !string.IsNullOrEmpty(decryptedMessage.Text) ? decryptedMessage.Text : string.Empty,
Value = messageExtraData.Inputs,
ChannelData = decryptedMessage,
Type = ActivityTypes.Event,
};
return activity;
}
/// <summary>
/// Adds the message's files to an attachments list.
/// </summary>
/// <param name="message">The message with the files to process.</param>
/// <returns>A list of attachments containing the message's files.</returns>
public static List<Attachment> HandleMessageAttachments(Message message)
{
var attachmentsList = new List<Attachment>();
var attachment = new Attachment
{
// Currently Webex API takes only one attachment
ContentUrl = message.FileUris[0].AbsoluteUri,
};
attachmentsList.Add(attachment);
return attachmentsList;
}
}
}

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

@ -1,95 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Adapters.Webex
{
/// <summary>
/// Represents the payload received when a Webex Message is sent to the bot.
/// </summary>
public class WebexMessageRequest
{
/// <summary>
/// Initializes a new instance of the <see cref="WebexMessageRequest"/> class.
/// Represents the request needed to create a message with attachments.
/// </summary>
public WebexMessageRequest()
{
}
/// <summary>
/// Gets or sets the room ID of the message.
/// </summary>
/// <value>
/// The room ID of the message.
/// </value>
[JsonProperty(PropertyName = "roomId")]
public string RoomId { get; set; }
/// <summary>
/// Gets or sets the person ID of the recipient when sending a private 1:1 message.
/// </summary>
/// <value>
/// The person ID of the recipient when sending a private 1:1 message.
/// </value>
[JsonProperty(PropertyName = "toPersonId")]
public string ToPersonId { get; set; }
/// <summary>
/// Gets or sets the email address of the recipient when sending a private 1:1 message.
/// </summary>
/// <value>
/// The email address of the recipient when sending a private 1:1 message.
/// </value>
[JsonProperty(PropertyName = "toPersonEmail")]
public string ToPersonEmail { get; set; }
/// <summary>
/// Gets or sets the text of the message.
/// </summary>
/// <value>
/// The text of the message.
/// </value>
[JsonProperty(PropertyName = "text")]
public string Text { get; set; }
/// <summary>
/// Gets or sets the message in Markdown format.
/// </summary>
/// <value>
/// The message, in Markdown format.
/// </value>
[JsonProperty(PropertyName = "markdown")]
public string Markdown { get; set; }
/// <summary>
/// Gets the URI to a binary file to be posted into the room. Only one file is allowed per message.
/// </summary>
/// <value>
/// The URI to a binary file to be posted into the room.
/// </value>
[JsonProperty(PropertyName = "files")]
public IList<Uri> Files { get; } = new List<Uri>();
/// <summary>
/// Gets or sets the content attachments to attach to the message.
/// </summary>
/// <value>
/// The content attachments to attach to the message.
/// </value>
[JsonProperty(PropertyName = "attachments")]
public object Attachments { get; set; }
/// <summary>
/// Checks if Files property should be serialized or not.
/// </summary>
/// <returns>True if there are files in the array to be serialized, false if there aren't.</returns>
public bool ShouldSerializeFiles()
{
return Files.Count > 0;
}
}
}

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 1.9 KiB

Двоичные данные
libraries/Adapters/icon.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 1.9 KiB

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

@ -1,43 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version Condition=" '$(IsBuildServer)' == '' ">$(LocalPackageVersion)</Version>
<Version Condition=" '$(IsBuildServer)' != '' ">$(ReleasePackageVersion)</Version>
<PackageVersion Condition=" '$(IsBuildServer)' == '' ">$(LocalPackageVersion)</PackageVersion>
<PackageVersion Condition=" '$(IsBuildServer)' != '' ">$(ReleasePackageVersion)</PackageVersion>
<Configurations>Debug;Release</Configurations>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<DocumentationFile>bin\$(Configuration)\netstandard2.0\AdaptiveExpressions.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>Full</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageId>AdaptiveExpressions</PackageId>
<Description>This library implements Microsoft Bot Builder Expression evaluator for .NET</Description>
<Summary>This library implements Microsoft Bot Builder Expression evaluator for .NET</Summary>
</PropertyGroup>
<ItemGroup>
<None Remove="WindowsIanaMapping" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="WindowsIanaMapping" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Antlr4.CodeGenerator" Version="4.6.6">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Antlr4.Runtime" Version="4.6.6" />
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
<PackageReference Include="Microsoft.Recognizers.Text.DataTypes.TimexExpression" Version="1.2.9" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
</Project>

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

@ -1,70 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using AdaptiveExpressions.Memory;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Used to access the variable value corresponding to the path.
/// </summary>
internal class Accessor : ExpressionEvaluator
{
/// <summary>
/// Initializes a new instance of the <see cref="Accessor"/> class.
/// </summary>
public Accessor()
: base(ExpressionType.Accessor, Evaluator, ReturnType.Object, Validator)
{
}
private static (object value, string error) Evaluator(Expression expression, IMemory state, Options options)
{
var (path, left, error) = FunctionUtils.TryAccumulatePath(expression, state, options);
if (error != null)
{
return (null, error);
}
if (left == null)
{
// fully converted to path, so we just delegate to memory scope
return FunctionUtils.WrapGetValue(state, path, options);
}
else
{
// stop at somewhere, so we figure out what's left
var (newScope, err) = left.TryEvaluate(state, options);
if (err != null)
{
return (null, err);
}
return FunctionUtils.WrapGetValue(MemoryFactory.Create(newScope), path, options);
}
}
private static void Validator(Expression expression)
{
var children = expression.Children;
if (children.Length == 0
|| !(children[0] is Constant cnst)
|| cnst.ReturnType != ReturnType.String)
{
throw new Exception($"{expression} must have a string as first argument.");
}
if (children.Length > 2)
{
throw new Exception($"{expression} has more than 2 children.");
}
if (children.Length == 2 && (children[1].ReturnType & ReturnType.Object) == 0)
{
throw new Exception($"{expression} must have an object as its second argument.");
}
}
}
}

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

@ -1,79 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Globalization;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Return the result from adding two or more numbers (pure number case) or concatting two or more strings (other case).
/// </summary>
internal class Add : ExpressionEvaluator
{
/// <summary>
/// Initializes a new instance of the <see cref="Add"/> class.
/// </summary>
public Add()
: base(ExpressionType.Add, Evaluator(), ReturnType.String | ReturnType.Number, Validator)
{
}
private static EvaluateExpressionDelegate Evaluator()
{
return FunctionUtils.ApplySequenceWithError(
args =>
{
object result = null;
string error = null;
var firstItem = args[0];
var secondItem = args[1];
var stringConcat = !firstItem.IsNumber() || !secondItem.IsNumber();
if ((firstItem == null && secondItem.IsNumber())
|| (secondItem == null && firstItem.IsNumber()))
{
error = "Operator '+' or add cannot be applied to operands of type 'number' and null object.";
}
else
{
if (stringConcat)
{
result = $"{firstItem?.ToString()}{secondItem?.ToString()}";
}
else
{
result = EvalAdd(args[0], args[1]);
}
}
return (result, error);
}, FunctionUtils.VerifyNumberOrStringOrNull);
}
private static object EvalAdd(object a, object b)
{
if (a == null)
{
throw new ArgumentNullException(nameof(a));
}
if (b == null)
{
throw new ArgumentNullException(nameof(b));
}
if (a.IsInteger() && b.IsInteger())
{
return Convert.ToInt64(a, CultureInfo.InvariantCulture) + Convert.ToInt64(b, CultureInfo.InvariantCulture);
}
return FunctionUtils.CultureInvariantDoubleConvert(a) + FunctionUtils.CultureInvariantDoubleConvert(b);
}
private static void Validator(Expression expression)
{
FunctionUtils.ValidateArityAndAnyType(expression, 2, int.MaxValue, ReturnType.String | ReturnType.Number);
}
}
}

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

@ -1,29 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Add a number of days to a timestamp.
/// AddDays function takes a timestamp string, an interval integer,
/// an optional format string whose default value "yyyy-MM-ddTHH:mm:ss.fffZ"
/// and an optional locale string whose default value is Thread.CurrentThread.CurrentCulture.Name.
/// </summary>
internal class AddDays : TimeTransformEvaluator
{
/// <summary>
/// Initializes a new instance of the <see cref="AddDays"/> class.
/// </summary>
public AddDays()
: base(ExpressionType.AddDays, Function)
{
}
private static DateTime Function(DateTime time, int interval)
{
return time.AddDays(interval);
}
}
}

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

@ -1,29 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Add a number of hours to a timestamp.
/// AddHours function takes a timestamp string, an interval integer,
/// an optional format string whose default value "yyyy-MM-ddTHH:mm:ss.fffZ"
/// and an optional locale string whose default value is Thread.CurrentThread.CurrentCulture.Name.
/// </summary>
internal class AddHours : TimeTransformEvaluator
{
/// <summary>
/// Initializes a new instance of the <see cref="AddHours"/> class.
/// </summary>
public AddHours()
: base(ExpressionType.AddHours, Function)
{
}
private static DateTime Function(DateTime time, int interval)
{
return time.AddHours(interval);
}
}
}

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

@ -1,29 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Add a number of minutes to a timestamp.
/// AddMinutes function takes a timestamp string, an interval integer,
/// an optional format string whose default value "yyyy-MM-ddTHH:mm:ss.fffZ"
/// and an optional locale string whose default value is Thread.CurrentThread.CurrentCulture.Name.
/// </summary>
internal class AddMinutes : TimeTransformEvaluator
{
/// <summary>
/// Initializes a new instance of the <see cref="AddMinutes"/> class.
/// </summary>
public AddMinutes()
: base(ExpressionType.AddMinutes, Function)
{
}
private static DateTime Function(DateTime time, int interval)
{
return time.AddMinutes(interval);
}
}
}

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

@ -1,84 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Globalization;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Return the ordinal number of the input number.
/// </summary>
internal class AddOrdinal : ExpressionEvaluator
{
/// <summary>
/// Initializes a new instance of the <see cref="AddOrdinal"/> class.
/// </summary>
public AddOrdinal()
: base(ExpressionType.AddOrdinal, Evaluator(), ReturnType.String, Validator)
{
}
private static EvaluateExpressionDelegate Evaluator()
{
return FunctionUtils.ApplyWithError(
args =>
{
object result = null;
string error = null;
var input = 0;
(input, error) = FunctionUtils.ParseInt32(args[0]);
if (error == null)
{
result = EvalAddOrdinal(input);
}
return (result, error);
}, FunctionUtils.VerifyInteger);
}
private static void Validator(Expression expression)
{
FunctionUtils.ValidateArityAndAnyType(expression, 1, 1, ReturnType.Number);
}
private static string EvalAddOrdinal(int num)
{
var hasResult = false;
var ordinalResult = num.ToString(CultureInfo.InvariantCulture);
if (num > 0)
{
switch (num % 100)
{
case 11:
case 12:
case 13:
ordinalResult += "th";
hasResult = true;
break;
}
if (!hasResult)
{
switch (num % 10)
{
case 1:
ordinalResult += "st";
break;
case 2:
ordinalResult += "nd";
break;
case 3:
ordinalResult += "rd";
break;
default:
ordinalResult += "th";
break;
}
}
}
return ordinalResult;
}
}
}

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

@ -1,48 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Add a property and its value, or name-value pair, to a JSON object, and return the updated object.
/// If the object already exists at runtime the function throws an error.
/// </summary>
internal class AddProperty : ExpressionEvaluator
{
/// <summary>
/// Initializes a new instance of the <see cref="AddProperty"/> class.
/// </summary>
public AddProperty()
: base(ExpressionType.AddProperty, Evaluator(), ReturnType.Object, Validator)
{
}
private static EvaluateExpressionDelegate Evaluator()
{
return FunctionUtils.ApplyWithError(args =>
{
var newJobj = (IDictionary<string, JToken>)args[0];
var prop = args[1].ToString();
string error = null;
if (newJobj.ContainsKey(prop))
{
error = $"{prop} already exists";
}
else
{
newJobj[prop] = FunctionUtils.ConvertToJToken(args[2]);
}
return (newJobj, error);
});
}
private static void Validator(Expression expression)
{
FunctionUtils.ValidateOrder(expression, null, ReturnType.Object, ReturnType.String, ReturnType.Object);
}
}
}

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

@ -1,29 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Add a number of seconds to a timestamp.
/// AddSeconds function takes a timestamp string, an interval integer,
/// an optional format string whose default value "yyyy-MM-ddTHH:mm:ss.fffZ"
/// and an optional locale string whose default value is Thread.CurrentThread.CurrentCulture.Name.
/// </summary>
internal class AddSeconds : TimeTransformEvaluator
{
/// <summary>
/// Initializes a new instance of the <see cref="AddSeconds"/> class.
/// </summary>
public AddSeconds()
: base(ExpressionType.AddSeconds, Function)
{
}
private static DateTime Function(DateTime time, int interval)
{
return time.AddSeconds(interval);
}
}
}

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

@ -1,83 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using AdaptiveExpressions.Memory;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Add a number of time units to a timestamp.
/// AddToTime function takes a timestamp string, an interval integer, a unit of time string,
/// an optional format string whose default value "yyyy-MM-ddTHH:mm:ss.fffZ"
/// and an optional locale string whose default value is Thread.CurrentThread.CurrentCulture.Name.
/// </summary>
internal class AddToTime : ExpressionEvaluator
{
/// <summary>
/// Initializes a new instance of the <see cref="AddToTime"/> class.
/// </summary>
public AddToTime()
: base(ExpressionType.AddToTime, Evaluator, ReturnType.String, Validator)
{
}
private static (object value, string error) Evaluator(Expression expression, IMemory state, Options options)
{
object value = null;
string error = null;
IReadOnlyList<object> args;
var locale = options.Locale != null ? new CultureInfo(options.Locale) : Thread.CurrentThread.CurrentCulture;
var format = FunctionUtils.DefaultDateTimeFormat;
(args, error) = FunctionUtils.EvaluateChildren(expression, state, options);
if (error == null)
{
(format, locale, error) = FunctionUtils.DetermineFormatAndLocale(args, format, locale, 5);
}
if (error == null)
{
if (args[1].IsInteger() && args[2] is string timeUnit)
{
(value, error) = EvalAddToTime(args[0], Convert.ToInt64(args[1], CultureInfo.InvariantCulture), timeUnit, format, locale);
}
else
{
error = $"{expression} should contain an ISO format timestamp, a time interval integer, a string unit of time, an optional output format string and an optional locale string.";
}
}
return (value, error);
}
private static (string, string) EvalAddToTime(object timestamp, long interval, string timeUnit, string format, CultureInfo locale)
{
string result = null;
string error = null;
object parsed = null;
(parsed, error) = FunctionUtils.NormalizeToDateTime(timestamp);
if (error == null)
{
var ts = (DateTime)parsed;
Func<DateTime, DateTime> converter;
(converter, error) = FunctionUtils.DateTimeConverter(interval, timeUnit, false);
if (error == null)
{
var addedTimeStamp = converter(ts);
(result, error) = FunctionUtils.ReturnFormatTimeStampStr(addedTimeStamp, format, locale);
}
}
return (result, error);
}
private static void Validator(Expression expression)
{
FunctionUtils.ValidateOrder(expression, new[] { ReturnType.String, ReturnType.String }, ReturnType.Object, ReturnType.Number, ReturnType.String);
}
}
}

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

@ -1,55 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using AdaptiveExpressions.Memory;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Check whether all expressions are true. Return true if all expressions are true,
/// or return false if at least one expression is false.
/// </summary>
#pragma warning disable CA1716 // Identifiers should not match keywords (by design and can't break binary compat, excluding)
internal class And : ExpressionEvaluator
#pragma warning restore CA1716 // Identifiers should not match keywords
{
/// <summary>
/// Initializes a new instance of the <see cref="And"/> class.
/// </summary>
public And()
: base(ExpressionType.And, Evaluator, ReturnType.Boolean, FunctionUtils.ValidateAtLeastOne)
{
}
private static (object value, string error) Evaluator(Expression expression, IMemory state, Options options)
{
object result = true;
string error = null;
foreach (var child in expression.Children)
{
(result, error) = child.TryEvaluate(state, new Options(options) { NullSubstitution = null });
if (error == null)
{
if (FunctionUtils.IsLogicTrue(result))
{
result = true;
}
else
{
result = false;
break;
}
}
else
{
// We interpret any error as false and swallow the error
result = false;
error = null;
break;
}
}
return (result, error);
}
}
}

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

@ -1,34 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Globalization;
using System.Linq;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Return the number average of a numeric array.
/// </summary>
internal class Average : ExpressionEvaluator
{
/// <summary>
/// Initializes a new instance of the <see cref="Average"/> class.
/// </summary>
public Average()
: base(ExpressionType.Average, Evaluator(), ReturnType.Number, FunctionUtils.ValidateUnary)
{
}
private static EvaluateExpressionDelegate Evaluator()
{
return FunctionUtils.Apply(
args =>
{
var operands = FunctionUtils.ResolveListValue(args[0]).OfType<object>().ToList();
return operands.Average(u => Convert.ToSingle(u, CultureInfo.InvariantCulture));
},
FunctionUtils.VerifyNumericList);
}
}
}

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

@ -1,39 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Return the base64-encoded version of a string or byte array.
/// </summary>
internal class Base64 : ExpressionEvaluator
{
/// <summary>
/// Initializes a new instance of the <see cref="Base64"/> class.
/// </summary>
public Base64()
: base(ExpressionType.Base64, Evaluator(), ReturnType.String, FunctionUtils.ValidateUnary)
{
}
private static EvaluateExpressionDelegate Evaluator()
{
return FunctionUtils.Apply((args) =>
{
byte[] byteArray;
if (args[0] is byte[] byteArr)
{
byteArray = byteArr;
}
else
{
byteArray = System.Text.Encoding.UTF8.GetBytes(args[0].ToString());
}
return Convert.ToBase64String(byteArray);
});
}
}
}

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

@ -1,26 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Return the binary array of a base64-encoded string.
/// </summary>
internal class Base64ToBinary : ExpressionEvaluator
{
/// <summary>
/// Initializes a new instance of the <see cref="Base64ToBinary"/> class.
/// </summary>
public Base64ToBinary()
: base(ExpressionType.Base64ToBinary, Evaluator(), ReturnType.Object, FunctionUtils.ValidateUnary)
{
}
private static EvaluateExpressionDelegate Evaluator()
{
return FunctionUtils.Apply(args => Convert.FromBase64String(args[0].ToString()), FunctionUtils.VerifyString);
}
}
}

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

@ -1,27 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Return the string version of a base64-encoded string,
/// effectively decoding the base64 string.
/// </summary>
internal class Base64ToString : ExpressionEvaluator
{
/// <summary>
/// Initializes a new instance of the <see cref="Base64ToString"/> class.
/// </summary>
public Base64ToString()
: base(ExpressionType.Base64ToString, Evaluator(), ReturnType.String, FunctionUtils.ValidateUnary)
{
}
private static EvaluateExpressionDelegate Evaluator()
{
return FunctionUtils.Apply(args => System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(args[0].ToString())), FunctionUtils.VerifyString);
}
}
}

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

@ -1,26 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Return the binary version of a string.
/// </summary>
#pragma warning disable CA1724 // Type names should not match namespaces (by design and we can't change this without breaking binary compat)
internal class Binary : ExpressionEvaluator
#pragma warning restore CA1724 // Type names should not match namespaces
{
/// <summary>
/// Initializes a new instance of the <see cref="Binary"/> class.
/// </summary>
public Binary()
: base(ExpressionType.Binary, Evaluator(), ReturnType.Object, FunctionUtils.ValidateUnary)
{
}
private static EvaluateExpressionDelegate Evaluator()
{
return FunctionUtils.Apply(args => FunctionUtils.ToBinary(args[0].ToString()), FunctionUtils.VerifyString);
}
}
}

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

@ -1,29 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections.Generic;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Return the Boolean version of a value.
/// </summary>
internal class Bool : ComparisonEvaluator
{
/// <summary>
/// Initializes a new instance of the <see cref="Bool"/> class.
/// </summary>
public Bool()
: base(
ExpressionType.Bool,
Function,
FunctionUtils.ValidateUnary)
{
}
private static bool Function(IReadOnlyList<object> args)
{
return FunctionUtils.IsLogicTrue(args[0]);
}
}
}

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

@ -1,28 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Globalization;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Returns the smallest integral value that is greater than or equal to the specified number.
/// </summary>
internal class Ceiling : NumberTransformEvaluator
{
/// <summary>
/// Initializes a new instance of the <see cref="Ceiling"/> class.
/// </summary>
public Ceiling()
: base(ExpressionType.Ceiling, Function)
{
}
private static object Function(IReadOnlyList<object> args)
{
return Math.Ceiling(Convert.ToDouble(args[0], CultureInfo.InvariantCulture));
}
}
}

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

@ -1,40 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Linq;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Return the first non-null value from one or more parameters.
/// Empty strings, empty arrays, and empty objects are not null.
/// </summary>
internal class Coalesce : ExpressionEvaluator
{
/// <summary>
/// Initializes a new instance of the <see cref="Coalesce"/> class.
/// </summary>
public Coalesce()
: base(ExpressionType.Coalesce, Evaluator(), ReturnType.Object, FunctionUtils.ValidateAtLeastOne)
{
}
private static EvaluateExpressionDelegate Evaluator()
{
return FunctionUtils.Apply(args => EvalCoalesce(args.ToArray()));
}
private static object EvalCoalesce(object[] objectList)
{
foreach (var obj in objectList)
{
if (obj != null)
{
return obj;
}
}
return null;
}
}
}

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

@ -1,81 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Comparison operators.
/// A comparison operator returns false if the comparison is false, or there is an error. This prevents errors from short-circuiting boolean expressions.
/// </summary>
public class ComparisonEvaluator : ExpressionEvaluator
{
/// <summary>
/// Initializes a new instance of the <see cref="ComparisonEvaluator"/> class.
/// </summary>
/// <param name="type">Name of the built-in function.</param>
/// <param name="function">The comparison function, it takes a list of objects and returns a boolean.</param>
/// <param name="validator">Validator of input arguments.</param>
/// <param name="verify">Optional function to verify each child's result.</param>
public ComparisonEvaluator(string type, Func<IReadOnlyList<object>, bool> function, ValidateExpressionDelegate validator, FunctionUtils.VerifyExpression verify = null)
: base(type, Evaluator(function, verify), ReturnType.Boolean, validator)
{
}
private static EvaluateExpressionDelegate Evaluator(Func<IReadOnlyList<object>, bool> function, FunctionUtils.VerifyExpression verify)
{
return (expression, state, options) =>
{
var result = false;
string error = null;
IReadOnlyList<object> args;
(args, error) = FunctionUtils.EvaluateChildren(expression, state, new Options(options) { NullSubstitution = null }, verify);
if (error == null)
{
// Ensure args are all of same type
bool? isNumber = null;
foreach (var arg in args)
{
var obj = arg;
if (isNumber.HasValue)
{
if (obj != null && obj.IsNumber() != isNumber.Value)
{
error = $"Arguments must either all be numbers or strings in {expression}";
break;
}
}
else
{
isNumber = obj.IsNumber();
}
}
if (error == null)
{
try
{
result = function(args);
}
#pragma warning disable CA1031 // Do not catch general exception types (we are capturing the exception and returning it)
catch (Exception e)
#pragma warning restore CA1031 // Do not catch general exception types
{
// NOTE: This should not happen in normal execution
error = e.Message;
}
}
}
else
{
// Swallow errors and treat as false
error = null;
}
return (result, error);
};
}
}
}

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

@ -1,54 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Linq;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Combine two or more strings, and return the combined string.
/// </summary>
internal class Concat : ExpressionEvaluator
{
/// <summary>
/// Initializes a new instance of the <see cref="Concat"/> class.
/// </summary>
public Concat()
: base(ExpressionType.Concat, Evaluator(), ReturnType.Array | ReturnType.String, FunctionUtils.ValidateAtLeastOne)
{
}
private static EvaluateExpressionDelegate Evaluator()
{
return FunctionUtils.ApplySequence(
args =>
{
var firstItem = args[0];
var secondItem = args[1];
var isFirstList = FunctionUtils.TryParseList(firstItem, out var firstList);
var isSecondList = FunctionUtils.TryParseList(secondItem, out var secondList);
if (firstItem == null && secondItem == null)
{
return null;
}
else if (firstItem == null && isSecondList)
{
return secondList;
}
else if (secondItem == null && isFirstList)
{
return firstList;
}
else if (isFirstList && isSecondList)
{
return firstList.OfType<object>().Concat(secondList.OfType<object>()).ToList();
}
else
{
return $"{firstItem?.ToString()}{secondItem?.ToString()}";
}
});
}
}
}

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

@ -1,49 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections;
using AdaptiveExpressions.Memory;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Check whether a collection has a specific item. Return true if the item is found,
/// or return false if not found.
/// This function is case-sensitive.
/// </summary>
internal class Contains : ExpressionEvaluator
{
/// <summary>
/// Initializes a new instance of the <see cref="Contains"/> class.
/// </summary>
public Contains()
: base(ExpressionType.Contains, Evaluator, ReturnType.Boolean, FunctionUtils.ValidateBinary)
{
}
private static (object value, string error) Evaluator(Expression expression, IMemory state, Options options)
{
var found = false;
var (args, error) = FunctionUtils.EvaluateChildren(expression, state, options);
if (error == null)
{
if (args[0] is string string0 && args[1] is string string1)
{
found = string0.Contains(string1);
}
else if (FunctionUtils.TryParseList(args[0], out IList ilist))
{
// list to find a value
var operands = FunctionUtils.ResolveListValue(ilist);
found = operands.Contains(args[1]);
}
else if (args[1] is string string2)
{
found = FunctionUtils.TryAccessProperty((object)args[0], string2, out var _);
}
}
return (found, null);
}
}
}

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

@ -1,88 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using AdaptiveExpressions.Memory;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Convert a timestamp from Universal Time Coordinated (UTC) to a target time zone.
/// ConvertFromUtc takes a timestamp string, a timezone string,
/// an optional format string whose default value "yyyy-MM-ddTHH:mm:ss.fffZ"
/// and an optional locale string whose default value is Thread.CurrentThread.CurrentCulture.Name.
/// </summary>
internal class ConvertFromUtc : ExpressionEvaluator
{
/// <summary>
/// Initializes a new instance of the <see cref="ConvertFromUtc"/> class.
/// </summary>
public ConvertFromUtc()
: base(ExpressionType.ConvertFromUtc, Evaluator, ReturnType.String, Validator)
{
}
private static (object value, string error) Evaluator(Expression expression, IMemory state, Options options)
{
object value = null;
string error = null;
IReadOnlyList<object> args;
var locale = options.Locale != null ? new CultureInfo(options.Locale) : Thread.CurrentThread.CurrentCulture;
var format = FunctionUtils.DefaultDateTimeFormat;
(args, error) = FunctionUtils.EvaluateChildren(expression, state, options);
if (error == null)
{
(format, locale, error) = FunctionUtils.DetermineFormatAndLocale(args, format, locale, 4);
}
if (error == null)
{
if (args[1] is string targetTimeZone)
{
(value, error) = EvalConvertFromUTC(args[0], targetTimeZone, format, locale);
}
else
{
error = $"{expression} should contain an ISO format timestamp, a destination time zone string, an optional output format string and an optional locale string.";
}
}
return (value, error);
}
private static (string, string) EvalConvertFromUTC(object utcTimestamp, string timezone, string format, CultureInfo locale)
{
string error = null;
string result = null;
var utcDt = DateTime.UtcNow;
object parsed = null;
object convertedTimeZone = null;
(parsed, error) = FunctionUtils.NormalizeToDateTime(utcTimestamp);
if (error == null)
{
utcDt = ((DateTime)parsed).ToUniversalTime();
}
if (error == null)
{
(convertedTimeZone, error) = FunctionUtils.ConvertTimeZoneFormat(timezone);
if (error == null)
{
var convertedDateTime = TimeZoneInfo.ConvertTimeFromUtc(utcDt, (TimeZoneInfo)convertedTimeZone);
(result, error) = FunctionUtils.ReturnFormatTimeStampStr(convertedDateTime, format, locale);
}
}
return (result, error);
}
private static void Validator(Expression expression)
{
FunctionUtils.ValidateArityAndAnyType(expression, 2, 4, ReturnType.String);
}
}
}

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

@ -1,99 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using AdaptiveExpressions.Memory;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Convert a timestamp to Universal Time Coordinated (UTC) from the source time zone.
/// ConvertToUtc function takes a timestamp string, a timezone string,
/// an optional format string whose default value "yyyy-MM-ddTHH:mm:ss.fffZ"
/// and an optional locale string whose default value is Thread.CurrentThread.CurrentCulture.Name.
/// </summary>
internal class ConvertToUtc : ExpressionEvaluator
{
/// <summary>
/// Initializes a new instance of the <see cref="ConvertToUtc"/> class.
/// </summary>
public ConvertToUtc()
: base(ExpressionType.ConvertToUtc, Evaluator, ReturnType.String, Validator)
{
}
private static (object value, string error) Evaluator(Expression expression, IMemory state, Options options)
{
object value = null;
string error = null;
IReadOnlyList<object> args;
var locale = options.Locale != null ? new CultureInfo(options.Locale) : Thread.CurrentThread.CurrentCulture;
var format = FunctionUtils.DefaultDateTimeFormat;
(args, error) = FunctionUtils.EvaluateChildren(expression, state, options);
if (error == null)
{
(format, locale, error) = FunctionUtils.DetermineFormatAndLocale(args, format, locale, 4);
}
if (error == null)
{
if (args[1] is string sourceTimeZone)
{
(value, error) = EvalConvertToUTC(args[0], sourceTimeZone, format, locale);
}
else
{
error = $"{expression} should contain an ISO format timestamp, a origin time zone string, an optional output format string and an optional locale string.";
}
}
return (value, error);
}
private static (string, string) EvalConvertToUTC(object sourceTimestamp, string sourceTimezone, string format, CultureInfo locale)
{
string error = null;
string result = null;
var srcDt = DateTime.UtcNow;
try
{
if (sourceTimestamp is string st)
{
srcDt = DateTime.Parse(st, CultureInfo.InvariantCulture);
}
else
{
srcDt = (DateTime)sourceTimestamp;
}
}
#pragma warning disable CA1031 // Do not catch general exception types (we should probably do something about this but ignoring it for not)
catch
#pragma warning restore CA1031 // Do not catch general exception types
{
error = $"illegal time-stamp representation {sourceTimestamp}";
}
if (error == null)
{
object convertedTimeZone;
(convertedTimeZone, error) = FunctionUtils.ConvertTimeZoneFormat(sourceTimezone);
if (error == null)
{
var convertedDateTime = TimeZoneInfo.ConvertTimeToUtc(srcDt, (TimeZoneInfo)convertedTimeZone);
(result, error) = FunctionUtils.ReturnFormatTimeStampStr(convertedDateTime, format, locale);
}
}
return (result, error);
}
private static void Validator(Expression expression)
{
FunctionUtils.ValidateArityAndAnyType(expression, 2, 4, ReturnType.String);
}
}
}

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

@ -1,45 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections;
namespace AdaptiveExpressions.BuiltinFunctions
{
/// <summary>
/// Return the number of items in a collection.
/// </summary>
internal class Count : ExpressionEvaluator
{
/// <summary>
/// Initializes a new instance of the <see cref="Count"/> class.
/// </summary>
public Count()
: base(ExpressionType.Count, Evaluator(), ReturnType.Number, Validator)
{
}
private static EvaluateExpressionDelegate Evaluator()
{
return FunctionUtils.Apply(
args =>
{
object count = null;
if (args[0] is string string0)
{
count = string0.Length;
}
else if (args[0] is IList list)
{
count = list.Count;
}
return count;
}, FunctionUtils.VerifyContainer);
}
private static void Validator(Expression expression)
{
FunctionUtils.ValidateOrder(expression, null, ReturnType.String | ReturnType.Array);
}
}
}

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