├── .gitignore ├── LICENSE ├── README.md ├── template-bot-master-csharp.sln └── template-bot-master-csharp ├── App_Start └── WebApiConfig.cs ├── DialogMatches.cs ├── Global.asax ├── Global.asax.cs ├── Properties ├── AssemblyInfo.cs ├── Strings.Designer.cs └── Strings.resx ├── Web.Debug.config ├── Web.Release.config ├── Web.config ├── composeExtensionSettings.html ├── default.htm ├── manifest ├── bot_blue.png └── manifest.json ├── middleware ├── AdaptiveCardSubmitActionHandler.cs ├── Middleware.cs └── StripBotAtMentions.cs ├── packages.config ├── popUpSignin.html ├── public ├── assets │ ├── ActionableCardIconImage.png │ ├── computer.jpg │ ├── computer_people.jpg │ ├── computer_person.jpg │ └── mascot.png └── tab │ └── tabConfig │ ├── index.html │ ├── tab.aspx │ ├── tab.aspx.cs │ └── tab.aspx.designer.cs ├── src ├── controllers │ └── MessagesController.cs └── dialogs │ ├── RootDialog.cs │ └── examples │ ├── auth │ ├── facebook │ │ ├── FacebookHelpers.cs │ │ ├── OAuthCallbackController.cs │ │ └── SimpleFacebookAuthDialog.cs │ └── vsts │ │ ├── OAuthCallbackVSTSController.cs │ │ ├── VSTSAPICallDialog.cs │ │ ├── VSTSGetworkItemDialog.cs │ │ └── VSTSHelpers.cs │ ├── basic │ ├── AdaptiveCardDialog.cs │ ├── GetLastDialogUsedDialog.cs │ ├── HelloDialog.cs │ ├── HeroCardDialog.cs │ ├── MessagebackDialog.cs │ ├── MultiDialog.cs │ ├── O365ConnectorCardActionsDialog.cs │ ├── O365ConnectorCardDialog.cs │ ├── PopupSigninCardDialog.cs │ └── ThumbnailcardDialog.cs │ ├── moderate │ ├── BeginDialogExampleDialog.cs │ ├── ListNamesDialog.cs │ ├── PromptDialog.cs │ ├── Quiz1Dialog.cs │ ├── Quiz2Dialog.cs │ └── QuizFullDialog.cs │ └── teams │ ├── AtMentionDialog.cs │ ├── DeepLinkStaticTabDialog.cs │ ├── DisplayCardsDialog.cs │ ├── FetchRosterDialog.cs │ ├── FetchTeamsInfoDialog.cs │ ├── HelpDialog.cs │ ├── ProactiveMsgTo1to1Dialog.cs │ ├── UpdateCardMsgDialog.cs │ ├── UpdateCardMsgSetupDialog.cs │ ├── UpdateTextMsgDialog.cs │ └── UpdateTextMsgSetupDialog.cs ├── template-bot-master-csharp.csproj └── utility ├── InvokeHandler.cs ├── TemplateUtility.cs └── WikipediaComposeExtension.cs /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | products: 4 | - office-teams 5 | - office-365 6 | languages: 7 | - csharp 8 | extensions: 9 | contentType: samples 10 | technologies: 11 | - Tabs 12 | - Microsoft Bot Framework 13 | createdDate: 9/22/2017 5:54:09 PM 14 | description: "Sample that shows how to build a bot for Microsoft Teams in C#." 15 | --- 16 | 17 | #### **NOTE:** This repository is now archived. Its contents have moved [here](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/app-complete-sample/csharp). 18 | 19 | # Microsoft Teams Bot in C# 20 | 21 | Sample that shows how to build a bot for Microsoft Teams in C#. 22 | 23 | ## Prerequisites 24 | 25 | * Install Git for windows: https://git-for-windows.github.io/ 26 | 27 | * Clone this repo:
28 | ```bash 29 | git clone https://github.com/OfficeDev/microsoft-teams-template-bot-CSharp.git 30 | ``` 31 | 32 | * Install Visual Studio and launch it as an administrator 33 | 34 | * Build the solution to download all configured NuGet packages 35 | 36 | * (Only needed if wanting to run in Microsoft Teams)
37 | Install some sort of tunnelling service. These instructions assume you are using ngrok: https://ngrok.com/ 38 | 39 | * (Only needed if wanting to run in the Bot Emulator)
40 | Install the Bot Emulator - click on "Bot Framework Emulator (Mac and Windows)": https://docs.botframework.com/en-us/downloads/#navtitle 41 | * NOTE: make sure to pin the emulator to your task bar because it can sometimes be difficult to find again 42 | 43 | ## Steps to see the bot running in the Bot Emulator 44 | 45 | NOTE: Teams does not work nor render things exactly like the Bot Emulator, but it is a quick way to see if your bot is running and functioning correctly. 46 | 47 | 1. Open the template-bot-master-csharp.sln solution with Visual Studio 48 | 49 | 2. In Visual Studio click the play button (should be defaulted to running the Microsoft Edge configuration) 50 | 51 | 3. Once the code is running, connect with the Bot Emulator to the default endpoint, "http://localhost:3979/api/messages", leaving "Microsoft App ID" and "Microsoft App Password" blank 52 | 53 | Congratulations!!! You can now chat with the bot in the Bot Emulator! 54 | 55 | ## Steps to see the full app in Microsoft Teams 56 | 57 | 1. Begin your tunnelling service to get an https endpoint. 58 | 59 | * Open a new **Command Prompt** window. 60 | 61 | * Change to the directory that contains the ngrok.exe application. 62 | 63 | * Run the command `ngrok http [port] --host-header=localhost` (you'll need the https endpoint for the bot registration) e.g.
64 | ``` 65 | ngrok http 3979 --host-header=localhost 66 | ``` 67 | 68 | * The ngrok application will fill the entire prompt window. Make note of the Forwarding address using https. This address is required in the next step. 69 | 70 | * Minimize the ngrok Command Prompt window. It is no longer referenced in this lab, but it must remain running. 71 | 72 | 73 | 74 | 2. Register a new bot (or update an existing one) with Bot Framework by using the https endpoint started by ngrok and the extension "/api/messages" as the full endpoint for the bot's "Messaging endpoint". e.g. "https://####abcd.ngrok.io/api/messages" - Bot registration is here (open in a new browser tab): https://dev.botframework.com/bots/new. Ignore the warning about migrating to Azure, it is not necessary for Teams-only bots. You can however safely migrate your bot to Azure if you so choose, or use the Azure portal to create your bot. 75 | 76 | > **NOTE**: When you create your bot you will create an App ID and App password - make sure you keep these for later. 77 | 78 | 3. You project needs to run with a configuration that matches your registered bot's configuration. To do this, you will need to update the web.config file: 79 | 80 | * In Visual Studio, open the Web.config file. Locate the `` section. 81 | 82 | * Enter the BotId value. The BotId is the **Bot handle** from the **Configuration** section of the bot registration. 83 | 84 | * Enter the MicrosoftAppId. The MicrosoftAppId is the app ID from the **Configuration** section of the bot registration. 85 | 86 | * Enter the MicrosoftAppPassword. The MicrosoftAppPassword is the auto-generated app password displayed in the pop-up during bot registration. 87 | 88 | * Enter the BaseUri. The BaseUri is the https endpoint generated from ngrok. 89 | 90 | Here is an example for reference: 91 | 92 | 93 | 94 | 95 | 96 | 97 | 4. In Visual Studio click the play button (should be defaulted to running the Microsoft Edge configuration) 98 | 99 | 5. Once the app is running, a manifest file is needed: 100 | * On the solution explorer of Visual Studio, navigate to the file, manifest/manifest.json - change: 101 | * <> (there are 3) change to your registered bot's app ID 102 | * <> (there are 2) change to your https endpoint from ngrok 103 | * <> (there is 1) change to your https endpoint from ngrok excluding the "https://" part 104 | 105 | * Save the file and zip this file and the bot_blue.png file (located next to it) together to create a manifest.zip file 106 | 107 | 6. Once complete, sideload your zipped manifest to a team as described here (open in a new browser tab): https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/deploy-and-publish/apps-upload 108 | 109 | Congratulations!!! You have just created and sideloaded your first Microsoft Teams app! Try adding a configurable tab, at-mentioning your bot by its registered name, or viewing your static tabs.

110 | NOTE: Most of this sample app's functionality will now work. The only limitations are the authentication examples because your app is not registered with AAD nor Visual Studio Team Services. 111 | 112 | ## Overview 113 | 114 | This project is meant to help a Teams developer in two ways. First, it is meant to show many examples of how an app can integrate into Teams. Second, it is meant to give a set of patterns, templates, and tools that can be used as a starting point for creating a larger, scalable, more enterprise level bot to work within Teams. Although this project focuses on creating a robust bot, it does include simples examples of tabs as well as examples of how a bot can give links into these tabs. 115 | 116 | ## What it is 117 | 118 | At a high level, this project is written in C#, built to run a .Net, and uses the BotFramework to handle the bot's requests and responses. This project is designed to be run in Visual Studio using its debugger in order to leverage breakpoints. Most directories will hold a README file which will describe what the files within that directory do. 119 | The easiest way to get started is to follow the steps listed in the "Steps to get started running the Bot Emulator". Once this is complete and running, the easiest way to add your own content is to create a new dialog in src/dialogs by copying one from src/dialogs/examples, change it accordingly, and then instantiate it with the others in the RootDialog.cs. 120 | 121 | ## General Architecture 122 | 123 | Most code files that need to be compile reside in the src directory. Most files outside of the src directory are static files used for either configuration or for providing static resources to tabs, e.g. images and html. 124 | 125 | ## Files and Directories 126 | 127 | * **manifest**

128 | This directory holds the skeleton of a manifest.json file that can be altered in order sideload this application into a team. 129 | 130 | * **middleware**

131 | This directory holds the stripping at mention for channel class and Invoke message processing. 132 | 133 | * **public**

134 | This directory holds static html, image, and javascript files used by the tabs and bot. This is not the only public directory that is used for the tabs, though. This directory holds the html and javascript used for the configuration page of the configurable tab. The main content of the static and configurable comes from the static files placed in /public/tab/tabConfig. 135 | 136 | * **src**

137 | This directory holds all the code files, which run the entire application. 138 | 139 | * **utility**

140 | This directory holds utility functions for the project. 141 | 142 | * **web.config**

143 | This file is a configuration file that can be used to update the config keys globally used in Application. 144 | 145 | ## Contributing 146 | 147 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 148 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 149 | the rights to use your contribution. For details, visit https://cla.microsoft.com. 150 | 151 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide 152 | a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions 153 | provided by the bot. You will only need to do this once across all repos using our CLA. 154 | 155 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 156 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 157 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 158 | -------------------------------------------------------------------------------- /template-bot-master-csharp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "template-bot-master-csharp", "template-bot-master-csharp\template-bot-master-csharp.csproj", "{A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {A8BA1066-5695-4D71-ABB4-65E5A5E0C3D4}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /template-bot-master-csharp/App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Serialization; 4 | 5 | namespace Microsoft.Teams.TemplateBotCSharp 6 | { 7 | public static class WebApiConfig 8 | { 9 | public static void Register(HttpConfiguration config) 10 | { 11 | // Json settings 12 | config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; 13 | config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 14 | config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented; 15 | JsonConvert.DefaultSettings = () => new JsonSerializerSettings() 16 | { 17 | ContractResolver = new CamelCasePropertyNamesContractResolver(), 18 | Formatting = Newtonsoft.Json.Formatting.Indented, 19 | NullValueHandling = NullValueHandling.Ignore, 20 | }; 21 | 22 | // Web API configuration and services 23 | 24 | // Web API routes 25 | config.MapHttpAttributeRoutes(); 26 | 27 | config.Routes.MapHttpRoute( 28 | name: "DefaultApi", 29 | routeTemplate: "api/{controller}/{id}", 30 | defaults: new { id = RouteParameter.Optional } 31 | ); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /template-bot-master-csharp/DialogMatches.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Teams.TemplateBotCSharp 2 | { 3 | /// 4 | /// This contains the matching expression 5 | /// commands to trigger the individual dialogs in RootDialog 6 | /// 7 | public static class DialogMatches 8 | { 9 | public const string FetchRosterApiMatch = "names"; 10 | 11 | public const string FetchRosterPayloadMatch = "roster"; 12 | 13 | public const string PromptFlowGameMatch = "prompt"; 14 | 15 | public const string RunQuizQuestionsMatch = "quiz"; 16 | 17 | public const string DialogFlowMatch = "dialog flow"; 18 | 19 | public const string HelloDialogMatch1 = "hi"; 20 | public const string HelloDialogMatch2 = "hello"; 21 | 22 | public const string AtMentionMatch1 = "at mention"; 23 | public const string AtMentionMatch2 = "atmention"; 24 | public const string AtMentionMatch3 = "at-mention"; 25 | 26 | public const string Help = "help"; 27 | 28 | public const string MultiDialog1Match1 = "multi dialog 1"; 29 | 30 | public const string MultiDialog2Match = "multi dialog 2"; 31 | 32 | public const string FecthLastExecutedDialogMatch = "last dialog"; 33 | 34 | public const string Send1to1Conversation = "send message to 1:1"; 35 | 36 | public const string SetUpTextMsg = "setup text message"; 37 | public const string UpdateLastSetupTextMsg = "update text message"; 38 | 39 | public const string SetUpCardMsg = "setup card message"; 40 | 41 | public const string DisplayCards = "display cards"; 42 | public const string StopShowingCards = "no"; 43 | 44 | public const string DeepLinkTabCard = "deep link"; 45 | 46 | public const string AuthSample = "auth"; 47 | 48 | public const string Facebooklogin= "fblogin"; 49 | public const string Facebooklogout = "fblogout"; 50 | 51 | public const string VSTSlogin = "vstslogin"; 52 | public const string VSTSlogout = "vstslogout"; 53 | 54 | public const string VSTSApi = "vstsapi"; 55 | 56 | public const string MessageBack = "msgback"; 57 | 58 | public const string LocalTime = "localtime"; 59 | 60 | public const string HeroCard = "hero card"; 61 | public const string ThumbnailCard = "thumbnail card"; 62 | 63 | public const string O365ConnectorCardDefault = "connector card"; 64 | public const string O365ConnectorCards = "connector card (.*)"; 65 | 66 | public const string O365ConnectorCardActionableCardDefault = "connector card actions"; 67 | public const string O365ConnectorCardActionableCards = "connector card actions (.*)"; 68 | 69 | public const string PopUpSignIn = "signin"; 70 | 71 | public const string TeamInfo = "team info"; 72 | 73 | public const string UpdateCard = "update card message"; 74 | 75 | public const string AdaptiveCard = "adaptive card"; 76 | } 77 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="Microsoft.Teams.TemplateBotCSharp.WebApiApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /template-bot-master-csharp/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using Microsoft.Bot.Builder.Dialogs; 3 | using Microsoft.Bot.Builder.Dialogs.Internals; 4 | using Microsoft.Bot.Connector; 5 | using System.Web.Http; 6 | 7 | namespace Microsoft.Teams.TemplateBotCSharp 8 | { 9 | public class WebApiApplication : System.Web.HttpApplication 10 | { 11 | protected void Application_Start() 12 | { 13 | GlobalConfiguration.Configure(WebApiConfig.Register); 14 | 15 | // Use an in-memory store for bot data. 16 | // This registers a IBotDataStore singleton that will be used throughout the app. 17 | var store = new InMemoryDataStore(); 18 | 19 | Conversation.UpdateContainer(builder => 20 | { 21 | builder.Register(c => new CachingBotDataStore(store, 22 | CachingBotDataStoreConsistencyPolicy 23 | .LastWriteWins)) 24 | .As>() 25 | .AsSelf() 26 | .InstancePerLifetimeScope(); 27 | }); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /template-bot-master-csharp/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Microsoft.Teams.TemplateBotCSharp")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Microsoft.Teams.TemplateBotCSharp")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("a1a5f2f5-3a75-44df-af34-a48f224abb8e")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /template-bot-master-csharp/Web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /template-bot-master-csharp/Web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /template-bot-master-csharp/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /template-bot-master-csharp/composeExtensionSettings.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Bot Info 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

I prefer:

13 | 14 | 15 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /template-bot-master-csharp/default.htm: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 |

Microsoft.Teams.TemplateBotCSharp

9 |

Describe your bot here and your terms of use etc.

10 |

Visit Bot Framework to register your bot. When you register it, remember to set your bot's endpoint to

https://your_bots_hostname/api/messages

11 | 12 | 13 | -------------------------------------------------------------------------------- /template-bot-master-csharp/manifest/bot_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-sample-complete-csharp/6c0a4e6992490d0ff71368980a14bb74e6d79dc6/template-bot-master-csharp/manifest/bot_blue.png -------------------------------------------------------------------------------- /template-bot-master-csharp/manifest/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.7/MicrosoftTeams.schema.json", 3 | "manifestVersion": "1.7", 4 | "version": "1.5", 5 | "id": "<>", 6 | "packageName": "com.skype.teams.samples.sampleapp", 7 | "developer": { 8 | "name": "Microsoft", 9 | "websiteUrl": "https://www.microsoft.com", 10 | "privacyUrl": "https://www.microsoft.com/privacy", 11 | "termsOfUseUrl": "https://www.microsoft.com/termsofuse" 12 | }, 13 | "name": { 14 | "short": "Sample-App-csharp", 15 | "full": "Sample-App-csharp" 16 | }, 17 | "description": { 18 | "short": "This is a small sample app we made for you!", 19 | "full": "This is a small sample app we made for you! This app has samples of all capabilities MS Teams supports." 20 | }, 21 | "icons": { 22 | "outline": "bot_blue.png", 23 | "color": "bot_blue.png" 24 | }, 25 | "accentColor": "#3F487F", 26 | "staticTabs": [ 27 | { 28 | "contentUrl": "<>/public/tab/tabConfig/tab.aspx", 29 | "entityId": "statictab", 30 | "name": "Bot Info", 31 | "scopes": [ 32 | "team", 33 | "personal" 34 | ] 35 | } 36 | ], 37 | "configurableTabs": [ 38 | { 39 | "configurationUrl": "<>/public/tab/tabConfig/index.html", 40 | "canUpdateConfiguration": false, 41 | "scopes": [ "team", "groupchat" ] 42 | } 43 | ], 44 | "bots": [ 45 | { 46 | "botId": "<>", 47 | "isNotificationOnly": false, 48 | "scopes": [ "team", "personal", "groupchat" ], 49 | "commandLists": [ 50 | { 51 | "scopes": [ 52 | "team" 53 | ], 54 | "commands": [ 55 | { 56 | "title": "help", 57 | "description": "To show the list of command for user interactions" 58 | }, 59 | { 60 | "title": "hello", 61 | "description": "Runs the simplest hello dialog" 62 | }, 63 | { 64 | "title": "multi dialog 2", 65 | "description": "Displays a card with invoke buttons" 66 | }, 67 | { 68 | "title": "roster", 69 | "description": "Fetches the full roster payload for the current conversation" 70 | }, 71 | { 72 | "title": "names", 73 | "description": "Lists the names of the users for the current conversation" 74 | }, 75 | { 76 | "title": "last dialog", 77 | "description": "Shows which dialog sent the last message" 78 | }, 79 | { 80 | "title": "send message to 1:1", 81 | "description": "Send the user a 1:1 message" 82 | }, 83 | { 84 | "title": "setup text message", 85 | "description": "Sets up a text message that can be updated" 86 | }, 87 | { 88 | "title": "update text message", 89 | "description": "Updates a text message once it is setup to be updated" 90 | }, 91 | { 92 | "title": "setup card message", 93 | "description": "Sets up a card that can be updated" 94 | } 95 | ] 96 | }, 97 | { 98 | "scopes": [ 99 | "personal" 100 | ], 101 | "commands": [ 102 | { 103 | "title": "help", 104 | "description": "To show the list of command for user interactions" 105 | }, 106 | { 107 | "title": "hello", 108 | "description": "Runs the simplest hello dialog" 109 | }, 110 | { 111 | "title": "multi dialog 2", 112 | "description": "Displays a card with invoke buttons" 113 | }, 114 | { 115 | "title": "roster", 116 | "description": "Fetches the full roster payload for the current conversation" 117 | }, 118 | { 119 | "title": "names", 120 | "description": "Lists the names of the users for the current conversation" 121 | }, 122 | { 123 | "title": "last dialog", 124 | "description": "Shows which dialog sent the last message" 125 | }, 126 | { 127 | "title": "send message to 1:1", 128 | "description": "Send the user a 1:1 message" 129 | }, 130 | { 131 | "title": "setup text message", 132 | "description": "Sets up a text message that can be updated" 133 | }, 134 | { 135 | "title": "update text message", 136 | "description": "Updates a text message once it is setup to be updated" 137 | }, 138 | { 139 | "title": "setup card message", 140 | "description": "Sets up a card that can be updated" 141 | } 142 | ] 143 | } 144 | ] 145 | } 146 | ], 147 | "composeExtensions": [ 148 | { 149 | "botId": "<>", 150 | "canUpdateConfiguration": true, 151 | "commands": [ 152 | { 153 | "id": "search123", 154 | "description": "Find a card", 155 | "title": "Search", 156 | "initialRun": true, 157 | "parameters": [ 158 | { 159 | "title": "query123", 160 | "name": "query", 161 | "description": "Search string" 162 | } 163 | ] 164 | } 165 | ] 166 | } 167 | ], 168 | "permissions": [ 169 | "identity", 170 | "messageTeamMembers" 171 | ], 172 | "validDomains": [ 173 | "<>" 174 | ] 175 | } 176 | -------------------------------------------------------------------------------- /template-bot-master-csharp/middleware/AdaptiveCardSubmitActionHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Connector; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | 5 | namespace Microsoft.Teams.TemplateBotCSharp.Utility 6 | { 7 | public static partial class Middleware 8 | { 9 | public static string AdaptiveCardActionKey = "dialog"; 10 | 11 | /// 12 | /// Set activity text to "adaptive card", if request is from an adaptive card 13 | /// 14 | /// 15 | /// 16 | public static Activity AdaptiveCardSubmitActionHandler(Activity activity) 17 | { 18 | // Check event text is blank, replyToId is not null, event value has isFromAdaptiveCard and messageText in incoming payload to check if the incoming 19 | // payload is from a Submit action button click from an AdaptiveCard (this is set in …\src\dialogs\examples\basic\AdaptiveCardDialog.cs) in the Submit action 20 | // data field. If so, then set the text field of the incoming payload so the BotFramework regex recognizers will route the message to the desired dialog. 21 | if (string.IsNullOrEmpty(activity.Text) && activity.ReplyToId != null && activity?.Value != null) 22 | { 23 | JObject jsonObject = activity.Value as JObject; 24 | 25 | if (jsonObject != null && jsonObject.Count > 0) 26 | { 27 | string isFromAdaptiveCard = Convert.ToString(jsonObject["isFromAdaptiveCard"]); 28 | string messageText = Convert.ToString(jsonObject["messageText"]); 29 | 30 | if (!string.IsNullOrEmpty(isFromAdaptiveCard) && isFromAdaptiveCard == "true" && !string.IsNullOrEmpty(messageText)) 31 | { 32 | // set activity text "adaptive card" 33 | activity.Text = messageText; 34 | } 35 | } 36 | } 37 | 38 | return activity; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/middleware/Middleware.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Connector; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Configuration; 5 | 6 | namespace Microsoft.Teams.TemplateBotCSharp.Utility 7 | { 8 | public static partial class Middleware 9 | { 10 | public static string TenantFilterSettingAny = "#ANY#"; 11 | 12 | /// 13 | /// Here are below scenarios - 14 | /// #Scenario 1 - Reject the Bot If Tenant is configured in web.config and doesn't match with Incoming request tenant 15 | /// #Scenario 2 - Allow Bot for every Tenant if Tenant is not configured in web.config file and default value is #ANY# 16 | /// 17 | /// 18 | /// 19 | /// 20 | public static bool RejectMessageBasedOnTenant(IMessageActivity activity, string currentTenant) 21 | { 22 | if (!String.Equals(ConfigurationManager.AppSettings["OFFICE_365_TENANT_FILTER"], TenantFilterSettingAny)) 23 | { 24 | //#Scenario 1 25 | return !string.Equals(ConfigurationManager.AppSettings["OFFICE_365_TENANT_FILTER"], currentTenant); 26 | } 27 | else 28 | { 29 | //Scenario 2 30 | return false; 31 | } 32 | } 33 | 34 | public static Activity ConvertActivityTextToLower(Activity activity) 35 | { 36 | //Convert input command in lower case for 1To1 and Channel users 37 | if (activity.Text != null) 38 | { 39 | activity.Text = activity.Text.ToLower(); 40 | } 41 | 42 | return activity; 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/middleware/StripBotAtMentions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Connector; 2 | using System; 3 | 4 | namespace Microsoft.Teams.TemplateBotCSharp.Utility 5 | { 6 | public static partial class Middleware 7 | { 8 | public static Activity StripAtMentionText(Activity activity) 9 | { 10 | if (activity == null) 11 | { 12 | throw new ArgumentNullException(nameof(activity)); 13 | } 14 | 15 | foreach (var m in activity.GetMentions()) 16 | { 17 | if (m.Mentioned.Id == activity.Recipient.Id) 18 | { 19 | //Bot is in the @mention list. 20 | //The below example will strip the bot name out of the message, so you can parse it as if it wasn't included. 21 | //Note that the Text object will contain the full bot name, if applicable. 22 | if (m.Text != null) 23 | activity.Text = activity.Text.Replace(m.Text, "").Trim(); 24 | } 25 | } 26 | 27 | return activity; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /template-bot-master-csharp/popUpSignin.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | User Name : 10 |
11 |
12 |
13 | Password : 14 |
15 | 16 | 17 | 18 | 19 | 20 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /template-bot-master-csharp/public/assets/ActionableCardIconImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-sample-complete-csharp/6c0a4e6992490d0ff71368980a14bb74e6d79dc6/template-bot-master-csharp/public/assets/ActionableCardIconImage.png -------------------------------------------------------------------------------- /template-bot-master-csharp/public/assets/computer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-sample-complete-csharp/6c0a4e6992490d0ff71368980a14bb74e6d79dc6/template-bot-master-csharp/public/assets/computer.jpg -------------------------------------------------------------------------------- /template-bot-master-csharp/public/assets/computer_people.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-sample-complete-csharp/6c0a4e6992490d0ff71368980a14bb74e6d79dc6/template-bot-master-csharp/public/assets/computer_people.jpg -------------------------------------------------------------------------------- /template-bot-master-csharp/public/assets/computer_person.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-sample-complete-csharp/6c0a4e6992490d0ff71368980a14bb74e6d79dc6/template-bot-master-csharp/public/assets/computer_person.jpg -------------------------------------------------------------------------------- /template-bot-master-csharp/public/assets/mascot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/microsoft-teams-sample-complete-csharp/6c0a4e6992490d0ff71368980a14bb74e6d79dc6/template-bot-master-csharp/public/assets/mascot.png -------------------------------------------------------------------------------- /template-bot-master-csharp/public/tab/tabConfig/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 26 | 27 | 28 |

Press save to create your tab

29 | 30 | 31 | -------------------------------------------------------------------------------- /template-bot-master-csharp/public/tab/tabConfig/tab.aspx: -------------------------------------------------------------------------------- 1 | <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="tab.aspx.cs" Inherits="Microsoft.Teams.TemplateBotCSharp.src.tab.tab" %> 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 15 | 16 |
    17 | 18 | 19 |
  • 20 | 21 |
  • 22 |
    23 | 24 |
25 | 26 |
27 |
28 |
29 |
30 | 31 |
32 | 35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /template-bot-master-csharp/public/tab/tabConfig/tab.aspx.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using System.Web.UI.WebControls; 5 | 6 | namespace Microsoft.Teams.TemplateBotCSharp.src.tab 7 | { 8 | public partial class tab : System.Web.UI.Page 9 | { 10 | private bool IsValidFilePath { get; set; } 11 | protected void Page_Load(object sender, EventArgs e) 12 | { 13 | if (!IsPostBack) 14 | { 15 | string DirectoryName = Request.MapPath("~/src/dialogs/"); 16 | if (Directory.Exists(DirectoryName)) 17 | { 18 | string[] sourceCodeFiles = Directory.GetFiles(DirectoryName, "*.*", SearchOption.AllDirectories); 19 | ListOfCodeFiles.DataSource = sourceCodeFiles; 20 | ListOfCodeFiles.DataBind(); 21 | } 22 | } 23 | } 24 | protected void ListOfCodeFiles_OnItemDataBound(object sender, RepeaterItemEventArgs e) 25 | { 26 | if (e.Item.ItemType == ListItemType.AlternatingItem || e.Item.ItemType == ListItemType.Item) 27 | { 28 | LinkButton fileNameLink = (LinkButton)e.Item.FindControl("nameOfFile"); 29 | String fullName = (String)e.Item.DataItem; 30 | fileNameLink.Text = fullName.Substring(fullName.LastIndexOf("\\") + 1); 31 | fileNameLink.CommandArgument = fullName.Substring(fullName.LastIndexOf("\\") + 1); 32 | } 33 | } 34 | protected void ListOfCodeFiles_OnItemCommand(object sender, RepeaterCommandEventArgs e) 35 | { 36 | StringBuilder fileCodeLines= new StringBuilder(); 37 | 38 | if (e.CommandName == "GOTO") 39 | { 40 | string[] dirs = Directory.GetDirectories(Request.MapPath("~/src/dialogs/"), "*", SearchOption.AllDirectories); 41 | 42 | if (dirs.Length > 0) 43 | { 44 | foreach (string dirName in dirs) 45 | { 46 | string dirPath = dirName.Substring(dirName.IndexOf("src"), (dirName.Length - dirName.IndexOf("src"))); 47 | dirPath = "~/" + dirPath.Replace("\\", "/") + "/"; 48 | 49 | fileCodeLines = ReadFileContentFromPath(Request.MapPath(dirPath) + (String)e.CommandArgument); 50 | 51 | //No need to read the files once we got the content 52 | if(fileCodeLines.Length!=0) 53 | { 54 | break; 55 | } 56 | } 57 | 58 | if (!IsValidFilePath) 59 | { 60 | fileCodeLines = ReadFileContentFromPath(Request.MapPath("~/src/dialogs/") + (String)e.CommandArgument); 61 | } 62 | 63 | fileName.Text = (String)e.CommandArgument; 64 | 65 | divFileContent.Style.Add("display", "block"); 66 | fileContent.Text = fileCodeLines.ToString(); 67 | } 68 | } 69 | } 70 | 71 | /// 72 | /// Read the source file content 73 | /// 74 | /// 75 | /// 76 | private StringBuilder ReadFileContentFromPath(string filePath) 77 | { 78 | StringBuilder fileContent = new StringBuilder(); 79 | try 80 | { 81 | string[] codeLines = File.ReadAllLines(filePath); 82 | IsValidFilePath = true; 83 | 84 | foreach (string line in codeLines) 85 | { 86 | fileContent.Append("
"); 87 | fileContent.Append("\t" + line); 88 | } 89 | } 90 | catch 91 | { 92 | // Ignore the File here 93 | } 94 | 95 | return fileContent; 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/public/tab/tabConfig/tab.aspx.designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // 5 | // Changes to this file may cause incorrect behavior and will be lost if 6 | // the code is regenerated. 7 | // 8 | //------------------------------------------------------------------------------ 9 | 10 | namespace Microsoft.Teams.TemplateBotCSharp.src.tab { 11 | 12 | 13 | public partial class tab { 14 | 15 | /// 16 | /// form1 control. 17 | /// 18 | /// 19 | /// Auto-generated field. 20 | /// To modify move field declaration from designer file to code-behind file. 21 | /// 22 | protected global::System.Web.UI.HtmlControls.HtmlForm form1; 23 | 24 | /// 25 | /// ListOfCodeFiles control. 26 | /// 27 | /// 28 | /// Auto-generated field. 29 | /// To modify move field declaration from designer file to code-behind file. 30 | /// 31 | protected global::System.Web.UI.WebControls.Repeater ListOfCodeFiles; 32 | 33 | /// 34 | /// fileName control. 35 | /// 36 | /// 37 | /// Auto-generated field. 38 | /// To modify move field declaration from designer file to code-behind file. 39 | /// 40 | protected global::System.Web.UI.WebControls.Label fileName; 41 | 42 | /// 43 | /// divFileContent control. 44 | /// 45 | /// 46 | /// Auto-generated field. 47 | /// To modify move field declaration from designer file to code-behind file. 48 | /// 49 | protected global::System.Web.UI.HtmlControls.HtmlGenericControl divFileContent; 50 | 51 | /// 52 | /// fileContent control. 53 | /// 54 | /// 55 | /// Auto-generated field. 56 | /// To modify move field declaration from designer file to code-behind file. 57 | /// 58 | protected global::System.Web.UI.WebControls.Label fileContent; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /template-bot-master-csharp/src/controllers/MessagesController.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using Microsoft.Bot.Builder.Dialogs; 3 | using Microsoft.Bot.Builder.Dialogs.Internals; 4 | using Microsoft.Bot.Connector; 5 | using Microsoft.Bot.Connector.Teams; 6 | using Microsoft.Bot.Connector.Teams.Models; 7 | using Microsoft.Teams.TemplateBotCSharp.Dialogs; 8 | using Microsoft.Teams.TemplateBotCSharp.Properties; 9 | using Microsoft.Teams.TemplateBotCSharp.Utility; 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Linq; 13 | using System.Net; 14 | using System.Net.Http; 15 | using System.Threading; 16 | using System.Threading.Tasks; 17 | using System.Web.Http; 18 | 19 | namespace Microsoft.Teams.TemplateBotCSharp 20 | { 21 | [BotAuthentication] 22 | public class MessagesController : ApiController 23 | { 24 | /// 25 | /// POST: api/Messages 26 | /// Receive a message from a user and reply to it 27 | /// 28 | public async Task Post([FromBody]Activity activity, CancellationToken cancellationToken) 29 | { 30 | var connectorClient = new ConnectorClient(new Uri(activity.ServiceUrl)); 31 | 32 | if (activity.Type == ActivityTypes.Message) 33 | { 34 | // Special handling for a command to simulate a reset of the bot chat 35 | if (!(activity.Conversation.IsGroup ?? false) && (activity.Text == "/resetbotchat")) 36 | { 37 | return await HandleResetBotChatAsync(activity, cancellationToken); 38 | } 39 | 40 | //Set the Locale for Bot 41 | activity.Locale = TemplateUtility.GetLocale(activity); 42 | 43 | //Strip At mention from incoming request text 44 | activity = Middleware.StripAtMentionText(activity); 45 | 46 | //Convert incoming activity text to lower case, to match the intent irrespective of incoming text case 47 | activity = Middleware.ConvertActivityTextToLower(activity); 48 | 49 | //Set the OFFICE_365_TENANT_FILTER key in web.config file with Tenant Information 50 | //Validate bot for specific teams tenant if any 51 | if (Middleware.RejectMessageBasedOnTenant(activity, activity.GetTenantId())) 52 | { 53 | Activity replyActivity = activity.CreateReply(); 54 | replyActivity.Text = Strings.TenantLevelDeniedAccess; 55 | 56 | await connectorClient.Conversations.ReplyToActivityAsync(replyActivity); 57 | return Request.CreateResponse(HttpStatusCode.OK); 58 | } 59 | 60 | // Set activity text if request is from an adaptive card submit action 61 | activity = Middleware.AdaptiveCardSubmitActionHandler(activity); 62 | 63 | await Conversation.SendAsync(activity, () => new RootDialog()); 64 | } 65 | else if (activity.Type == ActivityTypes.MessageReaction) 66 | { 67 | var reactionsAdded = activity.ReactionsAdded; 68 | var reactionsRemoved = activity.ReactionsRemoved; 69 | var replytoId = activity.ReplyToId; 70 | Activity reply; 71 | 72 | if (reactionsAdded != null && reactionsAdded.Count > 0) 73 | { 74 | reply = activity.CreateReply(Strings.LikeMessage); 75 | await connectorClient.Conversations.ReplyToActivityAsync(reply); 76 | } 77 | else if (reactionsRemoved != null && reactionsRemoved.Count > 0) 78 | { 79 | reply = activity.CreateReply(Strings.RemoveLike); 80 | await connectorClient.Conversations.ReplyToActivityAsync(reply); 81 | } 82 | 83 | return Request.CreateResponse(HttpStatusCode.OK); 84 | } 85 | else if (activity.Type == ActivityTypes.Invoke) // Received an invoke 86 | { 87 | // Handle ComposeExtension query 88 | if (activity.IsComposeExtensionQuery()) 89 | { 90 | WikipediaComposeExtension wikipediaComposeExtension = new WikipediaComposeExtension(); 91 | HttpResponseMessage httpResponse = null; 92 | 93 | using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity)) 94 | { 95 | var botDataStore = scope.Resolve>(); 96 | // Handle compose extension selected item 97 | if (activity.Name == "composeExtension/selectItem") 98 | { 99 | // This handler is used to process the event when a user in Teams selects wiki item from wiki result 100 | ComposeExtensionResponse selectedItemResponse = await wikipediaComposeExtension.HandleComposeExtensionSelectedItem(activity, botDataStore); 101 | httpResponse = Request.CreateResponse(HttpStatusCode.OK, selectedItemResponse); 102 | } 103 | else 104 | { 105 | // Handle the wiki compose extension request and returned the wiki result response 106 | ComposeExtensionResponse composeExtensionResponse = await wikipediaComposeExtension.GetComposeExtensionResponse(activity, botDataStore); 107 | httpResponse = Request.CreateResponse(HttpStatusCode.OK, composeExtensionResponse); 108 | } 109 | 110 | var address = Address.FromActivity(activity); 111 | await botDataStore.FlushAsync(address, CancellationToken.None); 112 | } 113 | return httpResponse; 114 | } 115 | //Actionable Message 116 | else if (activity.IsO365ConnectorCardActionQuery()) 117 | { 118 | // this will handle the request coming any action on Actionable messages 119 | return await HandleO365ConnectorCardActionQuery(activity); 120 | } 121 | //PopUp SignIn 122 | else if (activity.Name == "signin/verifyState") 123 | { 124 | // this will handle the request coming from PopUp SignIn 125 | return await PopUpSignInHandler(activity); 126 | } 127 | // Handle rest of the invoke request 128 | else 129 | { 130 | var messageActivity = (IMessageActivity)null; 131 | 132 | //this will parse the invoke value and change the message activity as well 133 | messageActivity = InvokeHandler.HandleInvokeRequest(activity); 134 | 135 | await Conversation.SendAsync(messageActivity, () => new Dialogs.RootDialog()); 136 | 137 | return Request.CreateResponse(HttpStatusCode.OK); 138 | } 139 | } 140 | else 141 | { 142 | await HandleSystemMessageAsync(activity, connectorClient, cancellationToken); 143 | } 144 | 145 | var response = Request.CreateResponse(HttpStatusCode.OK); 146 | 147 | return response; 148 | } 149 | 150 | private async Task HandleSystemMessageAsync(Activity message, ConnectorClient connectorClient, CancellationToken cancellationToken) 151 | { 152 | if (message.Type == ActivityTypes.DeleteUserData) 153 | { 154 | // Implement user deletion here 155 | // If we handle user deletion, return a real message 156 | } 157 | else if (message.Type == ActivityTypes.ConversationUpdate) 158 | { 159 | // This shows how to send a welcome message in response to a conversationUpdate event 160 | 161 | // We're only interested in member added events 162 | if (message.MembersAdded?.Count > 0) 163 | { 164 | // Determine if the bot was added to the team/conversation 165 | var botId = message.Recipient.Id; 166 | var botWasAdded = message.MembersAdded.Any(member => member.Id == botId); 167 | 168 | // Create the welcome message to send 169 | Activity welcomeMessage = message.CreateReply(); 170 | welcomeMessage.Text = Strings.BotWelcomeMessage; 171 | 172 | if (!(message.Conversation.IsGroup ?? false)) 173 | { 174 | // 1:1 conversation event 175 | 176 | // If the user hasn't received a first-run message yet, then send a message to the user 177 | // introducing your bot and what it can do. Do NOT send this blindly, as your bot can receive 178 | // spurious conversationUpdate events, especially if you use proactive messaging. 179 | using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) 180 | { 181 | var address = Address.FromActivity(message); 182 | var botDataStore = scope.Resolve>(); 183 | var botData = await botDataStore.LoadAsync(address, BotStoreType.BotUserData, cancellationToken); 184 | 185 | if (!botData.GetProperty("IsFreSent")) 186 | { 187 | await connectorClient.Conversations.ReplyToActivityWithRetriesAsync(welcomeMessage, cancellationToken); 188 | 189 | // Remember that we sent the welcome message already 190 | botData.SetProperty("IsFreSent", true); 191 | await botDataStore.SaveAsync(address, BotStoreType.BotUserData, botData, cancellationToken); 192 | } 193 | else 194 | { 195 | // First-run message has already been sent, so skip sending it again. 196 | // Do not remove the check for IsFreSent above. Your bot can receive spurious conversationUpdate 197 | // activities from chat service, so if you always respond to all of them, you will send random 198 | // welcome messages to users who have already received the welcome. 199 | } 200 | } 201 | } 202 | else 203 | { 204 | // Not 1:1 chat event (bot or user was added to a team or group chat) 205 | if (botWasAdded) 206 | { 207 | // Bot was added to the team 208 | // Send a message to the team's channel, introducing your bot and what you can do 209 | await connectorClient.Conversations.ReplyToActivityWithRetriesAsync(welcomeMessage, cancellationToken); 210 | } 211 | else 212 | { 213 | // Other users were added to the team/conversation 214 | } 215 | } 216 | } 217 | } 218 | else if (message.Type == ActivityTypes.ContactRelationUpdate) 219 | { 220 | // Handle add/remove from contact lists 221 | // Activity.From + Activity.Action represent what happened 222 | } 223 | else if (message.Type == ActivityTypes.Typing) 224 | { 225 | // Handle knowing that the user is typing 226 | } 227 | else if (message.Type == ActivityTypes.Ping) 228 | { 229 | } 230 | } 231 | 232 | /// 233 | /// Handles a request from the user to simulate a new chat. 234 | /// 235 | /// The incoming message requesting the reset 236 | /// The cancellation token 237 | /// 238 | private async Task HandleResetBotChatAsync(Activity message, CancellationToken cancellationToken) 239 | { 240 | // Forget everything we know about the user 241 | using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) 242 | { 243 | var address = Address.FromActivity(message); 244 | var botDataStore = scope.Resolve>(); 245 | await botDataStore.SaveAsync(address, BotStoreType.BotUserData, new BotData("*"), cancellationToken); 246 | await botDataStore.SaveAsync(address, BotStoreType.BotConversationData, new BotData("*"), cancellationToken); 247 | await botDataStore.SaveAsync(address, BotStoreType.BotPrivateConversationData, new BotData("*"), cancellationToken); 248 | } 249 | 250 | // If you need to reset the user state in other services your app uses, do it here. 251 | 252 | // Synthesize a conversation update event and simulate the bot receiving it 253 | // Note that this is a fake event, as Teams does not support deleting a 1:1 conversation and re-creating it 254 | var conversationUpdateMessage = new Activity { 255 | Type = ActivityTypes.ConversationUpdate, 256 | Id = message.Id, 257 | ServiceUrl = message.ServiceUrl, 258 | From = message.From, 259 | Recipient = message.Recipient, 260 | Conversation = message.Conversation, 261 | ChannelData = message.ChannelData, 262 | ChannelId = message.ChannelId, 263 | Timestamp = message.Timestamp, 264 | MembersAdded = new List { message.From, message.Recipient }, 265 | }; 266 | return await this.Post(conversationUpdateMessage, cancellationToken); 267 | } 268 | 269 | /// 270 | /// Handles O365 connector card action queries. 271 | /// 272 | /// Incoming request from Bot Framework. 273 | /// Connector client instance for posting to Bot Framework. 274 | /// Task tracking operation. 275 | 276 | private static async Task HandleO365ConnectorCardActionQuery(Activity activity) 277 | { 278 | var connectorClient = new ConnectorClient(new Uri(activity.ServiceUrl)); 279 | 280 | // Get O365 connector card query data. 281 | O365ConnectorCardActionQuery o365CardQuery = activity.GetO365ConnectorCardActionQueryData(); 282 | 283 | Activity replyActivity = activity.CreateReply(); 284 | replyActivity.TextFormat = "xml"; 285 | 286 | replyActivity.Text = $@" 287 |

Thanks, {activity.From.Name}


288 |

Your input action ID:


289 |
{o365CardQuery.ActionId}

290 |

Your input body:


291 |
{o365CardQuery.Body}
"; 292 | 293 | await connectorClient.Conversations.ReplyToActivityWithRetriesAsync(replyActivity); 294 | return new HttpResponseMessage(HttpStatusCode.OK); 295 | } 296 | 297 | /// 298 | /// Handle the PopUp SignIn requests 299 | /// 300 | /// 301 | /// 302 | private static async Task PopUpSignInHandler(Activity activity) 303 | { 304 | var connectorClient = new ConnectorClient(new Uri(activity.ServiceUrl)); 305 | Activity replyActivity = activity.CreateReply(); 306 | replyActivity.Text = $@"Authentication Successful"; 307 | 308 | await connectorClient.Conversations.ReplyToActivityWithRetriesAsync(replyActivity); 309 | return new HttpResponseMessage(HttpStatusCode.OK); 310 | } 311 | 312 | 313 | } 314 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/RootDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Builder.Scorables; 3 | using Microsoft.Bot.Connector; 4 | using Microsoft.Bot.Connector.Teams.Models; 5 | using Microsoft.Teams.TemplateBotCSharp.Properties; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Threading.Tasks; 9 | 10 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 11 | { 12 | /// 13 | /// This is Root Dialog, its a triggring point for every Child dialog based on the RexEx Match with user input command 14 | /// 15 | 16 | [Serializable] 17 | public class RootDialog : DispatchDialog 18 | { 19 | #region Fetch Roster Api Payload Pattern 20 | 21 | [RegexPattern(DialogMatches.FetchRosterPayloadMatch)] 22 | [ScorableGroup(1)] 23 | public void FetchRosterPayLoadDetails(IDialogContext context, IActivity activity) 24 | { 25 | context.Call(new FetchRosterDialog(), this.EndFetchRosterDialog); 26 | } 27 | 28 | #endregion 29 | 30 | #region Fetch Roster Api Pattern 31 | 32 | [RegexPattern(DialogMatches.FetchRosterApiMatch)] 33 | [ScorableGroup(1)] 34 | public void FetchRoster(IDialogContext context, IActivity activity) 35 | { 36 | context.Call(new ListNamesDialog(), this.EndFetchRosterDialog); 37 | } 38 | 39 | public async Task EndFetchRosterDialog(IDialogContext context, IAwaitable result) 40 | { 41 | await context.PostAsync(Strings.ThanksRosterTitleMsg); 42 | context.Done(null); 43 | } 44 | 45 | #endregion 46 | 47 | #region Play Quiz 48 | 49 | [RegexPattern(DialogMatches.RunQuizQuestionsMatch)] 50 | [ScorableGroup(1)] 51 | public async Task RunQuiz(IDialogContext context, IActivity activity) 52 | { 53 | await this.SendWelcomeMessageQuizAsync(context,activity); 54 | } 55 | private async Task SendWelcomeMessageQuizAsync(IDialogContext context, IActivity activity) 56 | { 57 | await context.PostAsync(Strings.QuizTitleWelcomeMsg); 58 | context.Call(new QuizFullDialog(), this.EndDialog); 59 | } 60 | 61 | #endregion 62 | 63 | #region Prompt Flow Game Dialog Api Pattern 64 | 65 | [RegexPattern(DialogMatches.PromptFlowGameMatch)] 66 | [ScorableGroup(1)] 67 | public void FlowGame(IDialogContext context, IActivity activity) 68 | { 69 | context.Call(new PromptDialogExample(), this.ResumeAfterFlowGame); 70 | } 71 | 72 | public async Task ResumeAfterFlowGame(IDialogContext context, IAwaitable result) 73 | { 74 | if (result == null) 75 | { 76 | throw new InvalidOperationException((nameof(result)) + Strings.NullException); 77 | } 78 | 79 | var resultedValue = await result; 80 | 81 | if(Convert.ToBoolean(resultedValue)) 82 | { 83 | await context.PostAsync(Strings.PlayGameThanksMsg); 84 | } 85 | else 86 | { 87 | await context.PostAsync(Strings.PlayGameFailMsg); 88 | } 89 | 90 | context.Done(null); 91 | } 92 | 93 | #endregion 94 | 95 | #region Dialog Flow 96 | 97 | [RegexPattern(DialogMatches.DialogFlowMatch)] 98 | [ScorableGroup(1)] 99 | public async Task RunDialogFlow(IDialogContext context, IActivity activity) 100 | { 101 | await context.PostAsync(Strings.DialogFlowStep1); 102 | await this.SendStep1MsgAsync(context, activity); 103 | } 104 | 105 | private async Task SendStep1MsgAsync(IDialogContext context, IActivity activity) 106 | { 107 | await context.PostAsync(Strings.DialogFlowStep2); 108 | context.Call(new BeginDialogExampleDialog(), this.ResumeAfterDialogFlow); 109 | } 110 | 111 | public async Task ResumeAfterDialogFlow(IDialogContext context, IAwaitable result) 112 | { 113 | await context.PostAsync(Strings.DialogFlowStep3); 114 | context.Done(null); 115 | } 116 | #endregion 117 | 118 | #region Hello Dialog 119 | 120 | [RegexPattern(DialogMatches.HelloDialogMatch1)] 121 | [RegexPattern(DialogMatches.HelloDialogMatch2)] 122 | [ScorableGroup(1)] 123 | public void RunHelloDialog(IDialogContext context, IActivity activity) 124 | { 125 | context.Call(new HelloDialog(), this.EndDialog); 126 | } 127 | 128 | #endregion 129 | 130 | #region Run at Mention Api Pattern 131 | 132 | [RegexPattern(DialogMatches.AtMentionMatch1)] 133 | [RegexPattern(DialogMatches.AtMentionMatch2)] 134 | [RegexPattern(DialogMatches.AtMentionMatch3)] 135 | [ScorableGroup(1)] 136 | public void AtMentionMatchUser(IDialogContext context, IActivity activity) 137 | { 138 | context.Call(new AtMentionDialog(), this.EndDialog); 139 | } 140 | 141 | #endregion 142 | 143 | #region Multi Dialog1 144 | [RegexPattern(DialogMatches.MultiDialog1Match1)] 145 | [ScorableGroup(1)] 146 | public void MultiDialog(IDialogContext context, IActivity activity) 147 | { 148 | context.Call(new MultiDialog1(), this.EndDialog); 149 | } 150 | 151 | #endregion 152 | 153 | #region Multi Dialog2 154 | [RegexPattern(DialogMatches.MultiDialog2Match)] 155 | [ScorableGroup(1)] 156 | public void MultiDialog2(IDialogContext context, IActivity activity) 157 | { 158 | context.Call(new MultiDialog2(), this.EndDialog); 159 | } 160 | 161 | #endregion 162 | 163 | #region Help Dialog 164 | 165 | [RegexPattern(DialogMatches.Help)] 166 | [ScorableGroup(1)] 167 | public void Help(IDialogContext context, IActivity activity) 168 | { 169 | this.Default(context, activity); 170 | } 171 | 172 | [MethodBind] 173 | [ScorableGroup(2)] 174 | public void Default(IDialogContext context, IActivity activity) 175 | { 176 | context.Call(new HelpDialog(), this.EndDefaultDialog); 177 | } 178 | 179 | public Task EndDefaultDialog(IDialogContext context, IAwaitable result) 180 | { 181 | context.Done(null); 182 | return Task.CompletedTask; 183 | } 184 | 185 | public Task EndDialog(IDialogContext context, IAwaitable result) 186 | { 187 | context.Done(null); 188 | return Task.CompletedTask; 189 | } 190 | 191 | #endregion 192 | 193 | #region Fetch Last Exceuted Dialog 194 | 195 | [RegexPattern(DialogMatches.FecthLastExecutedDialogMatch)] 196 | [ScorableGroup(1)] 197 | public void FetchLastExecutedDialog(IDialogContext context, IActivity activity) 198 | { 199 | context.Call(new GetLastDialogUsedDialog(), this.EndDialog); 200 | } 201 | 202 | #endregion 203 | 204 | #region Send 1:1 Bot Conversation 205 | 206 | [RegexPattern(DialogMatches.Send1to1Conversation)] 207 | [ScorableGroup(1)] 208 | public async Task SendOneToOneConversation(IDialogContext context, IActivity activity) 209 | { 210 | await context.PostAsync(Strings.Send1on1ConfirmMsg); 211 | context.Call(new ProactiveMsgTo1to1Dialog(), this.EndDialog); 212 | } 213 | 214 | #endregion 215 | 216 | #region Set Up Text Message 217 | 218 | [RegexPattern(DialogMatches.SetUpTextMsg)] 219 | [ScorableGroup(1)] 220 | public void SetUpTextMessage(IDialogContext context, IActivity activity) 221 | { 222 | context.Call(new UpdateTextMsgSetupDialog(), this.EndDialog); 223 | } 224 | 225 | #endregion 226 | 227 | #region Update Last Setup Text Message 228 | 229 | [RegexPattern(DialogMatches.UpdateLastSetupTextMsg)] 230 | [ScorableGroup(1)] 231 | public void UpdateLastSetUpTextMessage(IDialogContext context, IActivity activity) 232 | { 233 | context.Call(new UpdateTextMsgDialog(), this.EndDialog); 234 | } 235 | 236 | #endregion 237 | 238 | #region Set Up & Update Card 239 | 240 | [RegexPattern(DialogMatches.SetUpCardMsg)] 241 | [ScorableGroup(1)] 242 | public void SetUpCardMessage(IDialogContext context, IActivity activity) 243 | { 244 | context.Call(new UpdateCardMsgSetupDialog(), this.EndDialog); 245 | } 246 | 247 | [RegexPattern(DialogMatches.UpdateCard)] 248 | [ScorableGroup(1)] 249 | public void UpdateCardMessage(IDialogContext context, IActivity activity) 250 | { 251 | context.Call(new UpdateCardMsgDialog(), this.EndDialog); 252 | } 253 | 254 | #endregion 255 | 256 | #region Load Different Types of Cards 257 | 258 | [RegexPattern(DialogMatches.DisplayCards)] 259 | [ScorableGroup(1)] 260 | public void DisplayCards(IDialogContext context, IActivity activity) 261 | { 262 | context.Call(new DisplayCardsDialog(), this.EndDialog); 263 | } 264 | 265 | [RegexPattern(DialogMatches.StopShowingCards)] 266 | [ScorableGroup(1)] 267 | public async Task LoadNone(IDialogContext context, IActivity activity) 268 | { 269 | await context.PostAsync(Strings.DisplayCardsThanksMsg); 270 | } 271 | 272 | #endregion 273 | 274 | #region MessageBack Dialog 275 | 276 | [RegexPattern(DialogMatches.MessageBack)] 277 | [ScorableGroup(1)] 278 | public void RunMessageBackDialog(IDialogContext context, IActivity activity) 279 | { 280 | context.Call(new MessagebackDialog(), this.EndDialog); 281 | } 282 | 283 | #endregion 284 | 285 | #region LocalTime 286 | 287 | [RegexPattern(DialogMatches.LocalTime)] 288 | [ScorableGroup(1)] 289 | public async Task GetLocalTimeZone(IDialogContext context, IActivity activity) 290 | { 291 | await context.PostAsync(Strings.UTCTimeZonePrompt + activity.Timestamp); 292 | await context.PostAsync(Strings.LocalTimeZonePrompt + activity.LocalTimestamp); 293 | } 294 | 295 | #endregion 296 | 297 | #region Deeplink Dialog 298 | 299 | [RegexPattern(DialogMatches.DeepLinkTabCard)] 300 | [ScorableGroup(1)] 301 | public void DeeplinkDialog(IDialogContext context, IActivity activity) 302 | { 303 | context.Call(new DeepLinkStaticTabDialog(), this.EndDialog); 304 | } 305 | 306 | #endregion 307 | 308 | #region Authentication Dialog 309 | 310 | [RegexPattern(DialogMatches.AuthSample)] 311 | [ScorableGroup(1)] 312 | public async Task AuthSample(IDialogContext context, IActivity activity) 313 | { 314 | var message = CreateAuthSampleMessage(context); 315 | await context.PostAsync(message); 316 | } 317 | 318 | #region Create Auth Message Card 319 | private IMessageActivity CreateAuthSampleMessage(IDialogContext context) 320 | { 321 | var message = context.MakeMessage(); 322 | var attachment = CreateAuthSampleCard(); 323 | message.Attachments.Add(attachment); 324 | return message; 325 | } 326 | 327 | private Attachment CreateAuthSampleCard() 328 | { 329 | return new HeroCard 330 | { 331 | Title = Strings.AuthSampleCardTitle, 332 | Buttons = new List 333 | { 334 | new CardAction(ActionTypes.ImBack, Strings.FBAuthCardCaption, value: Strings.FBAuthCardValue), 335 | new CardAction(ActionTypes.ImBack, Strings.VSTSAuthCardCaption, value: Strings.VSTSAuthCardValue) 336 | } 337 | }.ToAttachment(); 338 | } 339 | #endregion 340 | 341 | #endregion 342 | 343 | #region Facebook Authentication Exmaple Dialog 344 | 345 | [RegexPattern(DialogMatches.Facebooklogin)] 346 | [ScorableGroup(1)] 347 | public void SimpleFacebookAuthLoginDialog(IDialogContext context, IActivity activity) 348 | { 349 | context.Call(new SimpleFacebookAuthDialog(), this.EndDialog); 350 | } 351 | 352 | [RegexPattern(DialogMatches.Facebooklogout)] 353 | [ScorableGroup(1)] 354 | public async Task SimpleFacebookAuthLogoutDialog(IDialogContext context, IActivity activity) 355 | { 356 | context.PrivateConversationData.RemoveValue(SimpleFacebookAuthDialog.AuthTokenKey); 357 | context.PrivateConversationData.RemoveValue("persistedCookie"); 358 | context.UserData.RemoveValue("name"); 359 | await context.PostAsync(Strings.FBSuccessfulLogoutPrompt); 360 | await context.PostAsync(Strings.FBSuccessfulLogoutLoginPrompt); 361 | } 362 | 363 | #endregion 364 | 365 | #region VSTS Authentication Exmaple Dialog 366 | 367 | [RegexPattern(DialogMatches.VSTSlogin)] 368 | [ScorableGroup(1)] 369 | public void VSTSAuthLoginDialog(IDialogContext context, IActivity activity) 370 | { 371 | context.Call(new VSTSAPICallDialog(), this.EndDialog); 372 | } 373 | 374 | [RegexPattern(DialogMatches.VSTSlogout)] 375 | [ScorableGroup(1)] 376 | public async Task VSTSAuthLogoutDialog(IDialogContext context, IActivity activity) 377 | { 378 | context.UserData.RemoveValue(VSTSAPICallDialog.VSTSAuthTokenKey); 379 | context.UserData.RemoveValue("persistedCookieVSTS"); 380 | context.UserData.RemoveValue("name"); 381 | await context.PostAsync(Strings.VSTSSuccessfulLogoutPrompt); 382 | await context.PostAsync(Strings.VSTSSuccessfulLogoutLoginPrompt); 383 | } 384 | 385 | #endregion 386 | 387 | #region VSTS Get Work Item Dialog 388 | 389 | [RegexPattern(DialogMatches.VSTSApi)] 390 | [ScorableGroup(1)] 391 | public void VSTSAuthGetWorkItemDialog(IDialogContext context, IActivity activity) 392 | { 393 | context.Call(new VSTSGetworkItemDialog(), this.EndDialog); 394 | } 395 | 396 | #endregion 397 | 398 | #region Load Hero Card Type 399 | 400 | [RegexPattern(DialogMatches.HeroCard)] 401 | [ScorableGroup(1)] 402 | public void HeroCard(IDialogContext context, IActivity activity) 403 | { 404 | context.Call(new HeroCardDialog(), this.EndDialog); 405 | } 406 | 407 | #endregion 408 | 409 | #region Load Thumbnail Card Type 410 | 411 | [RegexPattern(DialogMatches.ThumbnailCard)] 412 | [ScorableGroup(1)] 413 | public void ThumbnailCard(IDialogContext context, IActivity activity) 414 | { 415 | context.Call(new ThumbnailcardDialog(), this.EndDialog); 416 | } 417 | 418 | #endregion 419 | 420 | #region Load O365Connector Actionable Card Default 421 | [RegexPattern(DialogMatches.O365ConnectorCardActionableCardDefault)] 422 | [ScorableGroup(1)] 423 | public void O365ConnectorCardActionableMessageDefault(IDialogContext context, IActivity activity) 424 | { 425 | context.Call(new O365ConnectorCardActionsDialog(), this.EndDialog); 426 | } 427 | #endregion 428 | 429 | #region Load O365Connector Actionable Card Samples 430 | [RegexPattern(DialogMatches.O365ConnectorCardActionableCards)] 431 | [ScorableGroup(1)] 432 | public void O365ConnectorCardActionableMessage(IDialogContext context, IActivity activity) 433 | { 434 | context.Call(new O365ConnectorCardActionsDialog(), this.EndDialog); 435 | } 436 | #endregion 437 | 438 | #region Load O365Connector Card Default 439 | [RegexPattern(DialogMatches.O365ConnectorCardDefault)] 440 | [ScorableGroup(1)] 441 | public void O365ConnectorCardDefault(IDialogContext context, IActivity activity) 442 | { 443 | context.Call(new O365ConnectorCardDialog(), this.EndDialog); 444 | } 445 | #endregion 446 | 447 | #region Load O365Connector Card Samples 448 | [RegexPattern(DialogMatches.O365ConnectorCards)] 449 | [ScorableGroup(1)] 450 | public void O365ConnectorCard(IDialogContext context, IActivity activity) 451 | { 452 | context.Call(new O365ConnectorCardDialog(), this.EndDialog); 453 | } 454 | #endregion 455 | 456 | #region PopUp SignIn 457 | 458 | [RegexPattern(DialogMatches.PopUpSignIn)] 459 | [ScorableGroup(1)] 460 | public void PopUpSignIn(IDialogContext context, IActivity activity) 461 | { 462 | context.Call(new PopupSigninCardDialog(), this.EndDialog); 463 | } 464 | 465 | #endregion 466 | 467 | #region Team Info 468 | 469 | [RegexPattern(DialogMatches.TeamInfo)] 470 | [ScorableGroup(1)] 471 | public void TeamsInfo(IDialogContext context, IActivity activity) 472 | { 473 | context.Call(new FetchTeamsInfoDialog(), this.EndDialog); 474 | } 475 | 476 | #endregion 477 | 478 | #region Adaptive Card 479 | 480 | [RegexPattern(DialogMatches.AdaptiveCard)] 481 | [ScorableGroup(1)] 482 | public void AdaptiveCard(IDialogContext context, IActivity activity) 483 | { 484 | context.Call(new AdaptiveCardDialog(), this.EndDialog); 485 | } 486 | 487 | #endregion 488 | } 489 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/auth/facebook/FacebookHelpers.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Connector; 2 | using Microsoft.Teams.TemplateBotCSharp.Properties; 3 | using Newtonsoft.Json; 4 | using System; 5 | using System.Configuration; 6 | using System.Net.Http; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Web; 10 | 11 | namespace Microsoft.Teams.TemplateBotCSharp 12 | { 13 | 14 | /// 15 | /// Entity Class to store the Access Token for Auth Flow 16 | /// 17 | public class FacebookAcessToken 18 | { 19 | public FacebookAcessToken() 20 | { 21 | } 22 | 23 | [JsonProperty(PropertyName = "access_token")] 24 | public string AccessToken { get; set; } 25 | 26 | [JsonProperty(PropertyName = "token_type")] 27 | public string TokenType { get; set; } 28 | 29 | [JsonProperty(PropertyName = "expires_in")] 30 | public long ExpiresIn { get; set; } 31 | } 32 | 33 | /// 34 | /// Entity Class to store the User Profile information 35 | /// 36 | public class FacebookProfile 37 | { 38 | public FacebookProfile() 39 | { 40 | } 41 | 42 | [JsonProperty(PropertyName = "id")] 43 | public string Id { get; set; } 44 | [JsonProperty(PropertyName = "name")] 45 | public string Name { get; set; } 46 | 47 | [JsonProperty(PropertyName = "gender")] 48 | public string Gender { get; set; } 49 | 50 | [JsonProperty(PropertyName = "picture")] 51 | public Picture ProfilePicture { get; set; } 52 | 53 | [JsonProperty(PropertyName = "link")] 54 | public string link { get; set; } 55 | 56 | public class Data 57 | { 58 | public bool is_silhouette { get; set; } 59 | public string url { get; set; } 60 | } 61 | 62 | public class Picture 63 | { 64 | public Data data { get; set; } 65 | } 66 | } 67 | 68 | /// 69 | /// Helper class for implementing Facebook API calls. 70 | /// 71 | public static class FacebookHelpers 72 | { 73 | // The Facebook App Id 74 | public static readonly string FacebookAppId = ConfigurationManager.AppSettings["FBAppId"].ToString(); 75 | 76 | // The Facebook App Secret 77 | public static readonly string FacebookAppSecret = ConfigurationManager.AppSettings["FBAppSecret"].ToString(); 78 | 79 | /// 80 | /// Get the Authentication Token from Api code 81 | /// 82 | /// 83 | /// 84 | /// 85 | /// 86 | public async static Task ExchangeCodeForAccessToken(ConversationReference conversationReference, string code, string facebookOauthCallback) 87 | { 88 | var redirectUri = GetOAuthCallBack(conversationReference, facebookOauthCallback); 89 | var uri = GetUri(ConfigurationManager.AppSettings["FBTokenUrl"].ToString(), 90 | Tuple.Create("client_id", FacebookAppId), 91 | Tuple.Create("redirect_uri", redirectUri), 92 | Tuple.Create("client_secret", FacebookAppSecret), 93 | Tuple.Create("code", code) 94 | ); 95 | 96 | return await FacebookRequest(uri); 97 | } 98 | 99 | /// 100 | /// Validate the Access Token 101 | /// 102 | /// 103 | /// 104 | public static async Task ValidateAccessToken(string accessToken) 105 | { 106 | var uri = GetUri(ConfigurationManager.AppSettings["FBDebugUrl"].ToString(), 107 | Tuple.Create("input_token", accessToken), 108 | Tuple.Create("access_token", $"{FacebookAppId}|{FacebookAppSecret}")); 109 | 110 | var res = await FacebookRequest(uri).ConfigureAwait(false); 111 | return (((dynamic)res)?.data)?.is_valid; 112 | } 113 | 114 | /// 115 | /// Get the User Profile information using valid Access Token 116 | /// 117 | /// 118 | /// 119 | public static async Task GetFacebookProfileName(string accessToken) 120 | { 121 | var uri = GetUri(ConfigurationManager.AppSettings["FBProfileUrl"].ToString(), 122 | Tuple.Create("fields", "id,name,gender,picture,link"), 123 | Tuple.Create("access_token", accessToken)); 124 | 125 | var res = await FacebookRequest(uri); 126 | 127 | return res; 128 | } 129 | 130 | /// 131 | /// Get the Profile Picture detail using valid Access Token 132 | /// 133 | /// 134 | /// 135 | public static async Task GetFacebookProfilePicture(string accessToken) 136 | { 137 | var uri = GetUri("https://graph.facebook.com/v2.10/me/picture?width=200&height=200", 138 | Tuple.Create("access_token", accessToken)); 139 | var res = await FacebookRequest(uri); 140 | return res; 141 | } 142 | 143 | /// 144 | /// Create the Auth URL 145 | /// 146 | /// 147 | /// 148 | /// 149 | private static string GetOAuthCallBack(ConversationReference conversationReference, string facebookOauthCallback) 150 | { 151 | var uri = GetUri(facebookOauthCallback, 152 | Tuple.Create("userId", TokenEncoder(conversationReference.User.Id)), 153 | Tuple.Create("botId", TokenEncoder(conversationReference.Bot.Id)), 154 | Tuple.Create("conversationId", TokenEncoder(conversationReference.Conversation.Id)), 155 | Tuple.Create("serviceUrl", TokenEncoder(conversationReference.ServiceUrl)), 156 | Tuple.Create("channelId", conversationReference.ChannelId) 157 | ); 158 | return uri.ToString(); 159 | } 160 | 161 | /// 162 | /// Create Facebook Login URL 163 | /// 164 | /// 165 | /// 166 | /// 167 | public static string GetFacebookLoginURL(ConversationReference conversationReference, string facebookOauthCallback) 168 | { 169 | var redirectUri = GetOAuthCallBack(conversationReference, facebookOauthCallback); 170 | var uri = GetUri(ConfigurationManager.AppSettings["FBAuthUrl"].ToString(), 171 | Tuple.Create("client_id", FacebookAppId), 172 | Tuple.Create("redirect_uri", redirectUri), 173 | Tuple.Create("response_type", "code"), 174 | Tuple.Create("scope", "public_profile,email"), 175 | Tuple.Create("state", Convert.ToString(new Random().Next(9999))) 176 | ); 177 | 178 | return uri.ToString(); 179 | } 180 | 181 | /// 182 | /// Purpose of this request is to process the Api Calls 183 | /// 184 | /// 185 | /// 186 | /// 187 | private static async Task FacebookRequest(Uri uri) 188 | { 189 | string json; 190 | using (HttpClient client = new HttpClient()) 191 | { 192 | json = await client.GetStringAsync(uri).ConfigureAwait(false); 193 | } 194 | 195 | try 196 | { 197 | var result = JsonConvert.DeserializeObject(json); 198 | return result; 199 | } 200 | catch (JsonException ex) 201 | { 202 | throw new ArgumentException(Strings.FBAuthDeserializeError, ex); 203 | } 204 | } 205 | 206 | /// 207 | /// Helper method to create URL 208 | /// 209 | /// 210 | /// 211 | /// 212 | private static Uri GetUri(string endPoint, params Tuple[] queryParams) 213 | { 214 | var queryString = HttpUtility.ParseQueryString(string.Empty); 215 | foreach (var queryparam in queryParams) 216 | { 217 | queryString[queryparam.Item1] = queryparam.Item2; 218 | } 219 | 220 | var builder = new UriBuilder(endPoint); 221 | builder.Query = queryString.ToString(); 222 | return builder.Uri; 223 | } 224 | 225 | // because of a limitation on the characters in Facebook redirect_uri, we don't use the serialization of the cookie. 226 | // http://stackoverflow.com/questions/4386691/facebook-error-error-validating-verification-code 227 | public static string TokenEncoder(string token) 228 | { 229 | return HttpServerUtility.UrlTokenEncode(Encoding.UTF8.GetBytes(token)); 230 | } 231 | 232 | public static string TokenDecoder(string token) 233 | { 234 | return Encoding.UTF8.GetString(HttpServerUtility.UrlTokenDecode(token)); 235 | } 236 | 237 | 238 | } 239 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/auth/facebook/OAuthCallbackController.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using Microsoft.Bot.Builder.ConnectorEx; 3 | using Microsoft.Bot.Builder.Dialogs; 4 | using Microsoft.Bot.Builder.Dialogs.Internals; 5 | using Microsoft.Bot.Connector; 6 | using Microsoft.Teams.TemplateBotCSharp.Properties; 7 | using System; 8 | using System.Configuration; 9 | using System.Net; 10 | using System.Net.Http; 11 | using System.Threading; 12 | using System.Threading.Tasks; 13 | using System.Web.Http; 14 | 15 | namespace Microsoft.Teams.TemplateBotCSharp 16 | { 17 | public class OAuthCallbackController : ApiController 18 | { 19 | /// 20 | /// Facebook OAuth Callback Method 21 | /// 22 | /// The Id for the user that is getting authenticated. 23 | /// Bot Id. 24 | /// The Id of the conversation. 25 | /// The Id of the channel. 26 | /// The Id of the Teams Service url. 27 | /// The Authentication code returned by Facebook. 28 | /// The state returned by Facebook. 29 | /// 30 | [HttpGet] 31 | [Route("api/OAuthCallback")] 32 | public async Task OAuthCallback([FromUri] string userId, [FromUri] string botId, [FromUri] string conversationId, [FromUri] string channelId, [FromUri] string serviceUrl, [FromUri] string code, [FromUri] string state, CancellationToken token) 33 | { 34 | // Get the resumption cookie 35 | var address = new Address 36 | ( 37 | // purposefully using named arguments because these all have the same type 38 | botId: FacebookHelpers.TokenDecoder(botId), 39 | channelId: channelId, 40 | userId: FacebookHelpers.TokenDecoder(userId), 41 | conversationId: FacebookHelpers.TokenDecoder(conversationId), 42 | serviceUrl: FacebookHelpers.TokenDecoder(serviceUrl) 43 | ); 44 | 45 | var conversationReference = address.ToConversationReference(); 46 | 47 | // Exchange the Facebook Auth code with Access token 48 | var accessToken = await FacebookHelpers.ExchangeCodeForAccessToken(conversationReference, code, SimpleFacebookAuthDialog.FacebookOauthCallback.ToString()); 49 | 50 | //Set the User Token, Magic Number and IsValidated Property to User Properties. 51 | conversationReference.User.Properties.Add(ConfigurationManager.AppSettings["FBAccessTokenKey"].ToString(), accessToken.AccessToken); 52 | conversationReference.User.Properties.Add(ConfigurationManager.AppSettings["FBMagicNumberKey"].ToString(), ConfigurationManager.AppSettings["FBMagicNumberValue"].ToString()); 53 | conversationReference.User.Properties.Add(ConfigurationManager.AppSettings["FBIsValidatedKey"].ToString(), false); 54 | 55 | // Create the message that is send to conversation to resume the login flow 56 | var msg = conversationReference.GetPostToBotMessage(); 57 | msg.Text = $"token:{accessToken.AccessToken}"; 58 | 59 | // Resume the conversation to SimpleFacebookAuthDialog 60 | using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, msg)) 61 | { 62 | var dataBag = scope.Resolve(); 63 | await dataBag.LoadAsync(token); 64 | 65 | ConversationReference pending; 66 | var connector = new ConnectorClient(new Uri(conversationReference.ServiceUrl)); 67 | 68 | if (dataBag.PrivateConversationData.TryGetValue("persistedCookie", out pending)) 69 | { 70 | dataBag.PrivateConversationData.SetValue("persistedCookie", conversationReference); 71 | 72 | await dataBag.FlushAsync(token); 73 | 74 | //Send message to Bot 75 | IMessageActivity message = Activity.CreateMessageActivity(); 76 | message.From = conversationReference.User; 77 | message.Recipient = conversationReference.User; 78 | message.Conversation = new ConversationAccount(id: conversationReference.Conversation.Id); 79 | message.Text = Strings.OAuthCallbackUserPrompt; 80 | await connector.Conversations.SendToConversationAsync((Activity)message); 81 | 82 | return Request.CreateResponse(Strings.OAuthCallbackMessage); 83 | } 84 | else 85 | { 86 | // Callback is called with no pending message as a result the login flow cannot be resumed. 87 | return Request.CreateErrorResponse(HttpStatusCode.BadRequest, new InvalidOperationException(Strings.AuthCallbackResumeError)); 88 | } 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/auth/facebook/SimpleFacebookAuthDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.ConnectorEx; 2 | using Microsoft.Bot.Builder.Dialogs; 3 | using Microsoft.Bot.Connector; 4 | using Microsoft.Teams.TemplateBotCSharp.Properties; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Configuration; 8 | using System.Threading.Tasks; 9 | 10 | namespace Microsoft.Teams.TemplateBotCSharp 11 | { 12 | /// 13 | /// This Dialog implements the OAuth login flow for Facebook. 14 | /// You can read more about Facebook's login flow here: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow 15 | /// 16 | [Serializable] 17 | public class SimpleFacebookAuthDialog : IDialog 18 | { 19 | 20 | /// 21 | /// OAuth callback registered for Facebook app. 22 | /// implementats the callback. 23 | /// 24 | /// 25 | /// Make sure to replace this with the appropriate website url registered for your Facebook app. 26 | /// 27 | public static readonly Uri FacebookOauthCallback = new Uri(ConfigurationManager.AppSettings["FBCallbackUrl"].ToString()); 28 | 29 | /// 30 | /// The key that is used to keep the AccessToken in 31 | /// 32 | public static readonly string AuthTokenKey = ConfigurationManager.AppSettings["FBAuthToken"].ToString(); 33 | 34 | public async Task StartAsync(IDialogContext context) 35 | { 36 | //Set the Last Dialog in Conversation Data 37 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogFacebookDialog); 38 | 39 | await LogIn(context); 40 | } 41 | 42 | public async virtual Task MessageReceivedAsync(IDialogContext context, IAwaitable argument) 43 | { 44 | var msg = await (argument); 45 | ConversationReference conversationReference; 46 | FacebookAcessToken facebookToken = new FacebookAcessToken(); 47 | string magicNumber = string.Empty; 48 | string token = string.Empty; 49 | 50 | if (context.PrivateConversationData.TryGetValue("persistedCookie", out conversationReference)) 51 | { 52 | magicNumber = conversationReference.User.Properties[ConfigurationManager.AppSettings["FBMagicNumberKey"].ToString()].ToString(); 53 | 54 | if (string.Equals(msg.Text, magicNumber)) 55 | { 56 | conversationReference.User.Properties[ConfigurationManager.AppSettings["FBIsValidatedKey"].ToString()] = true; 57 | context.PrivateConversationData.SetValue("persistedCookie", conversationReference); 58 | 59 | token = conversationReference.User.Properties[ConfigurationManager.AppSettings["FBAccessTokenKey"].ToString()].ToString(); 60 | 61 | var valid = await FacebookHelpers.ValidateAccessToken(token); 62 | 63 | if(valid) 64 | { 65 | FacebookProfile profile = await FacebookHelpers.GetFacebookProfileName(token); 66 | var message = CreateFBMessage(context, profile); 67 | 68 | await context.PostAsync(message); 69 | await context.PostAsync(Strings.FBLginSuccessPromptLogoutInfo); 70 | 71 | context.PrivateConversationData.SetValue(AuthTokenKey, token); 72 | context.Done(token); 73 | } 74 | } 75 | else 76 | { 77 | //When entered number is not valid 78 | await context.PostAsync(Strings.AuthMagicNumberNotMacthed); 79 | await LogIn(context); 80 | } 81 | } 82 | else 83 | { 84 | await LogIn(context); 85 | } 86 | } 87 | 88 | #region Create FB Profile Message Card 89 | private Attachment CreateFBProfileCard(FacebookProfile profile) 90 | { 91 | return new ThumbnailCard 92 | { 93 | Title = Strings.FBLoginSuccessPrompt + " " + profile.Name + "(" + profile.Gender + ")", 94 | Images = new List { new CardImage(profile.ProfilePicture.data.url) }, 95 | Buttons = new List 96 | { 97 | new CardAction(ActionTypes.OpenUrl, Strings.FBCardButtonCaption, value: profile.link) 98 | } 99 | }.ToAttachment(); 100 | } 101 | 102 | private IMessageActivity CreateFBMessage(IDialogContext context, FacebookProfile profile) 103 | { 104 | var message = context.MakeMessage(); 105 | var attachment = CreateFBProfileCard(profile); 106 | message.Attachments.Add(attachment); 107 | return message; 108 | } 109 | #endregion 110 | 111 | /// 112 | /// Login the user. 113 | /// 114 | /// The Dialog context. 115 | /// A task that represents the login action. 116 | private async Task LogIn(IDialogContext context) 117 | { 118 | string token; 119 | if (!context.PrivateConversationData.TryGetValue(AuthTokenKey, out token)) 120 | { 121 | var conversationReference = context.Activity.ToConversationReference(); 122 | 123 | context.PrivateConversationData.SetValue("persistedCookie", conversationReference); 124 | 125 | // sending the sigin card with Facebook login url 126 | var reply = context.MakeMessage(); 127 | var fbLoginUrl = FacebookHelpers.GetFacebookLoginURL(conversationReference, FacebookOauthCallback.ToString()); 128 | reply.Text = Strings.FBLoginTitle; 129 | 130 | //Login Card 131 | var loginCard = new HeroCard 132 | { 133 | Title = Strings.FBLoginCardPrompt, 134 | Buttons = new List { new CardAction(ActionTypes.OpenUrl, Strings.FBLoginCardButtonCaption, value: fbLoginUrl) } 135 | }; 136 | 137 | reply.Attachments.Add(loginCard.ToAttachment()); 138 | 139 | await context.PostAsync(reply); 140 | context.Wait(MessageReceivedAsync); 141 | } 142 | else 143 | { 144 | await context.PostAsync(Strings.FBLoginSessionExistsPrompt); 145 | await context.PostAsync(Strings.FBLogoutPrompt); 146 | context.Done(token); 147 | } 148 | } 149 | } 150 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/auth/vsts/OAuthCallbackVSTSController.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using Microsoft.Bot.Builder.ConnectorEx; 3 | using Microsoft.Bot.Builder.Dialogs; 4 | using Microsoft.Bot.Builder.Dialogs.Internals; 5 | using Microsoft.Bot.Connector; 6 | using Microsoft.Teams.TemplateBotCSharp.Properties; 7 | using Newtonsoft.Json; 8 | using System; 9 | using System.Configuration; 10 | using System.Net; 11 | using System.Net.Http; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | using System.Web.Http; 15 | 16 | namespace Microsoft.Teams.TemplateBotCSharp 17 | { 18 | public class OAuthCallbackVSTSController : ApiController 19 | { 20 | /// 21 | /// OAuth call back that is called by VSTS. Read https://www.visualstudio.com/en-us/docs/integrate/extensions/overview for more details. 22 | /// 23 | /// The Authentication code returned by VSTS. 24 | /// The state returned by VSTS. 25 | /// 26 | [HttpGet] 27 | [Route("api/OAuthCallbackVSTS")] 28 | public async Task OAuthCallbackVSTS(string code, string state, CancellationToken token) 29 | { 30 | ConversationReference conversationReferenceJSON = JsonConvert.DeserializeObject(state); 31 | 32 | //// Get the resumption cookie 33 | var address = new Address 34 | ( 35 | botId: conversationReferenceJSON.Bot.Id, 36 | channelId: conversationReferenceJSON.ChannelId, 37 | userId: conversationReferenceJSON.User.Id, 38 | conversationId: conversationReferenceJSON.Conversation.Id, 39 | serviceUrl: conversationReferenceJSON.ServiceUrl 40 | ); 41 | 42 | var conversationReference = address.ToConversationReference(); 43 | 44 | string workItemId = string.Empty; 45 | 46 | if (conversationReferenceJSON.User.Properties["workItemId"] != null) 47 | { 48 | workItemId = conversationReferenceJSON.User.Properties["workItemId"].ToString(); 49 | conversationReference.User.Properties.Add("workItemId", workItemId); 50 | } 51 | 52 | VSTSAcessToken accessToken = new VSTSAcessToken(); 53 | String error = null; 54 | 55 | if (!String.IsNullOrEmpty(code)) 56 | { 57 | error = VSTSHelpers.PerformTokenRequest(VSTSHelpers.GenerateRequestPostData(code), true, out accessToken); 58 | 59 | //Set the User Token, Magic Number and IsValidated Property to User Properties. 60 | if (String.IsNullOrEmpty(error)) 61 | { 62 | conversationReference.User.Properties.Add("AccessToken", accessToken.accessToken); 63 | conversationReference.User.Properties.Add("RefreshToken", accessToken.refreshToken); 64 | conversationReference.User.Properties.Add("MagicNumber", ConfigurationManager.AppSettings["VSTSMagicNumber"].ToString()); 65 | conversationReference.User.Properties.Add("IsValidated", false); 66 | conversationReference.User.Properties.Add("UserName", conversationReferenceJSON.User.Name); 67 | } 68 | else 69 | { 70 | return null; 71 | } 72 | } 73 | else 74 | { 75 | return Request.CreateErrorResponse(HttpStatusCode.BadRequest, new InvalidOperationException(Strings.VSTSCallbackAuthError)); 76 | } 77 | 78 | // Create the message that is send to conversation to resume the login flow 79 | var msg = conversationReference.GetPostToBotMessage(); 80 | msg.Text = $"token:{accessToken.accessToken}"; 81 | 82 | // Resume the conversation to SimpleFacebookAuthDialog 83 | using (var scope1 = DialogModule.BeginLifetimeScope(Conversation.Container, msg)) 84 | { 85 | var dataBag = scope1.Resolve(); 86 | await dataBag.LoadAsync(token); 87 | 88 | ConversationReference pending; 89 | var connector = new ConnectorClient(new Uri(conversationReference.ServiceUrl)); 90 | 91 | if (dataBag.UserData.TryGetValue("persistedCookieVSTS", out pending)) 92 | { 93 | dataBag.UserData.SetValue("persistedCookieVSTS", conversationReference); 94 | 95 | await dataBag.FlushAsync(token); 96 | 97 | //Send message to Bot 98 | IMessageActivity message = Activity.CreateMessageActivity(); 99 | message.From = conversationReference.User; 100 | message.Recipient = conversationReference.User; 101 | message.Conversation = new ConversationAccount(id: conversationReference.Conversation.Id); 102 | message.Text = Strings.OAuthCallbackUserPrompt; 103 | await connector.Conversations.SendToConversationAsync((Activity)message); 104 | 105 | return Request.CreateResponse(Strings.OAuthCallbackMessage); 106 | } 107 | else 108 | { 109 | // Callback is called with no pending message as a result the login flow cannot be resumed. 110 | return Request.CreateErrorResponse(HttpStatusCode.BadRequest, new InvalidOperationException(Strings.AuthCallbackResumeError)); 111 | } 112 | } 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/auth/vsts/VSTSAPICallDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.ConnectorEx; 2 | using Microsoft.Bot.Builder.Dialogs; 3 | using Microsoft.Bot.Connector; 4 | using Microsoft.Teams.TemplateBotCSharp.Properties; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Configuration; 8 | using System.Threading.Tasks; 9 | 10 | namespace Microsoft.Teams.TemplateBotCSharp 11 | { 12 | /// 13 | /// This Dialog implements the OAuth login flow for VSTS. 14 | /// You can read more about VSTS login flow here: https://www.visualstudio.com/en-us/docs/integrate/extensions/overview 15 | /// 16 | [Serializable] 17 | public class VSTSAPICallDialog : IDialog 18 | { 19 | 20 | /// 21 | /// OAuth callback registered for Facebook app. 22 | /// implementats the callback. 23 | /// 24 | /// 25 | /// Make sure to replace this with the appropriate website url registered for your VSTS app. 26 | /// 27 | public static readonly Uri VSTSOauthCallback = new Uri(ConfigurationManager.AppSettings["CallbackUrl"].ToString()); 28 | 29 | /// 30 | /// The key that is used to keep the AccessToken in 31 | /// 32 | public static readonly string VSTSAuthTokenKey = ConfigurationManager.AppSettings["VSTSAuthToken"].ToString(); 33 | 34 | public async Task StartAsync(IDialogContext context) 35 | { 36 | //Set the Last Dialog in Conversation Data 37 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogVSTSDialog); 38 | 39 | await LogIn(context); 40 | } 41 | 42 | public async virtual Task MessageReceivedAsync(IDialogContext context, IAwaitable argument) 43 | { 44 | var msg = await (argument); 45 | ConversationReference conversationReference; 46 | VSTSAcessToken facebookToken = new VSTSAcessToken(); 47 | string magicNumber = string.Empty; 48 | string token = string.Empty; 49 | 50 | if (context.UserData.TryGetValue("persistedCookieVSTS", out conversationReference)) 51 | { 52 | magicNumber = conversationReference.User.Properties["MagicNumber"].ToString(); 53 | 54 | if (string.Equals(msg.Text, magicNumber)) 55 | { 56 | conversationReference.User.Properties["IsValidated"] = true; 57 | 58 | context.UserData.SetValue("persistedCookieVSTS", conversationReference); 59 | 60 | token = conversationReference.User.Properties["AccessToken"].ToString(); 61 | 62 | // Dialog is resumed by the OAuth callback and access token 63 | // is encoded in the message.Text 64 | var valid = Convert.ToBoolean(conversationReference.User.Properties["IsValidated"]); 65 | 66 | if (valid) 67 | { 68 | var name = conversationReference.User.Properties["UserName"].ToString(); 69 | context.UserData.SetValue("name", name); 70 | await context.PostAsync(Strings.VSTSLoginSuccessPrompt); 71 | await context.PostAsync(Strings.VSTSlogoutPrompt); 72 | context.UserData.SetValue(VSTSAuthTokenKey, token); 73 | context.Done(token); 74 | } 75 | 76 | } 77 | else 78 | { 79 | //When entered number is not valid 80 | await context.PostAsync(Strings.AuthMagicNumberNotMacthed); 81 | await LogIn(context); 82 | } 83 | } 84 | else 85 | { 86 | await LogIn(context); 87 | } 88 | } 89 | 90 | /// 91 | /// Login the user. 92 | /// 93 | /// The Dialog context. 94 | /// A task that represents the login action. 95 | private async Task LogIn(IDialogContext context) 96 | { 97 | string token; 98 | if (!context.UserData.TryGetValue(VSTSAuthTokenKey, out token)) 99 | { 100 | var conversationReference = context.Activity.ToConversationReference(); 101 | 102 | context.UserData.SetValue("persistedCookieVSTS", conversationReference); 103 | 104 | // sending the sigin card with Facebook login url 105 | var reply = context.MakeMessage(); 106 | var vstsLoginUrl = VSTSHelpers.GenerateAuthorizeUrl(conversationReference); 107 | 108 | reply.Text = Strings.VSTSLoginTitle; 109 | 110 | //Login Card 111 | var loginCard = new HeroCard 112 | { 113 | Title = Strings.VSTSLoginCardTitle, 114 | Buttons = new List { new CardAction(ActionTypes.OpenUrl, Strings.VSTSLoginCardButtonCaption, value: vstsLoginUrl) } 115 | }; 116 | 117 | reply.Attachments.Add(loginCard.ToAttachment()); 118 | 119 | await context.PostAsync(reply); 120 | context.Wait(MessageReceivedAsync); 121 | } 122 | else 123 | { 124 | await context.PostAsync(Strings.VSTSLoginSessionExistsPrompt); 125 | await context.PostAsync(Strings.VSTSlogoutPrompt); 126 | context.Done(token); 127 | } 128 | } 129 | } 130 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/auth/vsts/VSTSGetworkItemDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.ConnectorEx; 2 | using Microsoft.Bot.Builder.Dialogs; 3 | using Microsoft.Bot.Connector; 4 | using Microsoft.Teams.TemplateBotCSharp.Properties; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Configuration; 8 | using System.Threading.Tasks; 9 | 10 | namespace Microsoft.Teams.TemplateBotCSharp 11 | { 12 | /// 13 | /// This Dialog implements the OAuth login flow for Facebook. 14 | /// You can read more about Facebook's login flow here: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow 15 | /// 16 | [Serializable] 17 | public class VSTSGetworkItemDialog : IDialog 18 | { 19 | 20 | /// 21 | /// The key that is used to keep the AccessToken in 22 | /// 23 | public static readonly string VSTSAuthTokenKey = ConfigurationManager.AppSettings["VSTSAuthToken"].ToString(); 24 | 25 | public async Task StartAsync(IDialogContext context) 26 | { 27 | await context.PostAsync(Strings.VSTSGetWorkItemPrompt); 28 | context.Wait(this.MessageReceivedAsync); 29 | } 30 | 31 | public async virtual Task MessageReceivedAsync(IDialogContext context, IAwaitable argument) 32 | { 33 | var msg = await (argument); 34 | 35 | ConversationReference conversationReference; 36 | VSTSAcessToken vstsToken = new VSTSAcessToken(); 37 | string magicNumber = string.Empty; 38 | string token = string.Empty; 39 | string refreshToken = string.Empty; 40 | String error = null; 41 | string requestedWorkItemId = string.Empty; 42 | bool IsAuthenticated = false; 43 | 44 | if (context.UserData.TryGetValue("persistedCookieVSTS", out conversationReference)) 45 | { 46 | requestedWorkItemId = conversationReference.User.Properties["workItemId"].ToString(); 47 | 48 | if(string.IsNullOrEmpty(requestedWorkItemId)) 49 | { 50 | requestedWorkItemId = msg.Text; 51 | IsAuthenticated = true; 52 | } 53 | else 54 | { 55 | magicNumber = conversationReference.User.Properties["MagicNumber"].ToString(); 56 | 57 | if (string.Equals(msg.Text, magicNumber)) 58 | { 59 | IsAuthenticated = true; 60 | } 61 | else 62 | { 63 | //When entered number is not valid 64 | await context.PostAsync(Strings.AuthMagicNumberNotMacthed); 65 | await LogIn(context, msg.Text); 66 | } 67 | } 68 | 69 | if (IsAuthenticated) 70 | { 71 | refreshToken = conversationReference.User.Properties["RefreshToken"].ToString(); 72 | 73 | //Get the refreshed token 74 | error = VSTSHelpers.PerformTokenRequest(VSTSHelpers.GenerateRefreshPostData(refreshToken), true, out vstsToken); 75 | 76 | if (String.IsNullOrEmpty(error)) 77 | { 78 | conversationReference.User.Properties["AccessToken"] = vstsToken.accessToken; 79 | conversationReference.User.Properties["RefreshToken"] = vstsToken.refreshToken; 80 | } 81 | 82 | WorkItem workItem = await VSTSHelpers.GetWorkItem(vstsToken.accessToken, requestedWorkItemId); 83 | 84 | if (workItem != null) 85 | { 86 | var workItemCardMessage = CreateWorkItemCard(context, workItem, requestedWorkItemId); 87 | await context.PostAsync(workItemCardMessage); 88 | } 89 | } 90 | } 91 | else 92 | { 93 | await LogIn(context, msg.Text); 94 | } 95 | } 96 | 97 | /// 98 | /// Login the user. 99 | /// 100 | /// The Dialog context. 101 | /// A task that represents the login action. 102 | private async Task LogIn(IDialogContext context, string workItemId) 103 | { 104 | string token; 105 | if (!context.UserData.TryGetValue(VSTSAuthTokenKey, out token)) 106 | { 107 | var conversationReference = context.Activity.ToConversationReference(); 108 | 109 | context.UserData.SetValue("persistedCookieVSTS", conversationReference); 110 | conversationReference.User.Properties.Add("workItemId", workItemId); 111 | 112 | // sending the sigin card with Facebook login url 113 | var reply = context.MakeMessage(); 114 | var vstsLoginUrl = VSTSHelpers.GenerateAuthorizeUrl(conversationReference); 115 | 116 | reply.Text = Strings.VSTSGetWorkItemLoginPrompt; 117 | 118 | //Login Card 119 | 120 | var loginCard = new HeroCard 121 | { 122 | Title = Strings.VSTSLoginCardTitle, 123 | Buttons = new List { new CardAction(ActionTypes.OpenUrl, Strings.VSTSLoginCardButtonCaption, value: vstsLoginUrl) } 124 | }; 125 | 126 | reply.Attachments.Add(loginCard.ToAttachment()); 127 | 128 | await context.PostAsync(reply); 129 | context.Wait(MessageReceivedAsync); 130 | } 131 | else 132 | { 133 | await context.PostAsync(Strings.VSTSLoginSessionExistsPrompt); 134 | await context.PostAsync(Strings.VSTSlogoutPrompt); 135 | context.Done(token); 136 | } 137 | } 138 | 139 | #region Create Work Item Message Card 140 | private Attachment CreateWorkItemAttachment(WorkItem workItem, string workItemId) 141 | { 142 | var encodedUrl = workItem.TeamProject; 143 | string goToWorkItemUrl = "https://teamsbot.visualstudio.com/" + encodedUrl + "/_workitems?id=" + workItemId + "&_a=edit"; 144 | return new HeroCard 145 | { 146 | Title = workItem.Title, 147 | Subtitle = workItem.Url, 148 | Buttons = new List 149 | { 150 | new CardAction(ActionTypes.OpenUrl, Strings.VSTSGetWorkItemCardButtonCaption, value: goToWorkItemUrl), 151 | } 152 | }.ToAttachment(); 153 | } 154 | 155 | private IMessageActivity CreateWorkItemCard(IDialogContext context, WorkItem workItem, string workItemId) 156 | { 157 | var message = context.MakeMessage(); 158 | var attachment = CreateWorkItemAttachment(workItem, workItemId); 159 | message.Attachments.Add(attachment); 160 | return message; 161 | } 162 | #endregion 163 | 164 | } 165 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/auth/vsts/VSTSHelpers.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Connector; 2 | using Microsoft.Teams.TemplateBotCSharp.Properties; 3 | using Newtonsoft.Json; 4 | using Newtonsoft.Json.Linq; 5 | using System; 6 | using System.Configuration; 7 | using System.IO; 8 | using System.Net; 9 | using System.Net.Http; 10 | using System.Threading.Tasks; 11 | using System.Web; 12 | 13 | namespace Microsoft.Teams.TemplateBotCSharp 14 | { 15 | /// 16 | /// This is Entity Class for VSTS Profile Information 17 | /// 18 | class VSTSProfile 19 | { 20 | public VSTSProfile() 21 | { 22 | } 23 | 24 | [JsonProperty(PropertyName = "id")] 25 | public string Id { get; set; } 26 | [JsonProperty(PropertyName = "name")] 27 | public string Name { get; set; } 28 | } 29 | 30 | /// 31 | /// Entity Class to store the Access Token for Auth Flow 32 | /// 33 | public class VSTSAcessToken 34 | { 35 | public VSTSAcessToken() 36 | { 37 | 38 | } 39 | 40 | [JsonProperty(PropertyName = "access_token")] 41 | public String accessToken { get; set; } 42 | 43 | [JsonProperty(PropertyName = "token_type")] 44 | public String tokenType { get; set; } 45 | 46 | [JsonProperty(PropertyName = "expires_in")] 47 | public String expiresIn { get; set; } 48 | 49 | [JsonProperty(PropertyName = "refresh_token")] 50 | public String refreshToken { get; set; } 51 | 52 | } 53 | /// 54 | /// Entity Class to store the Work Item details 55 | /// 56 | public class WorkItem 57 | { 58 | public int Id { get; set; } 59 | public int Rev { get; set; } 60 | public string Title { get; set; } 61 | public string TeamProject { get; set; } 62 | public string Url { get; set; } 63 | } 64 | 65 | public static class VSTSHelpers 66 | { 67 | /// 68 | /// Execute the VSTS Api call 69 | /// 70 | /// 71 | /// 72 | /// 73 | /// 74 | public static String PerformTokenRequest(String postData, bool IsCallback, out VSTSAcessToken token) 75 | { 76 | var error = String.Empty; 77 | var strResponseData = String.Empty; 78 | 79 | HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create( 80 | ConfigurationManager.AppSettings["TokenUrl"] 81 | ); 82 | 83 | webRequest.Method = "POST"; 84 | webRequest.ContentLength = postData.Length; 85 | webRequest.ContentType = "application/x-www-form-urlencoded"; 86 | 87 | using (StreamWriter swRequestWriter = new StreamWriter(webRequest.GetRequestStream())) 88 | { 89 | swRequestWriter.Write(postData); 90 | } 91 | 92 | try 93 | { 94 | HttpWebResponse hwrWebResponse = (HttpWebResponse)webRequest.GetResponse(); 95 | 96 | if (hwrWebResponse.StatusCode == HttpStatusCode.OK) 97 | { 98 | using (StreamReader srResponseReader = new StreamReader(hwrWebResponse.GetResponseStream())) 99 | { 100 | strResponseData = srResponseReader.ReadToEnd(); 101 | } 102 | 103 | token = JsonConvert.DeserializeObject(strResponseData); 104 | return null; 105 | } 106 | } 107 | catch (WebException wex) 108 | { 109 | error = Strings.VSTSApiRequestError + wex.Message; 110 | } 111 | catch (Exception ex) 112 | { 113 | error = Strings.VSTSApiIssue + ex.Message; 114 | } 115 | 116 | token = new VSTSAcessToken(); 117 | return error; 118 | } 119 | 120 | /// 121 | /// Create authorization the url 122 | /// 123 | /// 124 | /// 125 | public static String GenerateAuthorizeUrl(ConversationReference conversationReference) 126 | { 127 | UriBuilder uriBuilder = new UriBuilder(ConfigurationManager.AppSettings["AuthUrl"]); 128 | var queryParams = HttpUtility.ParseQueryString(uriBuilder.Query ?? String.Empty); 129 | 130 | string stateData = JsonConvert.SerializeObject(conversationReference); 131 | 132 | queryParams["client_id"] = ConfigurationManager.AppSettings["AppId"]; 133 | queryParams["response_type"] = "Assertion"; 134 | queryParams["state"] = stateData; 135 | queryParams["scope"] = ConfigurationManager.AppSettings["Scope"]; 136 | queryParams["redirect_uri"] = ConfigurationManager.AppSettings["CallbackUrl"]; 137 | 138 | uriBuilder.Query = queryParams.ToString(); 139 | 140 | return uriBuilder.ToString(); 141 | } 142 | 143 | /// 144 | /// Get the VSTS Profile 145 | /// 146 | /// 147 | /// 148 | public static async Task GetVSTSProfile(string accessToken) 149 | { 150 | var uri = GetUri("https://teamsbot.visualstudio.com/DefaultCollection/_apis/profile/profiles/me"); 151 | var res = await VSTSRequestAPI(uri, accessToken); 152 | return res; 153 | } 154 | 155 | /// 156 | /// Get VSTS Work Item 157 | /// 158 | /// 159 | /// 160 | /// 161 | public static async Task GetWorkItem(string accessToken, string workItemId) 162 | { 163 | WorkItem item = new WorkItem(); 164 | var uri = GetUri("https://teamsbot.visualstudio.com/DefaultCollection/_apis/wit/workitems?", 165 | //Tuple.Create("access_token", accessToken)); 166 | Tuple.Create("id", workItemId), 167 | Tuple.Create("api-version", "1.0")); 168 | 169 | var res = await VSTSRequestAPI(uri, accessToken); 170 | 171 | var jsonObject= JObject.Parse(res); // parse as array 172 | 173 | if (jsonObject != null) 174 | { 175 | item.Title = jsonObject["fields"]["System.Title"].ToString(); 176 | item.TeamProject = jsonObject["fields"]["System.TeamProject"].ToString(); 177 | item.Url = jsonObject["url"].ToString(); 178 | } 179 | 180 | return item; 181 | } 182 | 183 | /// 184 | /// Get Create Api Post Data 185 | /// 186 | /// 187 | /// 188 | public static string GenerateRequestPostData(string code) 189 | { 190 | return string.Format("client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion={0}&grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion={1}&redirect_uri={2}", 191 | HttpUtility.UrlEncode(ConfigurationManager.AppSettings["AppSecret"]), 192 | HttpUtility.UrlEncode(code), 193 | ConfigurationManager.AppSettings["CallbackUrl"] 194 | ); 195 | } 196 | 197 | /// 198 | /// Get the Refresh Token Api Post Data 199 | /// 200 | /// 201 | /// 202 | public static string GenerateRefreshPostData(string refreshToken) 203 | { 204 | return string.Format("client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion={0}&grant_type=refresh_token&assertion={1}&redirect_uri={2}", 205 | HttpUtility.UrlEncode(ConfigurationManager.AppSettings["AppSecret"]), 206 | HttpUtility.UrlEncode(refreshToken), 207 | ConfigurationManager.AppSettings["CallbackUrl"] 208 | ); 209 | 210 | } 211 | 212 | /// 213 | /// Process the Api call 214 | /// 215 | /// 216 | /// 217 | /// 218 | private static async Task VSTSRequest(Uri uri) 219 | { 220 | string json; 221 | using (HttpClient client = new HttpClient()) 222 | { 223 | json = await client.GetStringAsync(uri).ConfigureAwait(false); 224 | } 225 | 226 | try 227 | { 228 | var result = JsonConvert.DeserializeObject(json); 229 | return result; 230 | } 231 | catch (JsonException ex) 232 | { 233 | throw new ArgumentException("Unable to deserialize the VSTS response.", ex); 234 | } 235 | } 236 | 237 | /// 238 | /// Process the Api call 239 | /// 240 | /// 241 | /// 242 | /// 243 | private static async Task VSTSRequestAPI(Uri uri, string accessToken) 244 | { 245 | string json = string.Empty; 246 | using (HttpClient client = new HttpClient()) 247 | { 248 | client.DefaultRequestHeaders.Add("authorization", "bearer " + accessToken); 249 | json = await client.GetStringAsync(uri).ConfigureAwait(false); 250 | } 251 | 252 | return json; 253 | } 254 | 255 | /// 256 | /// Create Uri 257 | /// 258 | /// 259 | /// 260 | /// 261 | private static Uri GetUri(string endPoint, params Tuple[] queryParams) 262 | { 263 | var queryString = HttpUtility.ParseQueryString(string.Empty); 264 | foreach (var queryparam in queryParams) 265 | { 266 | queryString[queryparam.Item1] = queryparam.Item2; 267 | } 268 | 269 | var builder = new UriBuilder(endPoint); 270 | builder.Query = queryString.ToString(); 271 | return builder.Uri; 272 | } 273 | } 274 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/basic/GetLastDialogUsedDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Teams.TemplateBotCSharp.Properties; 3 | using System; 4 | using System.Threading.Tasks; 5 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 6 | { 7 | /// 8 | /// This is get Last Dialog Class. Main purpose of this class is to set the Last Active dialog information 9 | /// 10 | 11 | [Serializable] 12 | public class GetLastDialogUsedDialog : IDialog 13 | { 14 | public async Task StartAsync(IDialogContext context) 15 | { 16 | if (context == null) 17 | { 18 | throw new ArgumentNullException(nameof(context)); 19 | } 20 | 21 | string dialogName = string.Empty; 22 | 23 | if (context.UserData.TryGetValue(Strings.LastDialogKey, out dialogName)) 24 | { 25 | await context.PostAsync(Strings.LastDialogPromptMsg + dialogName); 26 | } 27 | else 28 | { 29 | //Set the Last Dialog in Conversation Data 30 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogFetchDiaog); 31 | 32 | await context.PostAsync(Strings.LastDialogErrorMsg); 33 | } 34 | 35 | //Set the Last Dialog in Conversation Data 36 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogFetchDiaog); 37 | 38 | context.Done(null); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/basic/HelloDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Teams.TemplateBotCSharp.Properties; 3 | using System; 4 | using System.Threading.Tasks; 5 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 6 | { 7 | /// 8 | /// This is Begin Dialog Class. Main purpose of this class is to notify users that Child dialog has been called 9 | /// and its a Basic example to call Child dialog from Root Dialog. 10 | /// 11 | 12 | [Serializable] 13 | public class HelloDialog : IDialog 14 | { 15 | public async Task StartAsync(IDialogContext context) 16 | { 17 | if (context == null) 18 | { 19 | throw new ArgumentNullException(nameof(context)); 20 | } 21 | 22 | //Set the Last Dialog in Conversation Data 23 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogHelloDialog); 24 | 25 | await context.PostAsync(Strings.HelloDialogMsg); 26 | 27 | context.Done(null); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/basic/HeroCardDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using Microsoft.Teams.TemplateBotCSharp.Properties; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Threading.Tasks; 7 | 8 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 9 | { 10 | /// 11 | /// This is Hero Card Dialog Class. Main purpose of this class is to display the Hero Card example 12 | /// 13 | 14 | [Serializable] 15 | public class HeroCardDialog : IDialog 16 | { 17 | public async Task StartAsync(IDialogContext context) 18 | { 19 | if (context == null) 20 | { 21 | throw new ArgumentNullException(nameof(context)); 22 | } 23 | 24 | //Set the Last Dialog in Conversation Data 25 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogHeroCard); 26 | 27 | var message = context.MakeMessage(); 28 | var attachment = GetHeroCard(); 29 | 30 | message.Attachments.Add(attachment); 31 | 32 | await context.PostAsync(message); 33 | 34 | context.Done(null); 35 | } 36 | 37 | private static Attachment GetHeroCard() 38 | { 39 | var heroCard = new HeroCard 40 | { 41 | Title = Strings.HeroCardTitle, 42 | Subtitle = Strings.HeroCardSubTitle, 43 | Text = Strings.HeroCardTextMsg, 44 | Images = new List { new CardImage("https://sec.ch9.ms/ch9/7ff5/e07cfef0-aa3b-40bb-9baa-7c9ef8ff7ff5/buildreactionbotframework_960.jpg") }, 45 | Buttons = new List 46 | { 47 | new CardAction(ActionTypes.OpenUrl, Strings.HeroCardButtonCaption, value: "https://docs.microsoft.com/en-us/bot-framework/dotnet/bot-builder-dotnet-add-rich-card-attachments"), 48 | new CardAction(ActionTypes.MessageBack, Strings.MessageBackCardButtonCaption, value: "{\"" + Strings.cmdValueMessageBack + "\": \"" + Strings.cmdValueMessageBack+ "\"}", text:Strings.cmdValueMessageBack, displayText:Strings.MessageBackDisplayedText) 49 | } 50 | }; 51 | 52 | return heroCard.ToAttachment(); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/basic/MessagebackDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Teams.TemplateBotCSharp.Properties; 3 | using System; 4 | using System.Threading.Tasks; 5 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 6 | { 7 | /// 8 | /// This is Message Back Dialog Class. Main purpose of this class is to show example of Message Back event 9 | /// 10 | 11 | [Serializable] 12 | public class MessagebackDialog : IDialog 13 | { 14 | public async Task StartAsync(IDialogContext context) 15 | { 16 | if (context == null) 17 | { 18 | throw new ArgumentNullException(nameof(context)); 19 | } 20 | 21 | //Set the Last Dialog in Conversation Data 22 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogMessageBackDialog); 23 | 24 | await context.PostAsync(Strings.MessageBackTitleMsg); 25 | 26 | context.Done(null); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/basic/MultiDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using Microsoft.Teams.TemplateBotCSharp.Properties; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Configuration; 7 | using System.Threading.Tasks; 8 | 9 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 10 | { 11 | [Serializable] 12 | public class MultiDialog1 : IDialog 13 | { 14 | public async Task StartAsync(IDialogContext context) 15 | { 16 | if (context == null) 17 | { 18 | throw new ArgumentNullException(nameof(context)); 19 | } 20 | 21 | //Set the Last Dialog in Conversation Data 22 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogMultiDialog1); 23 | 24 | await context.PostAsync(Strings.HelpCaptionMultiDialog1); 25 | context.Done(null); 26 | } 27 | } 28 | 29 | [Serializable] 30 | public class MultiDialog2 : IDialog 31 | { 32 | public async Task StartAsync(IDialogContext context) 33 | { 34 | if (context == null) 35 | { 36 | throw new ArgumentNullException(nameof(context)); 37 | } 38 | 39 | var message = CreateMultiDialog(context); 40 | 41 | //Set the Last Dialog in Conversation Data 42 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogMultiDialog2); 43 | 44 | await context.PostAsync(message); 45 | context.Done(null); 46 | } 47 | 48 | private IMessageActivity CreateMultiDialog(IDialogContext context) 49 | { 50 | var message = context.MakeMessage(); 51 | var attachment = CreateMultiDialogCard(); 52 | message.Attachments.Add(attachment); 53 | return message; 54 | } 55 | 56 | private Attachment CreateMultiDialogCard() 57 | { 58 | return new HeroCard 59 | { 60 | Title = Strings.MultiDialogCardTitle, 61 | Subtitle = Strings.MultiDialogCardSubTitle, 62 | Text = Strings.MultiDialogCardText, 63 | Images = new List { new CardImage(ConfigurationManager.AppSettings["BaseUri"].ToString() + "/public/assets/computer_person.jpg") }, 64 | Buttons = new List 65 | { 66 | new CardAction("invoke", Strings.CaptionInvokeHelloDailog, value: "{\"" + Strings.InvokeRequestJsonKey + "\": \"" + Strings.cmdHelloDialog + "\"}"), 67 | new CardAction("invoke", Strings.CaptionInvokeMultiDailog, value: "{\"" + Strings.InvokeRequestJsonKey+ "\": \"" + Strings.cmdMultiDialog1 + "\"}"), 68 | } 69 | }.ToAttachment(); 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/basic/O365ConnectorCardDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using Microsoft.Bot.Connector.Teams.Models; 4 | using Microsoft.Teams.TemplateBotCSharp.Properties; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Threading.Tasks; 8 | 9 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 10 | { 11 | /// 12 | /// This is Connector Card Dialog Class. Main purpose of this class is to display the Connector Card basic examples 13 | /// 14 | 15 | [Serializable] 16 | public class O365ConnectorCardDialog : IDialog 17 | { 18 | public async Task StartAsync(IDialogContext context) 19 | { 20 | if (context == null) 21 | { 22 | throw new ArgumentNullException(nameof(context)); 23 | } 24 | 25 | //Set the Last Dialog in Conversation Data 26 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogConnectorCardDialog); 27 | 28 | // get the input number for the example to show if the user passed it into the command - e.g. 'show connector card 2' 29 | var activity = (IMessageActivity)context.Activity; 30 | 31 | string inputNumber = activity.Text.Substring(activity.Text.Length - 1, 1).Trim(); 32 | Attachment attachment = null; 33 | 34 | /* 35 | * Below are a few more examples of more complex connector cards 36 | * To use: simply call 'connector card 2' or 'connector card 3' 37 | * Note: these examples are just filled with demo data and that demo data is NOT using the localization system 38 | * Note: these examples are leveraging an actual JSON string as their input content - more examples can be found at 39 | * https://messagecardplayground.azurewebsites.net/ - it is recommended that the developer use the method 40 | * shown above in order to get the benefits of type checking from the O365ConnectorCard class 41 | */ 42 | 43 | switch (inputNumber) 44 | { 45 | case "3": 46 | attachment = O365ConnectorCardImageInSection(); 47 | break; 48 | case "2": 49 | attachment = O365ConnectorCardFactsInSection(); 50 | break; 51 | default: 52 | case "1": 53 | attachment = O365ConnectorCardDefault(); 54 | break; 55 | } 56 | 57 | var message = context.MakeMessage(); 58 | message.Attachments.Add(attachment); 59 | await context.PostAsync(message); 60 | 61 | context.Done(null); 62 | } 63 | 64 | /// 65 | /// Connector card with text in section with card title sample 66 | /// 67 | /// 68 | public static Attachment O365ConnectorCardDefault() 69 | { 70 | var o365connector = new O365ConnectorCard 71 | { 72 | Title = Strings.O365V1Title, 73 | Sections = new List 74 | { 75 | new O365ConnectorCardSection{ Text= Strings.O365V1Section1 }, 76 | new O365ConnectorCardSection{ Text= Strings.O365V1Section2 } 77 | }, 78 | }; 79 | 80 | return o365connector.ToAttachment(); 81 | } 82 | 83 | /// 84 | /// connector card with title, actvity title, facts in section sample 85 | /// 86 | /// 87 | public static Attachment O365ConnectorCardFactsInSection() 88 | { 89 | var section = new O365ConnectorCardSection 90 | { 91 | Title = Strings.O365V2Title, 92 | ActivityTitle = Strings.O365V2ActivityTitle, 93 | Facts = new List 94 | { 95 | new O365ConnectorCardFact(Strings.O365V2Fact1Key,Strings.O365V2Fact1Value), 96 | new O365ConnectorCardFact(Strings.O365V2Fact2Key,Strings.O365V2Fact2Value), 97 | new O365ConnectorCardFact(Strings.O365V2Fact3Key,Strings.O365V2Fact3Value), 98 | new O365ConnectorCardFact(Strings.O365V2Fact4Key,Strings.O365V2Fact4Value) 99 | } 100 | }; 101 | 102 | var o365connector = new O365ConnectorCard 103 | { 104 | ThemeColor = Strings.O365V2themecolor, 105 | Sections = new List { section }, 106 | }; 107 | 108 | return o365connector.ToAttachment(); 109 | } 110 | 111 | /// 112 | /// connector card with title, actvity title, activity subtitle, activity image, facts in section sample 113 | /// 114 | /// 115 | public static Attachment O365ConnectorCardImageInSection() 116 | { 117 | var section = new O365ConnectorCardSection 118 | { 119 | ActivityTitle = Strings.O365V3ActivityTitle, 120 | ActivitySubtitle = Strings.O365V3ActivitySubtitle, 121 | ActivityImage = Strings.O365V3ImageUrl, 122 | Facts = new List 123 | { 124 | new O365ConnectorCardFact(Strings.O365V3Fact1Key,Strings.O365V3Fact1Value), 125 | new O365ConnectorCardFact(Strings.O365V3Fact2Key,Strings.O365V3Fact2Value), 126 | } 127 | }; 128 | 129 | var o365connector = new O365ConnectorCard 130 | { 131 | ThemeColor = Strings.O365V3ThemeColor, 132 | Summary = Strings.O365V3Summary, 133 | Title = Strings.O365V3Title, 134 | Sections = new List { section }, 135 | Text = Strings.O365V3Text 136 | }; 137 | 138 | return o365connector.ToAttachment(); 139 | } 140 | } 141 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/basic/PopupSigninCardDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using Microsoft.Teams.TemplateBotCSharp.Properties; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Configuration; 7 | using System.Threading.Tasks; 8 | 9 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 10 | { 11 | /// 12 | /// This is PopUp SignIn Dialog Class. Main purpose of this class is to Display the PopUp SignIn Card 13 | /// 14 | 15 | [Serializable] 16 | public class PopupSigninCardDialog : IDialog 17 | { 18 | public async Task StartAsync(IDialogContext context) 19 | { 20 | if (context == null) 21 | { 22 | throw new ArgumentNullException(nameof(context)); 23 | } 24 | 25 | //Set the Last Dialog in Conversation Data 26 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogPopUpSignIn); 27 | 28 | var message = context.MakeMessage(); 29 | var attachment = GetPopUpSignInCard(); 30 | 31 | message.Attachments.Add(attachment); 32 | 33 | await context.PostAsync(message); 34 | 35 | context.Done(null); 36 | } 37 | 38 | private static Attachment GetPopUpSignInCard() 39 | { 40 | string baseUri = Convert.ToString(ConfigurationManager.AppSettings["BaseUri"]); 41 | 42 | var heroCard = new HeroCard 43 | { 44 | Title = Strings.PopUpSignInCardTitle, 45 | Buttons = new List 46 | { 47 | new CardAction(ActionTypes.Signin, Strings.PopUpSignInCardButtonTitle, value: baseUri + "/popUpSignin.html?height=200&width=200"), 48 | } 49 | }; 50 | 51 | return heroCard.ToAttachment(); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/basic/ThumbnailcardDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using Microsoft.Teams.TemplateBotCSharp.Properties; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Threading.Tasks; 7 | 8 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 9 | { 10 | /// 11 | /// This is Thumbnail Card Dialog Class. Main purpose of this class is to display the Thumbnail Card example 12 | /// 13 | 14 | [Serializable] 15 | public class ThumbnailcardDialog : IDialog 16 | { 17 | public async Task StartAsync(IDialogContext context) 18 | { 19 | if (context == null) 20 | { 21 | throw new ArgumentNullException(nameof(context)); 22 | } 23 | 24 | //Set the Last Dialog in Conversation Data 25 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogThumbnailCard); 26 | 27 | var message = context.MakeMessage(); 28 | var attachment = GetThumbnailCard(); 29 | 30 | message.Attachments.Add(attachment); 31 | 32 | await context.PostAsync(message); 33 | 34 | context.Done(null); 35 | } 36 | 37 | private static Attachment GetThumbnailCard() 38 | { 39 | var thumbnailCard = new ThumbnailCard 40 | { 41 | Title = Strings.ThumbnailCardTitle, 42 | Subtitle = Strings.ThumbnailCardSubTitle, 43 | Text = Strings.ThumbnailCardTextMsg, 44 | Images = new List { new CardImage(Strings.ThumbnailCardImageUrl) }, 45 | Buttons = new List 46 | { 47 | new CardAction(ActionTypes.OpenUrl, Strings.ThumbnailCardButtonCaption, value: "https://docs.microsoft.com/en-us/bot-framework/dotnet/bot-builder-dotnet-add-rich-card-attachments"), 48 | new CardAction(ActionTypes.MessageBack, Strings.MessageBackCardButtonCaption, value: "{\"" + Strings.cmdValueMessageBack + "\": \"" + Strings.cmdValueMessageBack+ "\"}", text:Strings.cmdValueMessageBack, displayText:Strings.MessageBackDisplayedText) 49 | } 50 | }; 51 | 52 | return thumbnailCard.ToAttachment(); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/moderate/BeginDialogExampleDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Teams.TemplateBotCSharp.Properties; 3 | using System; 4 | using System.Threading.Tasks; 5 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 6 | { 7 | /// 8 | /// This is Begin Dialog Class. Main purpose of this class is to notify users that Child dialog has been called 9 | /// and its a Basic example to call Child dialog from Root Dialog. 10 | /// 11 | 12 | [Serializable] 13 | public class BeginDialogExampleDialog : IDialog 14 | { 15 | public async Task StartAsync(IDialogContext context) 16 | { 17 | if (context == null) 18 | { 19 | throw new ArgumentNullException(nameof(context)); 20 | } 21 | 22 | //Set the Last Dialog in Conversation Data 23 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogBeginDialog); 24 | await context.PostAsync(Strings.BeginDialog); 25 | 26 | context.Call(new HelloDialog(), ResumeAfterHelloDialog); 27 | } 28 | 29 | private async Task ResumeAfterHelloDialog(IDialogContext context, IAwaitable result) 30 | { 31 | await context.PostAsync(Strings.BeginDialogEnd); 32 | context.Done(null); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/moderate/ListNamesDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using Microsoft.Teams.TemplateBotCSharp.Properties; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 10 | { 11 | /// 12 | /// This is Fetch Roster Dialog Class. Main purpose of this dialog class is to Call the Roster Api and Post the 13 | /// members information (Name and Id) in Teams. This Dialog is using Thumbnail Card to show the member information in teams. 14 | /// 15 | [Serializable] 16 | public class ListNamesDialog : IDialog 17 | { 18 | public async Task StartAsync(IDialogContext context) 19 | { 20 | if (context == null) 21 | { 22 | throw new ArgumentNullException(nameof(context)); 23 | } 24 | 25 | await context.PostAsync(Strings.RosterWelcomeMsgTitle); 26 | var connectorClient = new ConnectorClient(new Uri(context.Activity.ServiceUrl)); 27 | var response = await connectorClient.Conversations.GetConversationMembersAsync(context.Activity.Conversation.Id); 28 | var message = context.MakeMessage(); 29 | 30 | foreach (var obj in response.ToList()) 31 | { 32 | message.Attachments.Add(GetUserInformationCard(obj.Name, Convert.ToString(obj.Properties["objectId"]))); 33 | } 34 | 35 | //Set the Last Dialog in Conversation Data 36 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogFetchRosterDialog); 37 | 38 | await context.PostAsync(message); 39 | 40 | context.Done(null); 41 | } 42 | 43 | /// 44 | /// Create Card Template with Channel member information 45 | /// 46 | /// 47 | /// 48 | /// 49 | private static Attachment GetUserInformationCard(string userName, string objectId) 50 | { 51 | string chatUrl = "https://teams.microsoft.com/l/chat/0/0?users=8:orgid:" + objectId; 52 | var heroCard = new ThumbnailCard 53 | { 54 | Title = userName, 55 | Buttons = new List { new CardAction(ActionTypes.OpenUrl, Strings.CaptionChatButton, value: chatUrl) } 56 | }; 57 | 58 | return heroCard.ToAttachment(); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/moderate/PromptDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using Microsoft.Teams.TemplateBotCSharp.Properties; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Threading.Tasks; 7 | 8 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 9 | { 10 | /// 11 | /// This is Game Dialog Class. Here are the steps to play the games - 12 | /// 1. Its gives 3 options to users to choose. 13 | /// 2. If user choose any of the option, Bot take confirmation from the user about the choice. 14 | /// 3. Bot reply to the user based on user choice. 15 | /// 16 | [Serializable] 17 | public class PromptDialogExample : IDialog 18 | { 19 | private IEnumerable options = null; 20 | private IEnumerable choiceOptions = null; 21 | 22 | /// 23 | /// In below class constructor, we are initializing the strings Enumerable at runtime. 24 | /// 25 | public PromptDialogExample() 26 | { 27 | options = new string[] { Strings.PlayGameChoice1, Strings.PlayGameChoice2, Strings.PlayGameWrongChoice }; 28 | choiceOptions = new string[] { Strings.OptionYes, Strings.OptionNo }; 29 | } 30 | 31 | /// 32 | /// This is start of the Dialog and Prompting for User name 33 | /// 34 | /// 35 | /// 36 | public async Task StartAsync(IDialogContext context) 37 | { 38 | if (context == null) 39 | { 40 | throw new ArgumentNullException(nameof(context)); 41 | } 42 | 43 | //Set the Last Dialog in Conversation Data 44 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogGameDialog); 45 | 46 | // This will Prompt for Name of the user. 47 | await context.PostAsync(Strings.PlayGamePromptForName); 48 | context.Wait(this.MessageReceivedAsync); 49 | } 50 | 51 | /// 52 | /// Prompt the welcome message. 53 | /// Few options for user to choose any. 54 | /// 55 | /// 56 | /// 57 | /// 58 | private async Task MessageReceivedAsync(IDialogContext context, IAwaitable result) 59 | { 60 | if (result == null) 61 | { 62 | throw new InvalidOperationException((nameof(result)) + Strings.NullException); 63 | } 64 | 65 | //Prompt the user with welcome message before game starts 66 | var resultActivity = await result; 67 | await context.PostAsync(Strings.PlayGameAnswerForName + resultActivity.Text); 68 | 69 | //Here we are prompting few options for user to choose any. 70 | PromptDialog.Choice( 71 | context, 72 | this.ChooseOptions, 73 | options, 74 | Strings.PlayGameStartMsg, 75 | Strings.PromptInvalidMsg, 76 | 3); 77 | } 78 | 79 | /// 80 | /// In this methos, we are taking confirmation from user about the selection. 81 | /// 82 | /// 83 | /// 84 | /// 85 | private async Task ChooseOptions(IDialogContext context, IAwaitable result) 86 | { 87 | var selctedChoice = await result; 88 | 89 | //Once User choose any of the given option, we are taking confirmation from user about the selection. 90 | PromptDialog.Choice( 91 | context, 92 | this.ResultedOptions, 93 | choiceOptions, 94 | Strings.PlayGameReplyMsg + "'" + selctedChoice + "'?", 95 | Strings.PromptInvalidMsg, 96 | 3); 97 | } 98 | 99 | /// 100 | /// End this dialog and pass the user selection to ResumeAfterFlowGame method in Root Dialog 101 | /// 102 | /// 103 | /// 104 | /// 105 | private async Task ResultedOptions(IDialogContext context, IAwaitable result) 106 | { 107 | if (result == null) 108 | { 109 | throw new InvalidOperationException((nameof(result)) + Strings.NullException); 110 | } 111 | 112 | var selctedChoice = await result; 113 | if (selctedChoice == Strings.OptionYes) 114 | { 115 | context.Done(true); 116 | } 117 | else 118 | { 119 | context.Done(false); 120 | } 121 | } 122 | } 123 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/moderate/Quiz1Dialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using Microsoft.Teams.TemplateBotCSharp.Properties; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Threading.Tasks; 7 | 8 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 9 | { 10 | /// 11 | /// This is Quiz1 Dialog and Child Dialog of Quiz Dialog. 12 | /// Calling Sequence of this Dialog is :- 13 | /// Root Dialog --> Quiz Dialog 14 | /// Quiz Dialog --> Quiz1 Dialog (Child dialog of Quiz Dialog) 15 | /// Once this dialog Ends, it will resume in Quiz Dialog 16 | /// 17 | [Serializable] 18 | public class Quiz1Dialog : IDialog 19 | { 20 | public async Task StartAsync(IDialogContext context) 21 | { 22 | if (context == null) 23 | { 24 | throw new ArgumentNullException(nameof(context)); 25 | } 26 | 27 | var message = CreateQuiz(context); 28 | 29 | //Set the Last Dialog in Conversation Data 30 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogQuiz1Dialog); 31 | 32 | await context.PostAsync(message); 33 | context.Wait(this.MessageReceivedAsync); 34 | } 35 | 36 | #region Create Quiz Message Card 37 | private IMessageActivity CreateQuiz(IDialogContext context) 38 | { 39 | var message = context.MakeMessage(); 40 | var attachment = CreateQuizCard(); 41 | message.Attachments.Add(attachment); 42 | return message; 43 | } 44 | 45 | private Attachment CreateQuizCard() 46 | { 47 | return new HeroCard 48 | { 49 | Title = Strings.Quiz1Question, 50 | Images = new List { new CardImage("https://sec.ch9.ms/ch9/7ff5/e07cfef0-aa3b-40bb-9baa-7c9ef8ff7ff5/buildreactionbotframework_960.jpg") }, 51 | Buttons = new List 52 | { 53 | new CardAction(ActionTypes.ImBack, Strings.OptionYes, value: Strings.OptionYes), 54 | new CardAction(ActionTypes.ImBack, Strings.OptionNo, value: Strings.OptionNo) 55 | } 56 | }.ToAttachment(); 57 | } 58 | #endregion 59 | 60 | private async Task MessageReceivedAsync(IDialogContext context, IAwaitable result) 61 | { 62 | if (result == null) 63 | { 64 | throw new InvalidOperationException((nameof(result)) + Strings.NullException); 65 | } 66 | 67 | var activity = await result; 68 | 69 | if (activity.Text.ToLower() == Strings.OptionYes) 70 | { 71 | await context.PostAsync(Strings.QuizAnswerYes); 72 | context.Done(null); 73 | } 74 | else if (activity.Text.ToLower() == Strings.OptionNo) 75 | { 76 | await context.PostAsync(Strings.QuizAnswerNo); 77 | context.Done(null); 78 | } 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/moderate/Quiz2Dialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using Microsoft.Teams.TemplateBotCSharp.Properties; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Threading.Tasks; 7 | 8 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 9 | { 10 | /// 11 | /// This is Quiz2 Dialog and Child of Quiz Dialog as well. 12 | /// Calling sequence for this dialog is :- 13 | /// Quiz Dialog - > Quiz1 Dialog. 14 | /// Quiz1 Dialog Ends -> Quiz2 Dialog (Child dialog of Quiz Dialog). 15 | /// Once this dialog Ends, it will resume in Quiz Dialog. 16 | /// 17 | [Serializable] 18 | public class Quiz2Dialog : IDialog 19 | { 20 | public async Task StartAsync(IDialogContext context) 21 | { 22 | if (context == null) 23 | { 24 | throw new ArgumentNullException(nameof(context)); 25 | } 26 | 27 | var message = CreateQuiz(context); 28 | 29 | //Set the Last Dialog in Conversation Data 30 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogQuiz2Dialog); 31 | 32 | await context.PostAsync(message); 33 | 34 | context.Wait(this.MessageReceivedAsync); 35 | } 36 | 37 | #region Create Quiz Message Card 38 | private Attachment CreateQuizCard() 39 | { 40 | return new HeroCard 41 | { 42 | Title = Strings.Quiz2Question, 43 | Images = new List { new CardImage("https://sec.ch9.ms/ch9/7ff5/e07cfef0-aa3b-40bb-9baa-7c9ef8ff7ff5/buildreactionbotframework_960.jpg") }, 44 | Buttons = new List 45 | { 46 | new CardAction(ActionTypes.ImBack, Strings.OptionYes, value: Strings.OptionYes), 47 | new CardAction(ActionTypes.ImBack, Strings.OptionNo, value: Strings.OptionNo) 48 | } 49 | }.ToAttachment(); 50 | } 51 | 52 | private IMessageActivity CreateQuiz(IDialogContext context) 53 | { 54 | var message = context.MakeMessage(); 55 | var attachment = CreateQuizCard(); 56 | message.Attachments.Add(attachment); 57 | return message; 58 | } 59 | #endregion 60 | 61 | private async Task MessageReceivedAsync(IDialogContext context, IAwaitable result) 62 | { 63 | if (result == null) 64 | { 65 | throw new InvalidOperationException((nameof(result)) + Strings.NullException); 66 | } 67 | 68 | var activity = await result as Activity; 69 | 70 | if (activity.Text.ToLower() == Strings.OptionYes) 71 | { 72 | await context.PostAsync(Strings.QuizAnswerYes); 73 | context.Done(null); 74 | } 75 | else if (activity.Text.ToLower() == Strings.OptionNo) 76 | { 77 | await context.PostAsync(Strings.QuizAnswerNo); 78 | context.Done(null); 79 | } 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/moderate/QuizFullDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Teams.TemplateBotCSharp.Properties; 3 | using System; 4 | using System.Threading.Tasks; 5 | 6 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 7 | { 8 | /// 9 | /// This is Quiz Dialog class and its a example for below scenarios 10 | /// 1. Call Main Dialog from Root Dialog ( Root Dialog -> Quiz Dialog) 11 | /// 2. Call Dialog Within Dialog ( Quiz Dialog -> Quiz1 Dialog) 12 | /// 3. Call Another Dialog after end of Child Dialog ( Quiz1 Dialog --> Quiz2 Dialog, Quiz2 only be called when Quiz1 Completed, its not child dialog of Quiz1 Dialog) 13 | /// 4. Once Quiz 2 is Done -> it will Resume Quiz Dialog 14 | /// 5. Once Quiz Dialog Ends -> it will ResumeRoot Dialog 15 | /// 16 | [Serializable] 17 | public class QuizFullDialog : IDialog 18 | { 19 | public async Task StartAsync(IDialogContext context) 20 | { 21 | if (context == null) 22 | { 23 | throw new ArgumentNullException(nameof(context)); 24 | } 25 | 26 | //Set the Last Dialog in Conversation Data 27 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogQuizDialog); 28 | 29 | await context.PostAsync(Strings.QuizDialogBeginTitle); 30 | context.Call(new Quiz1Dialog(), ResumeAfterRunQuiz1Dialog); 31 | } 32 | 33 | private async Task ResumeAfterRunQuiz1Dialog(IDialogContext context, IAwaitable result) 34 | { 35 | await context.PostAsync(Strings.Quiz1ThanksTitleMsg); 36 | context.Call(new Quiz2Dialog(), this.ResumeAfterBeginQuiz2); 37 | } 38 | 39 | private async Task ResumeAfterBeginQuiz2(IDialogContext context, IAwaitable result) 40 | { 41 | await context.PostAsync(Strings.QuizThanksTitleMsg); 42 | context.Done(null); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/teams/AtMentionDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using Microsoft.Bot.Connector.Teams; 4 | using Microsoft.Teams.TemplateBotCSharp.Properties; 5 | using System; 6 | using System.Threading.Tasks; 7 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 8 | { 9 | /// 10 | /// This is At Mention Dialog Class. Main purpose of this dialog class is to Atmention (@mention) 11 | /// the user who has started the conversation. 12 | /// we can pass ChannelAccount object as first parameter of AddMentionToText method to @mention any user. 13 | /// e.g. ChannelAccount userInformation = new ChannelAccount("29:1TPHVQrnqOI3_FZbeY32VvlBwo1trPhN96SiKYP3av-QCKYGlLBApj-w9fgoI9SCUz4bEmzo9tVlSQdHzgoSzeQ", "Ashish"); 14 | /// 15 | 16 | [Serializable] 17 | public class AtMentionDialog : IDialog 18 | { 19 | public async Task StartAsync(IDialogContext context) 20 | { 21 | if (context == null) 22 | { 23 | throw new ArgumentNullException(nameof(context)); 24 | } 25 | 26 | var message = context.MakeMessage(); 27 | Activity replyActivity = message as Activity; 28 | replyActivity.AddMentionToText(context.Activity.From, MentionTextLocation.PrependText); 29 | await context.PostAsync(replyActivity); 30 | 31 | //Set the Last Dialog in Conversation Data 32 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogAtMentionDialog); 33 | context.Done(null); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/teams/DeepLinkStaticTabDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using Microsoft.Teams.TemplateBotCSharp.Properties; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Configuration; 7 | using System.Threading.Tasks; 8 | 9 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 10 | { 11 | /// 12 | /// This is DeepLink Dialog Class. Main purpose of this class is to show Deep link from Bot to Tab example 13 | /// 14 | 15 | [Serializable] 16 | public class DeepLinkStaticTabDialog : IDialog 17 | { 18 | private const string TabEntityID = "statictab"; 19 | private const string TabConfigEntityID = "configTab"; 20 | private string BotId { get; set; } 21 | private bool IsChannelUser { get; set; } = false; 22 | private string ChannelId { get; set; } 23 | private string TabUrl { get; set; } 24 | private string ButtonCaption { get; set; } 25 | private string DeepLinkCardTitle { get; set; } 26 | 27 | public async Task StartAsync(IDialogContext context) 28 | { 29 | if (context == null) 30 | { 31 | throw new ArgumentNullException(nameof(context)); 32 | } 33 | 34 | BotId = ConfigurationManager.AppSettings["MicrosoftAppId"]; 35 | 36 | GetChannelID(context); 37 | 38 | var message = CreateDeepLinkMessage(context); 39 | 40 | //Set the Last Dialog in Conversation Data 41 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogDeepLinkStaticTabDialog); 42 | 43 | await context.PostAsync(message); 44 | 45 | context.Done(null); 46 | } 47 | 48 | #region Create Deep Link Tab Card 49 | private IMessageActivity CreateDeepLinkMessage(IDialogContext context) 50 | { 51 | var message = context.MakeMessage(); 52 | var attachment = CreateDeepLinkCard(); 53 | message.Attachments.Add(attachment); 54 | return message; 55 | } 56 | 57 | private Attachment CreateDeepLinkCard() 58 | { 59 | if (IsChannelUser) 60 | { 61 | TabUrl = GetConfigTabDeepLinkURL(ChannelId); 62 | ButtonCaption = Strings.DeepLinkCardConfigButtonCaption; 63 | DeepLinkCardTitle = Strings.DeepLinkCardConfigTitle; 64 | } 65 | else 66 | { 67 | TabUrl = GetStaticTabDeepLinkURL(); 68 | ButtonCaption = Strings.DeepLinkCard1To1ButtonCaption; 69 | DeepLinkCardTitle = Strings.DeepLinkCard1To1Title; 70 | } 71 | 72 | return new HeroCard 73 | { 74 | Title = DeepLinkCardTitle, 75 | Buttons = new List 76 | { 77 | new CardAction(ActionTypes.OpenUrl, ButtonCaption, value: TabUrl), 78 | } 79 | }.ToAttachment(); 80 | } 81 | 82 | private string GetStaticTabDeepLinkURL() 83 | { 84 | //Example - BaseURL + 28:BotId + TabEntityId (set in the manifest) + ?conversationType=chat 85 | return "https://teams.microsoft.com/l/entity/28:" + BotId + "/" + TabEntityID + "?conversationType=chat"; 86 | } 87 | 88 | private string GetConfigTabDeepLinkURL(string channelId) 89 | { 90 | //Example - BaseURL + BotId + TabConfigEntityId (e.g. entityId: "configTab" : it should be same which we have set at the time of Tab Creation like below) + ?context= + {"channelId":"19:47051e5643ed49b58665e1250b6db460@thread.skype"} (should be encoded) 91 | //microsoftTeams.settings.setSettings({ suggestedDisplayName: "Bot Info", contentUrl: createTabUrl(), entityId: "configTab" }); 92 | 93 | channelId = channelId.Replace("19:", "19%3a") 94 | .Replace("@thread.skype", "%40thread.skype"); 95 | 96 | return "https://teams.microsoft.com/l/entity/" + BotId + "/" + TabConfigEntityID + "?context=%7B%22channelId%22%3A%22" + channelId + "%22%7D"; 97 | } 98 | 99 | private void GetChannelID(IDialogContext context) 100 | { 101 | 102 | IsChannelUser = false; 103 | 104 | if (context.Activity.ChannelData != null) 105 | { 106 | ChannelId = context.Activity.ChannelData["teamsChannelId"]; 107 | 108 | if (!string.IsNullOrEmpty(ChannelId)) 109 | { 110 | IsChannelUser = true; 111 | } 112 | else 113 | { 114 | IsChannelUser = false; 115 | } 116 | } 117 | } 118 | #endregion 119 | } 120 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/teams/DisplayCardsDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Teams.TemplateBotCSharp.Properties; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 7 | { 8 | /// 9 | /// This is Card Dialog Class. Main purpose of this dialog class is to post different types of Cards example like Hero Card, Thumbnail Card etc. 10 | /// 11 | [Serializable] 12 | public class DisplayCardsDialog : IDialog 13 | { 14 | private IEnumerable options = null; 15 | 16 | public DisplayCardsDialog() 17 | { 18 | options = new List { Strings.DisplayCardHeroCard, Strings.DisplayCardThumbnailCard, Strings.DisplayCardO365ConnectorCardDefault, Strings.DisplayCardO365ConnectorCard2, Strings.DisplayCardO365ConnectorCard3, Strings.DisplayCardO365ConnectorActionableCardDefault, Strings.DisplayCardO365ConnectorActionableCard2, Strings.DisplayCardAdaptiveCard }; 19 | } 20 | 21 | public async Task StartAsync(IDialogContext context) 22 | { 23 | if (context == null) 24 | { 25 | throw new ArgumentNullException(nameof(context)); 26 | } 27 | 28 | //Set the Last Dialog in Conversation Data 29 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogDisplayCardsDialog); 30 | 31 | await context.PostAsync(Strings.DisplayCardMsgTitle); 32 | 33 | PromptDialog.Choice( 34 | context, 35 | this.DisplaySelectedCard, 36 | options, 37 | Strings.DisplayCardPromptText, 38 | Strings.PromptInvalidMsg, 39 | 3); 40 | } 41 | 42 | public async Task DisplaySelectedCard(IDialogContext context, IAwaitable result) 43 | { 44 | var selectedCard = await result; 45 | 46 | if (selectedCard.Equals(Strings.DisplayCardHeroCard)) 47 | { 48 | context.Call(new HeroCardDialog(), ResumeAfterOptionDialog); 49 | } 50 | else if (selectedCard.Equals(Strings.DisplayCardThumbnailCard)) 51 | { 52 | context.Call(new ThumbnailcardDialog(), ResumeAfterOptionDialog); 53 | } 54 | else if (selectedCard.Equals(Strings.DisplayCardO365ConnectorCardDefault)) 55 | { 56 | context.Call(new O365ConnectorCardDialog(), ResumeAfterOptionDialog); 57 | } 58 | else if (selectedCard.Equals(Strings.DisplayCardO365ConnectorCard2)) 59 | { 60 | context.Call(new O365ConnectorCardDialog(), ResumeAfterOptionDialog); 61 | } 62 | else if (selectedCard.Equals(Strings.DisplayCardO365ConnectorCard3)) 63 | { 64 | context.Call(new O365ConnectorCardDialog(), ResumeAfterOptionDialog); 65 | } 66 | else if (selectedCard.Equals(Strings.DisplayCardO365ConnectorActionableCardDefault)) 67 | { 68 | context.Call(new O365ConnectorCardActionsDialog(), ResumeAfterOptionDialog); 69 | } 70 | else if (selectedCard.Equals(Strings.DisplayCardO365ConnectorActionableCard2)) 71 | { 72 | context.Call(new O365ConnectorCardActionsDialog(), ResumeAfterOptionDialog); 73 | } 74 | else if (selectedCard.Equals(Strings.DisplayCardAdaptiveCard)) 75 | { 76 | context.Call(new AdaptiveCardDialog(), ResumeAfterOptionDialog); 77 | } 78 | } 79 | 80 | private async Task ResumeAfterOptionDialog(IDialogContext context, IAwaitable result) 81 | { 82 | try 83 | { 84 | var message = await result; 85 | context.Done(null); 86 | } 87 | catch (Exception ex) 88 | { 89 | await context.PostAsync(Strings.DisplayCardErrorMsg + ex.Message); 90 | } 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/teams/FetchRosterDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using Microsoft.Teams.TemplateBotCSharp.Properties; 4 | using Newtonsoft.Json; 5 | using System; 6 | using System.Threading.Tasks; 7 | 8 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 9 | { 10 | /// 11 | /// This is Fetch Roster Payload Dialog Class. Main purpose of this dialog class is to Call the Roster Api and Post the 12 | /// full JSON Payload in Teams returned by Roster Api. 13 | /// 14 | [Serializable] 15 | public class FetchRosterDialog : IDialog 16 | { 17 | public async Task StartAsync(IDialogContext context) 18 | { 19 | if (context == null) 20 | { 21 | throw new ArgumentNullException(nameof(context)); 22 | } 23 | 24 | await context.PostAsync(Strings.RosterWelcomeMsgTitle); 25 | var connectorClient = new ConnectorClient(new Uri(context.Activity.ServiceUrl)); 26 | 27 | var response = await connectorClient.Conversations.GetConversationMembersAsync(context.Activity.Conversation.Id); 28 | string output = JsonConvert.SerializeObject(response); 29 | 30 | var message = context.MakeMessage(); 31 | message.Text = output; 32 | 33 | //Set the Last Dialog in Conversation Data 34 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogFetchPayloadRosterDialog); 35 | 36 | await context.PostAsync(message); 37 | 38 | context.Done(null); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/teams/FetchTeamsInfoDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using Microsoft.Bot.Connector.Teams; 4 | using Microsoft.Bot.Connector.Teams.Models; 5 | using Microsoft.Teams.TemplateBotCSharp.Properties; 6 | using System; 7 | using System.Threading.Tasks; 8 | using System.Web; 9 | 10 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 11 | { 12 | /// 13 | /// This is Fetch Teams Info Dialog Class main purpose of this dialog class is to display Team Name, TeamId and AAD GroupId. 14 | /// 15 | [Serializable] 16 | public class FetchTeamsInfoDialog : IDialog 17 | { 18 | public async Task StartAsync(IDialogContext context) 19 | { 20 | if (context == null) 21 | { 22 | throw new ArgumentNullException(nameof(context)); 23 | } 24 | 25 | var team = context.Activity.GetChannelData().Team; 26 | 27 | if (team != null) 28 | { 29 | var connectorClient = new ConnectorClient(new Uri(context.Activity.ServiceUrl)); 30 | 31 | // Handle for channel conversation, AAD GroupId only exists within channel 32 | TeamDetails teamDetails = await connectorClient.GetTeamsConnectorClient().Teams.FetchTeamDetailsAsync(team.Id); 33 | 34 | var message = context.MakeMessage(); 35 | message.Text = GenerateTable(teamDetails); 36 | 37 | await context.PostAsync(message); 38 | } 39 | else 40 | { 41 | // Handle for 1 to 1 bot conversation 42 | await context.PostAsync(Strings.TeamInfo1To1ConversationError); 43 | } 44 | 45 | //Set the Last Dialog in Conversation Data 46 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogFetchTeamInfoDialog); 47 | 48 | context.Done(null); 49 | } 50 | 51 | /// 52 | /// Generate HTML dynamically to show TeamId, TeamName and AAD GroupId in table format 53 | /// 54 | /// 55 | /// 56 | private string GenerateTable(TeamDetails teamDetails) 57 | { 58 | if (teamDetails == null) 59 | { 60 | return string.Empty; 61 | } 62 | 63 | string tableHtml = $@" 64 | 65 | 66 | 67 |
Team id {HttpUtility.HtmlEncode(teamDetails.Id)}
Team name {HttpUtility.HtmlEncode(teamDetails.Name)}
AAD group id {HttpUtility.HtmlEncode(teamDetails.AadGroupId)}
"; 68 | return tableHtml; 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/teams/HelpDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using Microsoft.Teams.TemplateBotCSharp.Properties; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Threading.Tasks; 7 | 8 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 9 | { 10 | /// 11 | /// This is Help Dialog Class. Main purpose of this dialog class is to post the help commands in Teams. 12 | /// These are Actionable help commands for easy to use. 13 | /// 14 | [Serializable] 15 | public class HelpDialog : IDialog 16 | { 17 | public async Task StartAsync(IDialogContext context) 18 | { 19 | if (context == null) 20 | { 21 | throw new ArgumentNullException(nameof(context)); 22 | } 23 | 24 | var message = context.MakeMessage(); 25 | 26 | //Set the Last Dialog in Conversation Data 27 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogHelpDialog); 28 | 29 | // This will create Interactive Card with help command buttons 30 | 31 | message.Attachments = new List 32 | { 33 | new HeroCard(Strings.HelpTitle) 34 | { 35 | Buttons = new List 36 | { 37 | new CardAction(ActionTypes.ImBack, Strings.HelpCaptionRunQuiz, value: Strings.cmdRunQuiz), 38 | new CardAction(ActionTypes.ImBack, Strings.HelpCaptionFetchRoster, value: Strings.cmdFetchRoster), 39 | new CardAction(ActionTypes.ImBack, Strings.HelpCaptionPlayGame, value: Strings.cmdPrompt), 40 | new CardAction(ActionTypes.ImBack, Strings.HelpCaptionRosterPayload, value: Strings.cmdRosterPayload), 41 | new CardAction(ActionTypes.ImBack, Strings.HelpCaptionDialogFlow, value: Strings.cmdDialogFlow), 42 | new CardAction(ActionTypes.ImBack, Strings.HelpCaptionHelloDialog, value: Strings.cmdHelloDialog), 43 | new CardAction(ActionTypes.ImBack, Strings.HelpCaptionAtMention, value: Strings.cmdRunAtMentionDialog), 44 | new CardAction(ActionTypes.ImBack, Strings.HelpCaptionMultiDialog1, value: Strings.cmdMultiDialog1), 45 | new CardAction(ActionTypes.ImBack, Strings.HelpCaptionMultiDialog2, value: Strings.cmdMultiDialog2), 46 | new CardAction(ActionTypes.ImBack, Strings.HelpCaptionFetchLastDialog, value: Strings.cmdFetchLastDialog), 47 | new CardAction(ActionTypes.ImBack, Strings.HelpCaptionSetupMessage, value: Strings.cmdSetupMessage), 48 | new CardAction(ActionTypes.ImBack, Strings.HelpCaptionUpdateMessage, value: Strings.cmdUpdateMessage), 49 | new CardAction(ActionTypes.ImBack, Strings.HelpCaptionSend1on1Conversation, value: Strings.cmdSend1on1ConversationDialog), 50 | new CardAction(ActionTypes.ImBack, Strings.HelpCaptionUpdateCard, value: Strings.cmdUpdateCard), 51 | new CardAction(ActionTypes.ImBack, Strings.HelpCaptionDisplayCards, value: Strings.cmdDisplayCards), 52 | new CardAction(ActionTypes.ImBack, Strings.HelpCaptionDeepLink, value: Strings.cmdDeepLink), 53 | new CardAction(ActionTypes.ImBack, Strings.HelpCaptionAuthSample, value: Strings.cmdAuthSampleDialog), 54 | new CardAction(ActionTypes.ImBack, Strings.HelpLocalTimeZone, value: Strings.cmdLocalTime), 55 | new CardAction(ActionTypes.ImBack, Strings.HelpCaptionMessageBack, value: Strings.cmdMessageBack), 56 | new CardAction(ActionTypes.ImBack, Strings.HelpCaptionPopUpSignIn, value: Strings.cmdPopUpSignIn), 57 | new CardAction(ActionTypes.ImBack, Strings.HelpCaptionTeamInfo, value: Strings.cmdGetTeamInfo) 58 | } 59 | }.ToAttachment() 60 | }; 61 | 62 | await context.PostAsync(message); 63 | context.Done(null); 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/teams/ProactiveMsgTo1to1Dialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using Microsoft.Bot.Connector.Teams.Models; 4 | using Microsoft.Teams.TemplateBotCSharp.Properties; 5 | using System; 6 | using System.Threading.Tasks; 7 | 8 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 9 | { 10 | /// 11 | /// This is Proactive Message Dialog Class. Main purpose of this class is to show the Send Proactive Message Example 12 | /// 13 | [Serializable] 14 | public class ProactiveMsgTo1to1Dialog : IDialog 15 | { 16 | public async Task StartAsync(IDialogContext context) 17 | { 18 | if (context == null) 19 | { 20 | throw new ArgumentNullException(nameof(context)); 21 | } 22 | 23 | //Set the Last Dialog in Conversation Data 24 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogSend1on1Dialog); 25 | 26 | var userId = context.Activity.From.Id; 27 | var botId = context.Activity.Recipient.Id; 28 | var botName = context.Activity.Recipient.Name; 29 | 30 | var channelData = context.Activity.GetChannelData(); 31 | var connectorClient = new ConnectorClient(new Uri(context.Activity.ServiceUrl)); 32 | 33 | var parameters = new ConversationParameters 34 | { 35 | Bot = new ChannelAccount(botId, botName), 36 | Members = new ChannelAccount[] { new ChannelAccount(userId) }, 37 | ChannelData = new TeamsChannelData 38 | { 39 | Tenant = channelData.Tenant 40 | } 41 | }; 42 | 43 | var conversationResource = await connectorClient.Conversations.CreateConversationAsync(parameters); 44 | 45 | var message = Activity.CreateMessageActivity(); 46 | message.From = new ChannelAccount(botId, botName); 47 | message.Conversation = new ConversationAccount(id: conversationResource.Id.ToString()); 48 | message.Text = Strings.Send1on1Prompt; 49 | 50 | await connectorClient.Conversations.SendToConversationAsync((Activity)message); 51 | 52 | context.Done(null); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/teams/UpdateCardMsgDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using Microsoft.Teams.TemplateBotCSharp.Properties; 4 | using Microsoft.Teams.TemplateBotCSharp.Utility; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Configuration; 8 | using System.Threading.Tasks; 9 | 10 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 11 | { 12 | /// 13 | /// This is update card dialog class. Main purpose of this class is to update the card, if user has already setup the card message from below dialog file 14 | /// microsoft-teams-sample-complete-csharp\template-bot-master-csharp\src\dialogs\examples\teams\updatecardmsgsetupdialog.cs 15 | /// 16 | [Serializable] 17 | public class UpdateCardMsgDialog : IDialog 18 | { 19 | public int updateCounter; 20 | 21 | public async Task StartAsync(IDialogContext context) 22 | { 23 | if (context == null) 24 | { 25 | throw new ArgumentNullException(nameof(context)); 26 | } 27 | 28 | if (!string.IsNullOrEmpty(context.Activity.ReplyToId)) 29 | { 30 | Activity activity = context.Activity as Activity; 31 | updateCounter = TemplateUtility.ParseUpdateCounterJson(activity); 32 | 33 | var updatedMessage = CreateUpdatedMessage(context); 34 | 35 | ConnectorClient client = new ConnectorClient(new Uri(context.Activity.ServiceUrl)); 36 | 37 | try 38 | { 39 | ResourceResponse resp = await client.Conversations.UpdateActivityAsync(context.Activity.Conversation.Id, context.Activity.ReplyToId, (Activity)updatedMessage); 40 | await context.PostAsync(Strings.UpdateCardMessageConfirmation); 41 | } 42 | catch (Exception ex) 43 | { 44 | await context.PostAsync(Strings.ErrorUpdatingCard + ex.Message); 45 | } 46 | } 47 | else 48 | { 49 | await context.PostAsync(Strings.NoMsgToUpdate); 50 | } 51 | 52 | context.Done(null); 53 | 54 | //Set the Last Dialog in Conversation Data 55 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogSetupUpdateCard); 56 | } 57 | 58 | #region Create Updated Card Message 59 | private IMessageActivity CreateUpdatedMessage(IDialogContext context) 60 | { 61 | var message = context.MakeMessage(); 62 | var attachment = CreateUpdatedCardAttachment(); 63 | message.Attachments.Add(attachment); 64 | return message; 65 | } 66 | 67 | private Attachment CreateUpdatedCardAttachment() 68 | { 69 | return new HeroCard 70 | { 71 | Title = Strings.UpdatedCardTitle + " " + updateCounter, 72 | Subtitle = Strings.UpdatedCardSubTitle, 73 | Images = new List { new CardImage(ConfigurationManager.AppSettings["BaseUri"].ToString() + "/public/assets/computer_person.jpg") }, 74 | Buttons = new List 75 | { 76 | new CardAction(ActionTypes.MessageBack, Strings.UpdateCardButtonCaption, value: "{\"updateKey\": \"" + ++updateCounter + "\"}", text: DialogMatches.UpdateCard) 77 | } 78 | }.ToAttachment(); 79 | } 80 | #endregion 81 | } 82 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/teams/UpdateCardMsgSetupDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using Microsoft.Teams.TemplateBotCSharp.Properties; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Configuration; 7 | using System.Threading.Tasks; 8 | 9 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 10 | { 11 | /// 12 | /// This is setup card dialog class. Main purpose of this class is to setup the card message and then user can update the card using below update dialog file 13 | /// microsoft-teams-sample-complete-csharp\template-bot-master-csharp\src\dialogs\examples\teams\UpdateCardMsgDialog.cs 14 | /// 15 | [Serializable] 16 | public class UpdateCardMsgSetupDialog : IDialog 17 | { 18 | public int updateCounter = 0; 19 | public async Task StartAsync(IDialogContext context) 20 | { 21 | if (context == null) 22 | { 23 | throw new ArgumentNullException(nameof(context)); 24 | } 25 | 26 | var message = SetupMessage(context); 27 | await context.PostAsync(message); 28 | 29 | //Set the Last Dialog in Conversation Data 30 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogSetupUpdateCard); 31 | 32 | context.Done(null); 33 | } 34 | 35 | #region Create Message to Setup Card 36 | private IMessageActivity SetupMessage(IDialogContext context) 37 | { 38 | var message = context.MakeMessage(); 39 | var attachment = CreateCard(); 40 | message.Attachments.Add(attachment); 41 | return message; 42 | } 43 | 44 | private Attachment CreateCard() 45 | { 46 | return new HeroCard 47 | { 48 | Title = Strings.SetUpCardTitle, 49 | Subtitle = Strings.SetupCardSubTitle, 50 | Images = new List { new CardImage("https://sec.ch9.ms/ch9/7ff5/e07cfef0-aa3b-40bb-9baa-7c9ef8ff7ff5/buildreactionbotframework_960.jpg") }, 51 | Buttons = new List 52 | { 53 | new CardAction(ActionTypes.MessageBack, Strings.UpdateCardButtonCaption, value: "{\"updateKey\": \"" + ++updateCounter + "\"}", text: DialogMatches.UpdateCard) 54 | } 55 | }.ToAttachment(); 56 | } 57 | #endregion 58 | } 59 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/teams/UpdateTextMsgDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using Microsoft.Teams.TemplateBotCSharp.Properties; 4 | using System; 5 | using System.Threading.Tasks; 6 | 7 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 8 | { 9 | /// 10 | /// This is Update Text Dialog Class. Main purpose of this class is to Update the Text in Bot 11 | /// 12 | [Serializable] 13 | public class UpdateTextMsgDialog : IDialog 14 | { 15 | public async Task StartAsync(IDialogContext context) 16 | { 17 | if (context == null) 18 | { 19 | throw new ArgumentNullException(nameof(context)); 20 | } 21 | 22 | string cachedMessage = string.Empty; 23 | 24 | if (context.UserData.TryGetValue(Strings.SetUpMsgKey, out cachedMessage)) 25 | { 26 | IMessageActivity reply = context.MakeMessage(); 27 | reply.Text = Strings.UpdateMessagePrompt; 28 | 29 | ConnectorClient client = new ConnectorClient(new Uri(context.Activity.ServiceUrl)); 30 | ResourceResponse resp = await client.Conversations.UpdateActivityAsync(context.Activity.Conversation.Id, cachedMessage, (Activity)reply); 31 | 32 | await context.PostAsync(Strings.UpdateMessageConfirmation); 33 | } 34 | else 35 | { 36 | await context.PostAsync(Strings.ErrorTextMessageUpdate); 37 | } 38 | 39 | //Set the Last Dialog in Conversation Data 40 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogUpdateMessasge); 41 | 42 | context.Done(null); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/src/dialogs/examples/teams/UpdateTextMsgSetupDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using Microsoft.Teams.TemplateBotCSharp.Properties; 4 | using System; 5 | using System.Threading.Tasks; 6 | 7 | namespace Microsoft.Teams.TemplateBotCSharp.Dialogs 8 | { 9 | /// 10 | /// This is Update Setup Text Dialog Class. Main purpose of this class is to Setup the text message that will be update later in Bot example. 11 | /// 12 | [Serializable] 13 | public class UpdateTextMsgSetupDialog : IDialog 14 | { 15 | public async Task StartAsync(IDialogContext context) 16 | { 17 | if (context == null) 18 | { 19 | throw new ArgumentNullException(nameof(context)); 20 | } 21 | 22 | IMessageActivity reply = context.MakeMessage(); 23 | reply.Text = Strings.SetupMessagePrompt; 24 | 25 | ConnectorClient client = new ConnectorClient(new Uri(context.Activity.ServiceUrl)); 26 | ResourceResponse resp = await client.Conversations.ReplyToActivityAsync((Activity)reply); 27 | 28 | //Set the Last Dialog in Conversation Data 29 | context.UserData.SetValue(Strings.LastDialogKey, Strings.LastDialogSetupMessasge); 30 | 31 | //Set the Last Dialog in Conversation Data 32 | context.UserData.SetValue(Strings.SetUpMsgKey, resp.Id.ToString()); 33 | 34 | context.Done(null); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/utility/InvokeHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Connector; 2 | using Microsoft.Teams.TemplateBotCSharp.Properties; 3 | using System; 4 | 5 | namespace Microsoft.Teams.TemplateBotCSharp.Utility 6 | { 7 | public static class InvokeHandler 8 | { 9 | /// 10 | /// Parse the invoke value and change the activity type as message 11 | /// 12 | /// 13 | /// 14 | public static IMessageActivity HandleInvokeRequest(IMessageActivity activity) 15 | { 16 | if (activity == null) 17 | { 18 | throw new ArgumentNullException(nameof(activity)); 19 | } 20 | 21 | activity.Text = TemplateUtility.ParseInvokeRequestJson(activity.Value.ToString()); 22 | 23 | //Change the Type of Activity to work in exisiting Root Dialog Architecture 24 | activity.Type = Strings.MessageActivity; 25 | 26 | return activity; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /template-bot-master-csharp/utility/TemplateUtility.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Builder.Dialogs.Internals; 3 | using Microsoft.Bot.Connector; 4 | using Microsoft.Bot.Connector.Teams; 5 | using Microsoft.Bot.Connector.Teams.Models; 6 | using Microsoft.Teams.TemplateBotCSharp.Properties; 7 | using Newtonsoft.Json; 8 | using Newtonsoft.Json.Linq; 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Threading; 12 | using System.Threading.Tasks; 13 | 14 | namespace Microsoft.Teams.TemplateBotCSharp.Utility 15 | { 16 | /// 17 | /// Get the locale from incoming activity payload and handle compose extension methods 18 | /// 19 | public static class TemplateUtility 20 | { 21 | public static string GetLocale(Activity activity) 22 | { 23 | if (activity == null) 24 | { 25 | throw new ArgumentNullException(nameof(activity)); 26 | } 27 | 28 | //Get the locale from activity 29 | if (activity.Entities != null) 30 | { 31 | foreach(var entity in activity.Entities) 32 | { 33 | if (string.Equals(entity.Type.ToString().ToLower(), "clientinfo")) 34 | { 35 | var locale = entity.Properties["locale"]; 36 | if (locale != null) 37 | { 38 | return locale.ToString(); 39 | } 40 | } 41 | } 42 | } 43 | return activity.Locale; 44 | } 45 | 46 | public static ComposeExtensionAttachment CreateComposeExtensionCardsAttachments(WikiHelperSearchResult wikiResult, string selectedType) 47 | { 48 | return GetComposeExtensionMainResultAttachment(wikiResult, selectedType).ToComposeExtensionAttachment(GetComposeExtensionPreviewAttachment(wikiResult, selectedType)); 49 | } 50 | 51 | public static ComposeExtensionAttachment CreateComposeExtensionCardsAttachmentsSelectedItem(WikiHelperSearchResult wikiResult, string selectedType) 52 | { 53 | return GetComposeExtensionMainResultAttachment(wikiResult, selectedType).ToComposeExtensionAttachment(); 54 | } 55 | 56 | public static Attachment GetComposeExtensionMainResultAttachment(WikiHelperSearchResult wikiResult, string selectedType) 57 | { 58 | CardType cardType; 59 | Attachment cardAttachment = null; 60 | 61 | var images = new List 62 | { 63 | new CardImage(wikiResult.imageUrl) 64 | }; 65 | 66 | if (Enum.TryParse(selectedType, out cardType)) 67 | { 68 | switch (cardType) 69 | { 70 | case CardType.hero: 71 | cardAttachment = new HeroCard() 72 | { 73 | Title = wikiResult.highlightedTitle, 74 | Text = wikiResult.text, 75 | Images = images 76 | }.ToAttachment(); 77 | break; 78 | case CardType.thumbnail: 79 | cardAttachment = new ThumbnailCard() 80 | { 81 | Title = wikiResult.highlightedTitle, 82 | Text = wikiResult.text, 83 | Images = images 84 | }.ToAttachment(); 85 | break; 86 | } 87 | } 88 | 89 | return cardAttachment; 90 | } 91 | 92 | public static Attachment GetComposeExtensionPreviewAttachment(WikiHelperSearchResult wikiResult, string selectedType) 93 | { 94 | string invokeVal = GetCardActionInvokeValue(wikiResult); 95 | var tapAction = new CardAction("invoke", value: invokeVal); 96 | 97 | CardType cardType; 98 | Attachment cardAttachment = null; 99 | 100 | var images = new List 101 | { 102 | new CardImage(wikiResult.imageUrl) 103 | }; 104 | 105 | if (Enum.TryParse(selectedType, out cardType)) 106 | { 107 | switch (cardType) 108 | { 109 | case CardType.hero: 110 | cardAttachment = new HeroCard() 111 | { 112 | Title = wikiResult.highlightedTitle, 113 | Tap = tapAction, 114 | Images = images 115 | }.ToAttachment(); 116 | break; 117 | case CardType.thumbnail: 118 | cardAttachment = new ThumbnailCard() 119 | { 120 | Title = wikiResult.highlightedTitle, 121 | Tap = tapAction, 122 | Images = images 123 | }.ToAttachment(); 124 | break; 125 | } 126 | } 127 | 128 | return cardAttachment; 129 | } 130 | 131 | private static string GetCardActionInvokeValue(WikiHelperSearchResult wikiResult) 132 | { 133 | InvokeValue invokeValue = new InvokeValue(wikiResult.imageUrl, wikiResult.text, wikiResult.highlightedTitle); 134 | return JsonConvert.SerializeObject(invokeValue); 135 | } 136 | 137 | /// 138 | /// Parse the invoke request json and returned the invoke value 139 | /// 140 | /// 141 | /// 142 | public static string ParseInvokeRequestJson(string inputString) 143 | { 144 | JObject invokeObjects = JObject.Parse(inputString); 145 | 146 | if (invokeObjects.Count > 0) 147 | { 148 | return invokeObjects[Strings.InvokeRequestJsonKey].Value(); 149 | } 150 | 151 | return null; 152 | } 153 | 154 | /// 155 | /// Parse the Update card message back json value returned the updated counter 156 | /// 157 | /// 158 | /// 159 | public static int ParseUpdateCounterJson(Activity activity) 160 | { 161 | if (activity != null && activity.Value != null) 162 | { 163 | JObject invokeObjects = activity.Value as JObject; 164 | 165 | if (invokeObjects != null && invokeObjects.Count > 0) 166 | { 167 | return invokeObjects["updateKey"].Value(); 168 | } 169 | } 170 | 171 | return 0; 172 | } 173 | 174 | public static async Task GetBotUserDataObject(IBotDataStore botDataStore, Activity activity) 175 | { 176 | IAddress key = Address.FromActivity(activity); 177 | BotData botData = await botDataStore.LoadAsync(key, BotStoreType.BotUserData, CancellationToken.None); 178 | return botData; 179 | } 180 | 181 | public static async Task SaveBotUserDataObject(IBotDataStore botDataStore, Activity activity, BotData userData) 182 | { 183 | IAddress key = Address.FromActivity(activity); 184 | await botDataStore.SaveAsync(key, BotStoreType.BotUserData, userData, CancellationToken.None); 185 | } 186 | } 187 | 188 | public class InvokeValue 189 | { 190 | public string imageUrl { get; set; } 191 | public string text { get; set; } 192 | public string highlightedTitle { get; set; } 193 | 194 | public InvokeValue(string urlValue, string textValue, string highlightedTitleValue) 195 | { 196 | imageUrl = urlValue; 197 | text = textValue; 198 | highlightedTitle = highlightedTitleValue; 199 | } 200 | } 201 | } --------------------------------------------------------------------------------