Removing all other libraries
This commit is contained in:
Родитель
48affcdbe0
Коммит
670677dcdb
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Двоичные данные
libraries/Adapters/Microsoft.Bot.Builder.Adapters.Slack/icon.png
Двоичные данные
libraries/Adapters/Microsoft.Bot.Builder.Adapters.Slack/icon.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 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;
|
||||
}
|
||||
}
|
||||
}
|
Двоичные данные
libraries/Adapters/Microsoft.Bot.Builder.Adapters.Webex/icon.png
Двоичные данные
libraries/Adapters/Microsoft.Bot.Builder.Adapters.Webex/icon.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 1.9 KiB |
Двоичные данные
libraries/Adapters/icon.png
Двоичные данные
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);
|
||||
}
|
||||
}
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче