├── .gitignore ├── BotFramework ├── Actions │ ├── Actions.csproj │ ├── IMyRPAClient.cs │ ├── Models │ │ ├── CancelOrderInput.cs │ │ ├── CancelOrderOutput.cs │ │ ├── CreatePurchaseOrderInput.cs │ │ ├── CreatePurchaseOrderOutput.cs │ │ ├── CreateSalesOrderInput.cs │ │ ├── CreateSalesOrderOutput.cs │ │ ├── GetItemsInput.cs │ │ ├── GetItemsOutput.cs │ │ ├── Internal │ │ │ └── GetItemsOutputInternal.cs │ │ └── Item.cs │ ├── MyRPAClient.cs │ └── MyRPAClientMock.cs ├── ActionsTest │ ├── ActionsTest.csproj │ ├── GetItemsOutputInternal.cs │ └── MyRpaClientTest.cs ├── Common │ ├── AdapterWithErrorHandler.cs │ ├── BotDialogSet.cs │ ├── BotIntents.cs │ ├── BotServices.cs │ ├── BotSettings.cs │ ├── Common.csproj │ ├── Models │ │ ├── DetourIntent.cs │ │ ├── EntityState.cs │ │ ├── Intent.cs │ │ ├── IntentType.cs │ │ ├── LuisEntity.cs │ │ └── TerminationIntent.cs │ └── MultiTurnBot.cs ├── Data │ ├── Luis │ │ ├── .luisrc │ │ ├── Sample.json │ │ └── Sample.lu │ └── Packages │ │ ├── CancelOrder.1.0.1-alpha.nupkg │ │ ├── CreatePurchaseOrder.1.0.1-alpha.1.nupkg │ │ ├── CreateSalesOrder.1.0.1-alpha.1.nupkg │ │ └── GetItems.1.0.1-alpha.3.nupkg ├── Dialogs │ ├── Dialogs.csproj │ ├── MyBot.cs │ ├── MyBotSettings.cs │ ├── MyDialog │ │ ├── Base │ │ │ ├── DialogBase.cs │ │ │ └── StatefulDialogBase.cs │ │ ├── CannotFindOrderDialog.cs │ │ ├── DelayedShipmentDialog.cs │ │ ├── GetCustomerProfileDialog.cs │ │ ├── GreetingDialog.cs │ │ └── ReplaceItemDialog.cs │ ├── MyIntents.cs │ └── Utils.cs ├── DialogsTest │ ├── DialogTest.cs │ ├── DialogsTest.csproj │ └── IntentTest.cs ├── OrchestratorClient.Test │ ├── AuthHeadHandlerTest.cs │ ├── ClientTest.cs │ ├── MockHelper.cs │ ├── OrchestratorClient.Test.csproj │ ├── OrchestratorSettingsTest.cs │ └── UtilsTest.cs ├── OrchestratorClient │ ├── Auth │ │ ├── BasicAuthHeadHandler.cs │ │ ├── BasicAuthResponse.cs │ │ ├── BasicAuthSettings.cs │ │ ├── CloudAuthHeadHandler.cs │ │ ├── CloudAuthResponse.cs │ │ ├── CloudAuthSettings.cs │ │ ├── ITokenService.cs │ │ └── TokenService.cs │ ├── IOrchestratorClient.cs │ ├── JobModels │ │ ├── ODataList.cs │ │ ├── StartJobBody.cs │ │ ├── StartJobInfo.cs │ │ └── StartJobResponse.cs │ ├── OrchestratorClient.cs │ ├── OrchestratorClient.csproj │ ├── OrchestratorSettings.cs │ ├── ProcessKey.cs │ └── Utils.cs ├── README.md ├── Resources │ ├── Resource.Designer.cs │ ├── Resource.resx │ └── Resources.csproj ├── SampleBot.sln ├── SampleBot │ ├── ConfigurationCredentialProvider.cs │ ├── Controllers │ │ └── BotController.cs │ ├── DeploymentTemplates │ │ ├── template-with-new-rg.json │ │ └── template-with-preexisting-rg.json │ ├── Program.cs │ ├── SampleBot.csproj │ ├── Startup.cs │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── samplebot.bot │ └── wwwroot │ │ └── default.htm ├── Test.Common │ ├── OrchestratorSettingOption.cs │ └── Test.Common.csproj ├── Utils.Test │ ├── CheckNullReferenceTest.cs │ ├── OrdinalEqualTest.cs │ ├── TestType.cs │ └── Utils.Test.csproj └── Utils │ ├── CommonExtensions.cs │ └── Utils.csproj ├── LICENSE.md ├── README.md └── User guide.pdf /.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/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | # *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | # **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /BotFramework/Actions/Actions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | UiPath.ChatbotSamples.BotFramework.Actions 6 | UiPath.ChatbotSamples.BotFramework.Actions 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | ..\..\..\..\..\..\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.extensions.options\2.1.1\lib\netstandard2.0\Microsoft.Extensions.Options.dll 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /BotFramework/Actions/IMyRPAClient.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using UiPath.ChatbotSamples.BotFramework.Actions.Models; 3 | 4 | namespace UiPath.ChatbotSamples.BotFramework.Actions 5 | { 6 | public interface IMyRpaClient 7 | { 8 | Task GetItemsAsync(GetItemsInput input); 9 | 10 | Task CreatePurchaseOrderAsync(CreatePurchaseOrderInput input); 11 | 12 | Task CreateSalesOrderAsync(CreateSalesOrderInput input); 13 | 14 | Task CancelOrderAsync(CancelOrderInput input); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /BotFramework/Actions/Models/CancelOrderInput.cs: -------------------------------------------------------------------------------- 1 | namespace UiPath.ChatbotSamples.BotFramework.Actions.Models 2 | { 3 | public class CancelOrderInput 4 | { 5 | public string OrderId { get; set; } 6 | 7 | public string CancelReason { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /BotFramework/Actions/Models/CancelOrderOutput.cs: -------------------------------------------------------------------------------- 1 | namespace UiPath.ChatbotSamples.BotFramework.Actions.Models 2 | { 3 | public class CancelOrderOutput 4 | { 5 | public string ReturnLabelLocation { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /BotFramework/Actions/Models/CreatePurchaseOrderInput.cs: -------------------------------------------------------------------------------- 1 | namespace UiPath.ChatbotSamples.BotFramework.Actions.Models 2 | { 3 | public class CreatePurchaseOrderInput 4 | { 5 | public string ItemId { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /BotFramework/Actions/Models/CreatePurchaseOrderOutput.cs: -------------------------------------------------------------------------------- 1 | namespace UiPath.ChatbotSamples.BotFramework.Actions.Models 2 | { 3 | public class CreatePurchaseOrderOutput 4 | { 5 | public string PurchaseOrderId { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /BotFramework/Actions/Models/CreateSalesOrderInput.cs: -------------------------------------------------------------------------------- 1 | namespace UiPath.ChatbotSamples.BotFramework.Actions.Models 2 | { 3 | public class CreateSalesOrderInput 4 | { 5 | public string CustomerId { get; set; } 6 | 7 | public string Quantity { get; set; } 8 | 9 | public string ItemId { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /BotFramework/Actions/Models/CreateSalesOrderOutput.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UiPath.ChatbotSamples.BotFramework.Actions.Models 4 | { 5 | public class CreateSalesOrderOutput 6 | { 7 | public DateTime DeliveryDate { get; set; } 8 | 9 | public string OrderId { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /BotFramework/Actions/Models/GetItemsInput.cs: -------------------------------------------------------------------------------- 1 | namespace UiPath.ChatbotSamples.BotFramework.Actions.Models 2 | { 3 | public class GetItemsInput 4 | { 5 | public string CustomerId { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /BotFramework/Actions/Models/GetItemsOutput.cs: -------------------------------------------------------------------------------- 1 | namespace UiPath.ChatbotSamples.BotFramework.Actions.Models 2 | { 3 | public class GetItemsOutput 4 | { 5 | public Item[] Items { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /BotFramework/Actions/Models/Internal/GetItemsOutputInternal.cs: -------------------------------------------------------------------------------- 1 | namespace UiPath.ChatbotSamples.BotFramework.Actions.Models 2 | { 3 | internal class GetItemsOutputInternal 4 | { 5 | public string[] DeliveryDate { get; set; } 6 | 7 | public string[] DeliveryStatus { get; set; } 8 | 9 | public string[] ItemId { get; set; } 10 | 11 | public string[] ItemName { get; set; } 12 | 13 | public string[] OrderId { get; set; } 14 | 15 | public string[] Quantity { get; set; } 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /BotFramework/Actions/Models/Item.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UiPath.ChatbotSamples.BotFramework.Actions.Models 4 | { 5 | public class Item 6 | { 7 | public string ItemId { get; set; } 8 | 9 | public string ItemName { get; set; } 10 | 11 | public string Quantity { get; set; } 12 | 13 | public string OrderId { get; set; } 14 | 15 | public DateTime DeliveryDate { get; set; } 16 | 17 | public string DeliveryStatus { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /BotFramework/Actions/MyRPAClient.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Options; 2 | using Newtonsoft.Json; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using UiPath.ChatbotSamples.BotFramework.Actions.Models; 8 | using UiPath.ChatbotSamples.BotFramework.OrchestratorClient; 9 | using UiPath.ChatbotSamples.BotFramework.OrchestratorClient.JobModels; 10 | using UiPath.ChatbotSamples.BotFramework.Utils; 11 | 12 | namespace UiPath.ChatbotSamples.BotFramework.Actions 13 | { 14 | public class MyRpaClient: IMyRpaClient 15 | { 16 | private readonly IOrchestratorClient _orchestratorClient; 17 | private readonly OrchestratorSettings _orchestratorSettings; 18 | 19 | public MyRpaClient(IOrchestratorClient orchestratorClient, IOptionsMonitor orchestratorSettingsAccessor) 20 | { 21 | _orchestratorClient = orchestratorClient.CheckNullReference(); 22 | _orchestratorSettings = orchestratorSettingsAccessor.CheckNullReference().CurrentValue; 23 | } 24 | 25 | public async Task CreatePurchaseOrderAsync(CreatePurchaseOrderInput input) 26 | { 27 | var jobInfo = CreateStartJobInfo(GetProcessKey(nameof(CreatePurchaseOrderAsync)), input); 28 | var job = await _orchestratorClient.ExecuteJobAsync(jobInfo); 29 | return JsonConvert.DeserializeObject(job.OutputArguments); 30 | } 31 | 32 | public async Task CreateSalesOrderAsync(CreateSalesOrderInput input) 33 | { 34 | var jobInfo = CreateStartJobInfo(GetProcessKey(nameof(CreateSalesOrderAsync)), input); 35 | var job = await _orchestratorClient.ExecuteJobAsync(jobInfo); 36 | return JsonConvert.DeserializeObject(job.OutputArguments); 37 | } 38 | 39 | public async Task GetItemsAsync(GetItemsInput input) 40 | { 41 | var jobInfo = CreateStartJobInfo(GetProcessKey(nameof(GetItemsAsync)), input); 42 | var job = await _orchestratorClient.ExecuteJobAsync(jobInfo); 43 | var itemsOutputInternal = JsonConvert.DeserializeObject(job.OutputArguments); 44 | return GetItemsOutputFromInternalResult(itemsOutputInternal); 45 | } 46 | 47 | public async Task CancelOrderAsync(CancelOrderInput input) 48 | { 49 | var jobInfo = CreateStartJobInfo(GetProcessKey(nameof(CancelOrderAsync)), input); 50 | var job = await _orchestratorClient.ExecuteJobAsync(jobInfo); 51 | return JsonConvert.DeserializeObject(job.OutputArguments); 52 | } 53 | 54 | // this is just a simple example. A mapper library is recommended if lots of conversion is needed. 55 | private GetItemsOutput GetItemsOutputFromInternalResult(GetItemsOutputInternal itemsOutputInternal) 56 | { 57 | var items = new List(); 58 | for (var i = 0; i < itemsOutputInternal.ItemId.Length; i++) 59 | { 60 | var item = new Item() 61 | { 62 | DeliveryDate = Convert.ToDateTime(itemsOutputInternal.DeliveryDate[i]), 63 | ItemId = itemsOutputInternal.ItemId[i], 64 | OrderId = itemsOutputInternal.OrderId[i], 65 | ItemName = itemsOutputInternal.ItemName[i], 66 | Quantity = itemsOutputInternal.Quantity[i], 67 | DeliveryStatus = itemsOutputInternal.DeliveryStatus[i], 68 | }; 69 | items.Add(item); 70 | } 71 | 72 | return new GetItemsOutput() 73 | { 74 | Items = items.ToArray(), 75 | }; 76 | } 77 | 78 | private string GetProcessKey(string methodName) 79 | { 80 | // default mapping is Process + Async = MethodName. If the procss key not found, it will throw here. 81 | var processName = methodName.EndsWith("Async") ? 82 | methodName.Substring(0, methodName.Length - "Async".Length) : methodName; 83 | return _orchestratorSettings.ProcessKeys.Where(p => p.Process.OrdinalEquals(processName)).First().Key; 84 | } 85 | 86 | private StartJobInfo CreateStartJobInfo(string processKey) 87 | { 88 | return new StartJobInfo() 89 | { 90 | ReleaseKey = processKey, 91 | RobotIds = _orchestratorSettings.RobotIds, 92 | JobsCount = _orchestratorSettings.JobsCount, 93 | Strategy = _orchestratorSettings.Strategy, 94 | }; 95 | } 96 | 97 | private StartJobInfo CreateStartJobInfo(string processKey, T input) where T: class 98 | { 99 | return new StartJobInfo() 100 | { 101 | ReleaseKey = processKey, 102 | RobotIds = _orchestratorSettings.RobotIds, 103 | JobsCount = _orchestratorSettings.JobsCount, 104 | Strategy = _orchestratorSettings.Strategy, 105 | InputArguments = JsonConvert.SerializeObject(input), 106 | }; 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /BotFramework/Actions/MyRPAClientMock.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using UiPath.ChatbotSamples.BotFramework.Actions.Models; 4 | using UiPath.ChatbotSamples.BotFramework.Utils; 5 | 6 | namespace UiPath.ChatbotSamples.BotFramework.Actions 7 | { 8 | public class MyRpaClientMock : IMyRpaClient 9 | { 10 | public async Task GetItemsAsync(GetItemsInput input) 11 | { 12 | if (input.CustomerId.OrdinalEquals("123456")) 13 | { 14 | return new GetItemsOutput() 15 | { 16 | Items = new Item[] 17 | { 18 | new Item() 19 | { 20 | DeliveryDate = DateTime.Today.AddDays(2), 21 | ItemId = "35678", 22 | OrderId = "45436", 23 | ItemName = "wireless speaker", 24 | Quantity = "1", 25 | DeliveryStatus = "out of stock", 26 | }, 27 | new Item() 28 | { 29 | DeliveryDate = DateTime.Today.AddDays(10), 30 | ItemId = "35678", 31 | OrderId = "45436", 32 | ItemName = "TV", 33 | Quantity = "1", 34 | DeliveryStatus = "out of stock", 35 | }, 36 | new Item() 37 | { 38 | DeliveryDate = DateTime.Today.AddDays(-1), 39 | ItemId = "123", 40 | OrderId = "5645", 41 | ItemName = "monitor", 42 | Quantity = "1", 43 | DeliveryStatus = "delivered", 44 | }, 45 | new Item() 46 | { 47 | DeliveryDate = DateTime.Today.AddDays(-5), 48 | ItemId = "123", 49 | OrderId = "5645", 50 | ItemName = "headphone", 51 | Quantity = "1", 52 | DeliveryStatus = "delivered", 53 | }, 54 | }, 55 | }; 56 | } 57 | else 58 | { 59 | return new GetItemsOutput(); 60 | } 61 | } 62 | 63 | public async Task CreatePurchaseOrderAsync(CreatePurchaseOrderInput input) 64 | { 65 | return new CreatePurchaseOrderOutput() 66 | { 67 | PurchaseOrderId = "12345", 68 | }; 69 | } 70 | 71 | public async Task CreateSalesOrderAsync(CreateSalesOrderInput input) 72 | { 73 | return new CreateSalesOrderOutput() 74 | { 75 | DeliveryDate = DateTime.Today.AddDays(7), 76 | OrderId = "45678", 77 | }; 78 | } 79 | 80 | public async Task CancelOrderAsync(CancelOrderInput input) 81 | { 82 | return new CancelOrderOutput() 83 | { 84 | ReturnLabelLocation = "http://returnurl/123", 85 | }; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /BotFramework/ActionsTest/ActionsTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | UiPath.ChatbotSamples.BotFramework.Actions.Test 6 | UiPath.ChatbotSamples.BotFramework.Actions.Test 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /BotFramework/ActionsTest/GetItemsOutputInternal.cs: -------------------------------------------------------------------------------- 1 | namespace UiPath.ChatbotSamples.BotFramework.Actions.Test 2 | { 3 | // instead of open up the protection level in Actions project, 4 | // copy the definition here for test only purpose. 5 | internal class GetItemsOutputInternal 6 | { 7 | public string[] DeliveryDate { get; set; } 8 | 9 | public string[] DeliveryStatus { get; set; } 10 | 11 | public string[] ItemId { get; set; } 12 | 13 | public string[] ItemName { get; set; } 14 | 15 | public string[] OrderId { get; set; } 16 | 17 | public string[] Quantity { get; set; } 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /BotFramework/ActionsTest/MyRpaClientTest.cs: -------------------------------------------------------------------------------- 1 | using Moq; 2 | using Newtonsoft.Json; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using UiPath.ChatbotSamples.BotFramework.Actions.Models; 7 | using UiPath.ChatbotSamples.BotFramework.OrchestratorClient; 8 | using UiPath.ChatbotSamples.BotFramework.OrchestratorClient.JobModels; 9 | using UiPath.ChatbotSamples.BotFramework.Test.Common; 10 | using Xunit; 11 | 12 | namespace UiPath.ChatbotSamples.BotFramework.Actions.Test 13 | { 14 | public class MyRpaClientTest 15 | { 16 | private readonly OrchestratorSettingOption _invalidSetting = new OrchestratorSettingOption( 17 | new OrchestratorSettings() 18 | { 19 | AuthMode = "Cloud", 20 | RefreshToken = "randometoken", 21 | ServiceInstanceLogicalName = "test", 22 | AccountLogicalName = "test", 23 | BaseUrl = "Https://platform.uipath.com", 24 | Strategy = "Specific", 25 | RobotIds = new long[] { 1, 2, 3 }, 26 | ProcessKeys = new ProcessKey[] { new ProcessKey() { Key = Guid.NewGuid().ToString(), Process = "test" } }, 27 | }); 28 | 29 | private readonly OrchestratorSettingOption _validSetting = new OrchestratorSettingOption( 30 | new OrchestratorSettings() 31 | { 32 | AuthMode = "Cloud", 33 | RefreshToken = "randometoken", 34 | ServiceInstanceLogicalName = "test", 35 | AccountLogicalName = "test", 36 | BaseUrl = "Https://platform.uipath.com", 37 | Strategy = "Specific", 38 | RobotIds = new long[] { 1, 2, 3 }, 39 | ProcessKeys = new ProcessKey[] { 40 | new ProcessKey() { Key = Guid.NewGuid().ToString(), Process = "CreatePurchaseOrder" }, 41 | new ProcessKey() { Key = Guid.NewGuid().ToString(), Process = "CreateSalesOrder" }, 42 | new ProcessKey() { Key = Guid.NewGuid().ToString(), Process = "GetItems" }, 43 | new ProcessKey() { Key = Guid.NewGuid().ToString(), Process = "CancelOrder" }, 44 | }, 45 | }); 46 | 47 | [Fact] 48 | public void ThrowIfCannotFindProcessKey() 49 | { 50 | var orchestratorClient = new Mock(); 51 | 52 | var client = new MyRpaClient(orchestratorClient.Object, _invalidSetting); 53 | Assert.Throws(() => client.CreatePurchaseOrderAsync(new CreatePurchaseOrderInput()).GetAwaiter().GetResult()); 54 | } 55 | 56 | [Fact] 57 | public void CreatePurchaseOrderThrowIfNoOutput() 58 | { 59 | CreatePurchaseOrderOutput mockOutput = null; 60 | var mockResponse = CreateJobResponse(mockOutput); 61 | 62 | var client = new MyRpaClient(GetMockOrchestratorClient(mockResponse), _validSetting); 63 | Assert.Throws(() => client.CreatePurchaseOrderAsync(new CreatePurchaseOrderInput()).GetAwaiter().GetResult()); 64 | } 65 | 66 | [Fact] 67 | public void CreatePurchaseOrderSuccess() 68 | { 69 | var mockOutput = new CreatePurchaseOrderOutput() 70 | { 71 | PurchaseOrderId = "123456", 72 | }; 73 | 74 | var mockResponse = CreateJobResponse(mockOutput); 75 | 76 | var client = new MyRpaClient(GetMockOrchestratorClient(mockResponse), _validSetting); 77 | var output = client.CreatePurchaseOrderAsync(new CreatePurchaseOrderInput()).GetAwaiter().GetResult(); 78 | Assert.Equal(mockOutput.PurchaseOrderId, output.PurchaseOrderId); 79 | } 80 | 81 | [Fact] 82 | public void CreateSalesOrderThrowIfNoOutput() 83 | { 84 | CreateSalesOrderOutput mockOutput = null; 85 | var mockResponse = CreateJobResponse(mockOutput); 86 | 87 | var client = new MyRpaClient(GetMockOrchestratorClient(mockResponse), _validSetting); 88 | Assert.Throws(() => client.CreateSalesOrderAsync(new CreateSalesOrderInput()).GetAwaiter().GetResult()); 89 | } 90 | 91 | [Fact] 92 | public void CreateSalesOrderSuccess() 93 | { 94 | var mockOutput = new CreateSalesOrderOutput() 95 | { 96 | OrderId = "123456", 97 | DeliveryDate = DateTime.Today.AddDays(3), 98 | }; 99 | 100 | var mockResponse = CreateJobResponse(mockOutput); 101 | 102 | var client = new MyRpaClient(GetMockOrchestratorClient(mockResponse), _validSetting); 103 | var output = client.CreateSalesOrderAsync(new CreateSalesOrderInput()).GetAwaiter().GetResult(); 104 | Assert.Equal(mockOutput.OrderId, output.OrderId); 105 | Assert.Equal(mockOutput.DeliveryDate.ToShortDateString(), output.DeliveryDate.ToShortDateString()); 106 | } 107 | 108 | [Fact] 109 | public void GetItemsThrowIfNoOutput() 110 | { 111 | GetItemsOutput mockOutput = null; 112 | var mockResponse = CreateJobResponse(mockOutput); 113 | 114 | var client = new MyRpaClient(GetMockOrchestratorClient(mockResponse), _validSetting); 115 | Assert.Throws(() => client.GetItemsAsync(new GetItemsInput()).GetAwaiter().GetResult()); 116 | } 117 | 118 | [Fact] 119 | public void GetItemsSuccess() 120 | { 121 | var mockOutput = new GetItemsOutputInternal() 122 | { 123 | DeliveryDate = new string[] 124 | { 125 | DateTime.Today.AddDays(2).ToShortDateString(), 126 | DateTime.Today.AddDays(10).ToShortDateString() 127 | }, 128 | ItemId = new string[] { "1234", "5678" }, 129 | OrderId = new string[] { "7890", "1357" }, 130 | ItemName = new string[] { "Name1", "Name2" }, 131 | Quantity = new string[] { "1", "2" }, 132 | DeliveryStatus = new string[] { "out of stock", "delivered" }, 133 | }; 134 | 135 | var mockResponse = CreateJobResponse(mockOutput); 136 | 137 | var client = new MyRpaClient(GetMockOrchestratorClient(mockResponse), _validSetting); 138 | var output = client.GetItemsAsync(new GetItemsInput()).GetAwaiter().GetResult(); 139 | Assert.Equal(mockOutput.ItemId.Length, output.Items.Length); 140 | Assert.Equal(mockOutput.ItemId[0], output.Items[0].ItemId); 141 | } 142 | 143 | [Fact] 144 | public void CancelOrderThrowIfNoOutput() 145 | { 146 | CancelOrderOutput mockOutput = null; 147 | var mockResponse = CreateJobResponse(mockOutput); 148 | 149 | var client = new MyRpaClient(GetMockOrchestratorClient(mockResponse), _validSetting); 150 | Assert.Throws(() => client.CancelOrderAsync(new CancelOrderInput()).GetAwaiter().GetResult()); 151 | } 152 | 153 | [Fact] 154 | public void CancelOrderSuccess() 155 | { 156 | var mockOutput = new CancelOrderOutput() 157 | { 158 | ReturnLabelLocation = "randome locaion", 159 | }; 160 | 161 | var mockResponse = CreateJobResponse(mockOutput); 162 | 163 | var client = new MyRpaClient(GetMockOrchestratorClient(mockResponse), _validSetting); 164 | var output = client.CancelOrderAsync(new CancelOrderInput()).GetAwaiter().GetResult(); 165 | Assert.Equal(mockOutput.ReturnLabelLocation, output.ReturnLabelLocation); 166 | } 167 | 168 | private IOrchestratorClient GetMockOrchestratorClient(StartJobResponse mockResponse) 169 | { 170 | var orchestratorClient = new Mock(); 171 | orchestratorClient 172 | .Setup(c => c.ExecuteJobAsync(It.IsAny())) 173 | .ReturnsAsync(mockResponse); 174 | return orchestratorClient.Object; 175 | } 176 | 177 | private StartJobResponse CreateJobResponse(T t) 178 | { 179 | return new StartJobResponse() 180 | { 181 | Id = "random", 182 | Key = "random", 183 | State = "random", 184 | OutputArguments = t == null? null : JsonConvert.SerializeObject(t), 185 | }; 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /BotFramework/Common/AdapterWithErrorHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder; 2 | using Microsoft.Bot.Builder.Integration.AspNet.Core; 3 | using Microsoft.Bot.Connector.Authentication; 4 | using Microsoft.Extensions.Logging; 5 | using System; 6 | using UiPath.ChatbotSamples.BotFramework.Resources; 7 | 8 | namespace UiPath.ChatbotSamples.BotFramework.Bot.Common 9 | { 10 | public class AdapterWithErrorHandler : BotFrameworkHttpAdapter 11 | { 12 | public AdapterWithErrorHandler(ICredentialProvider credentialProvider, ILogger logger, ConversationState conversationState = null) 13 | : base(credentialProvider) 14 | { 15 | OnTurnError = async (turnContext, exception) => 16 | { 17 | // Log any leaked exception from the application. 18 | logger.LogError($"Exception caught : {exception.Message}"); 19 | 20 | // Send a catch-all apology to the user. 21 | await turnContext.SendActivityAsync(Resource.Internal_Exception); 22 | 23 | if (conversationState != null) 24 | { 25 | try 26 | { 27 | // Delete the conversationState for the current conversation to prevent the 28 | // bot from getting stuck in a error-loop caused by being in a bad state. 29 | // ConversationState should be thought of as similar to "cookie-state" in a Web pages. 30 | await conversationState.DeleteAsync(turnContext); 31 | } 32 | catch (Exception e) 33 | { 34 | logger.LogError($"Exception caught on attempting to Delete ConversationState : {e.Message}"); 35 | } 36 | } 37 | }; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /BotFramework/Common/BotDialogSet.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder; 2 | using Microsoft.Bot.Builder.Dialogs; 3 | using Microsoft.Extensions.Logging; 4 | using System.Threading.Tasks; 5 | using UiPath.ChatbotSamples.BotFramework.Common.Models; 6 | 7 | namespace UiPath.ChatbotSamples.BotFramework.Common 8 | { 9 | public class BotDialogSet 10 | { 11 | private DialogSet _dialogs; 12 | 13 | public BotDialogSet(IStatePropertyAccessor dialogStateAccessor, IStatePropertyAccessor userProfileStateAccessor, ILoggerFactory loggerFactory) 14 | { 15 | _dialogs = new DialogSet(dialogStateAccessor); 16 | } 17 | 18 | public void AddDialogToSet(Dialog dialog) 19 | { 20 | _dialogs.Add(dialog); 21 | } 22 | 23 | public async Task CreateDialogContextAsync(ITurnContext turnContext) 24 | { 25 | return await _dialogs.CreateContextAsync(turnContext); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /BotFramework/Common/BotIntents.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Schema; 2 | using System.Collections.Generic; 3 | using UiPath.ChatbotSamples.BotFramework.Common.Models; 4 | 5 | namespace UiPath.ChatbotSamples.BotFramework.Common 6 | { 7 | public interface IBotIntents 8 | { 9 | IReadOnlyDictionary Intents { get; } 10 | 11 | double IntentTriggerThreshold { get; } 12 | 13 | Intent DefaultIntent { get; } 14 | 15 | IActivity FallbackActivity { get; } 16 | 17 | IActivity WelcomeActivity { get; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /BotFramework/Common/BotServices.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.AI.Luis; 2 | using Microsoft.Bot.Configuration; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace UiPath.ChatbotSamples.BotFramework.Common 7 | { 8 | /// 9 | /// Represents references to external services. 10 | /// 11 | /// For example, LUIS services are kept here as a singleton. This external service is configured 12 | /// using the class. 13 | /// 14 | // See https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.1 15 | // for more information regarding dependency injection 16 | // See https://www.luis.ai/home" for more information regarding language understanding using LUIS 17 | public class BotServices 18 | { 19 | /// 20 | /// Initializes a new instance of the class. 21 | /// A dictionary of named instances for usage within the bot. 22 | /// 23 | public BotServices(BotConfiguration botConfiguration) 24 | { 25 | foreach (var service in botConfiguration.Services) 26 | { 27 | switch (service.Type) 28 | { 29 | case ServiceTypes.Luis: 30 | { 31 | var luis = (LuisService)service; 32 | EnsureNotNull(luis, ServiceTypes.Luis); 33 | 34 | var app = new LuisApplication(luis.AppId, luis.AuthoringKey, luis.GetEndpoint()); 35 | var recognizer = new LuisRecognizer(app); 36 | LuisServices.Add(luis.Name, recognizer); 37 | break; 38 | } 39 | case ServiceTypes.Endpoint: 40 | { 41 | var endPoint = (EndpointService)service; 42 | EnsureNotNull(endPoint, ServiceTypes.Endpoint); 43 | EndpointServices.Add(endPoint.Name, endPoint); 44 | break; 45 | } 46 | } 47 | } 48 | 49 | if (EndpointServices.Count == 0) 50 | { 51 | throw new InvalidOperationException($"The .bot file does not contain an endpoint."); 52 | } 53 | } 54 | 55 | /// 56 | /// Gets the set of LUIS Services used. 57 | /// Given there can be multiple services used in a single bot, 58 | /// LuisServices is represented as a dictionary. This is also modeled in the 59 | /// ".bot" file since the elements are named. 60 | /// The LUIS services collection should not be modified while the bot is running. 61 | /// 62 | /// A client instance created based on configuration in the .bot file. 63 | /// 64 | /// 65 | public Dictionary LuisServices { get; } = new Dictionary(); 66 | 67 | /// 68 | /// Gets the set of endpoint services used. 69 | /// Given there can be multiple endpoints for different environments, 70 | /// EndpointServices is represented as a dictionary. 71 | /// 72 | public Dictionary EndpointServices { get; } = new Dictionary(); 73 | 74 | private void EnsureNotNull(T service, string serviceType) 75 | { 76 | if (service == null) 77 | { 78 | throw new InvalidCastException($"The {serviceType} service is not configured correctly in your .bot file."); 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /BotFramework/Common/BotSettings.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UiPath.ChatbotSamples.BotFramework.Common.Models; 3 | 4 | namespace UiPath.ChatbotSamples.BotFramework.Common 5 | { 6 | public abstract class BotSettings 7 | { 8 | public abstract string LuisConfiguration { get; } 9 | 10 | 11 | public abstract IReadOnlyDictionary Entities { get; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /BotFramework/Common/Common.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | UiPath.ChatbotSamples.BotFramework.Common 6 | UiPath.ChatbotSamples.BotFramework.Common 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /BotFramework/Common/Models/DetourIntent.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Schema; 2 | 3 | namespace UiPath.ChatbotSamples.BotFramework.Common.Models 4 | { 5 | public class DetourIntent : Intent 6 | { 7 | public IActivity[] DetourActivities { get; private set; } 8 | 9 | public DetourIntent(string name, IActivity[] detourActivities) 10 | : base(name, string.Empty, IntentType.Detour) 11 | { 12 | DetourActivities = detourActivities; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /BotFramework/Common/Models/EntityState.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace UiPath.ChatbotSamples.BotFramework.Common.Models 4 | { 5 | public class EntityState 6 | { 7 | public IDictionary Entities { get; } = new Dictionary(); 8 | 9 | public void AddOrUpdate(string key, string value) 10 | { 11 | if (Entities.ContainsKey(key)) 12 | { 13 | Entities[key] = value; 14 | } 15 | else 16 | { 17 | Entities.Add(key, value); 18 | } 19 | } 20 | 21 | public string TryGet(string key) 22 | { 23 | if (Entities.ContainsKey(key)) 24 | { 25 | return Entities[key]; 26 | } 27 | else 28 | { 29 | return string.Empty; 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /BotFramework/Common/Models/Intent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UiPath.ChatbotSamples.BotFramework.Common.Models 4 | { 5 | public class Intent 6 | { 7 | public string Name { get; private set; } 8 | 9 | public string DialogName { get; private set; } 10 | 11 | public IntentType Type { get; private set; } 12 | 13 | public Intent(string name, string dialogName, IntentType type) 14 | { 15 | Name = name; 16 | DialogName = dialogName; 17 | Type = type; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /BotFramework/Common/Models/IntentType.cs: -------------------------------------------------------------------------------- 1 | namespace UiPath.ChatbotSamples.BotFramework.Common.Models 2 | { 3 | public enum IntentType 4 | { 5 | None = 0, 6 | Normal, 7 | Termination, 8 | Detour, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /BotFramework/Common/Models/LuisEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace UiPath.ChatbotSamples.BotFramework.Common.Models 5 | { 6 | public class LuisEntity 7 | { 8 | private Func _postProcess; 9 | public LuisEntity(List patterns, Func postProcess = null) 10 | { 11 | Patterns.AddRange(patterns); 12 | _postProcess = postProcess; 13 | } 14 | 15 | // entity names in luis that map in bot entity 16 | public List Patterns { get; } = new List(); 17 | 18 | // postproces the entity value and customize it for bot use 19 | public string PostProcess(string value) 20 | { 21 | if (_postProcess != null) 22 | { 23 | return _postProcess(value); 24 | } 25 | return value; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /BotFramework/Common/Models/TerminationIntent.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Schema; 2 | 3 | namespace UiPath.ChatbotSamples.BotFramework.Common.Models 4 | { 5 | public class TerminationIntent : Intent 6 | { 7 | public IActivity[] TerminationSuccessActivities { get; private set; } 8 | 9 | public IActivity[] TerminationFailActivities { get; private set; } 10 | 11 | public TerminationIntent(string name, IActivity[] successActivities, IActivity[] failActivities) 12 | : base(name, string.Empty, IntentType.Termination) 13 | { 14 | TerminationSuccessActivities = successActivities; 15 | TerminationFailActivities = failActivities; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /BotFramework/Common/MultiTurnBot.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder; 2 | using Microsoft.Bot.Builder.AI.Luis; 3 | using Microsoft.Bot.Builder.Dialogs; 4 | using Microsoft.Bot.Schema; 5 | using Microsoft.Extensions.Logging; 6 | using System; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using UiPath.ChatbotSamples.BotFramework.Common.Models; 10 | using UiPath.ChatbotSamples.BotFramework.Utils; 11 | 12 | namespace UiPath.ChatbotSamples.BotFramework.Common 13 | { 14 | public class MultiTurnBot : IBot 15 | { 16 | protected readonly UserState _userState; 17 | protected readonly ConversationState _conversationState; 18 | protected readonly LuisRecognizer _luisRecognizer; 19 | protected readonly BotDialogSet _dialogSet; 20 | protected readonly BotSettings _botSettings; 21 | protected readonly IBotIntents _botIntents; 22 | protected readonly ILogger _logger; 23 | 24 | protected readonly IStatePropertyAccessor _entityStateAccessor; 25 | 26 | public MultiTurnBot(BotServices services, UserState userState, ConversationState conversationState, BotSettings botSettings, IBotIntents botIntents, ILoggerFactory loggerFactory) 27 | { 28 | _userState = userState.CheckNullReference(); 29 | _conversationState = conversationState.CheckNullReference(); 30 | _botSettings = botSettings.CheckNullReference(); 31 | _botIntents = botIntents.CheckNullReference(); 32 | 33 | // Verify LUIS configuration. 34 | if (!services.CheckNullReference().LuisServices.ContainsKey(botSettings.LuisConfiguration)) 35 | { 36 | throw new InvalidOperationException($"The bot configuration does not contain a service type of `luis` with the id `{botSettings.LuisConfiguration}`."); 37 | } 38 | _luisRecognizer = services.LuisServices[botSettings.LuisConfiguration]; 39 | 40 | 41 | _entityStateAccessor = _userState.CreateProperty(nameof(EntityState)); 42 | var dialogStateAccessor = _conversationState.CreateProperty(nameof(DialogState)); 43 | _dialogSet = new BotDialogSet(dialogStateAccessor, _entityStateAccessor, loggerFactory); 44 | 45 | _logger = loggerFactory.CheckNullReference().CreateLogger(); 46 | } 47 | 48 | public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken)) 49 | { 50 | var activity = turnContext.Activity; 51 | var dialogContext = await _dialogSet.CreateDialogContextAsync(turnContext); 52 | 53 | if (activity.Type == ActivityTypes.Message) 54 | { 55 | 56 | var luisResults = await _luisRecognizer.RecognizeAsync(dialogContext.Context, cancellationToken); 57 | var topIntent = GetTopIntent(luisResults); 58 | 59 | await UpdateEntityState(luisResults, turnContext); 60 | 61 | // Handle conversation interrupts first. 62 | var interrupted = await IsTurnInterruptedAsync(dialogContext, topIntent); 63 | if (interrupted) 64 | { 65 | await ExitTurnAsync(turnContext); 66 | return; 67 | } 68 | 69 | var dialogResult = await dialogContext.ContinueDialogAsync(cancellationToken); 70 | 71 | if (!dialogContext.Context.Responded) 72 | { 73 | // examine results from active dialog 74 | switch (dialogResult.Status) 75 | { 76 | case DialogTurnStatus.Empty: 77 | if (!string.IsNullOrEmpty(topIntent.DialogName)) 78 | { 79 | await dialogContext.BeginDialogAsync(topIntent.DialogName); 80 | } 81 | else 82 | { 83 | await dialogContext.Context.SendActivityAsync(_botIntents.FallbackActivity); 84 | } 85 | break; 86 | 87 | case DialogTurnStatus.Waiting: 88 | // The active dialog is waiting for a response from the user, so do nothing. 89 | break; 90 | 91 | case DialogTurnStatus.Complete: 92 | await dialogContext.EndDialogAsync(); 93 | break; 94 | 95 | default: 96 | await dialogContext.CancelAllDialogsAsync(); 97 | break; 98 | } 99 | } 100 | } 101 | else if (activity.Type == ActivityTypes.ConversationUpdate) 102 | { 103 | await SendWelcomeMessageAsync(activity, dialogContext); 104 | } 105 | 106 | await ExitTurnAsync(turnContext); 107 | } 108 | 109 | protected async Task SendWelcomeMessageAsync(Activity activity, DialogContext dialogContext) 110 | { 111 | if (activity.MembersAdded != null) 112 | { 113 | // Iterate over all new members added to the conversation. 114 | foreach (var member in activity.MembersAdded) 115 | { 116 | // Greet anyone that was not the target (recipient) of this message. 117 | if (member.Id != activity.Recipient.Id) 118 | { 119 | await dialogContext.Context.SendActivityAsync(_botIntents.WelcomeActivity); 120 | } 121 | } 122 | } 123 | } 124 | 125 | protected async Task ExitTurnAsync(ITurnContext turnContext) 126 | { 127 | await _conversationState.SaveChangesAsync(turnContext); 128 | await _userState.SaveChangesAsync(turnContext); 129 | } 130 | 131 | protected Intent GetTopIntent(RecognizerResult luisResult) 132 | { 133 | if (luisResult == null) 134 | { 135 | return _botIntents.DefaultIntent; 136 | } 137 | 138 | var topScoringIntent = luisResult.GetTopScoringIntent(); 139 | if (topScoringIntent.score >= _botIntents.IntentTriggerThreshold) 140 | { 141 | var topIntent = topScoringIntent.intent; 142 | if (_botIntents.Intents.ContainsKey(topIntent)) 143 | { 144 | return _botIntents.Intents[topIntent]; 145 | } 146 | else 147 | { 148 | _logger.LogError($"Fail to find intent {topIntent} in bot definition."); 149 | } 150 | } 151 | return _botIntents.DefaultIntent; 152 | } 153 | 154 | protected async Task IsTurnInterruptedAsync(DialogContext dialogContext, Intent topIntent) 155 | { 156 | // See if there are any conversation interrupts we need to handle. 157 | if (topIntent.Type == IntentType.Termination) 158 | { 159 | return await TerminateIntentAsync(dialogContext, topIntent as TerminationIntent); 160 | } 161 | 162 | if (topIntent.Type == IntentType.Detour) 163 | { 164 | return await DetourIntentAsync(dialogContext, topIntent as DetourIntent); 165 | } 166 | 167 | return false; 168 | } 169 | 170 | protected async Task TerminateIntentAsync(DialogContext dialogContext, TerminationIntent intent) 171 | { 172 | IActivity[] activities = intent.TerminationFailActivities; 173 | if (dialogContext.ActiveDialog != null) 174 | { 175 | await dialogContext.CancelAllDialogsAsync(); 176 | activities = intent.TerminationSuccessActivities; 177 | } 178 | 179 | foreach (var activity in activities) 180 | { 181 | await dialogContext.Context.SendActivityAsync(activity); 182 | } 183 | 184 | return true; 185 | } 186 | 187 | protected async Task DetourIntentAsync(DialogContext dialogContext, DetourIntent intent) 188 | { 189 | IActivity[] activities = intent.DetourActivities; 190 | 191 | foreach (var activity in activities) 192 | { 193 | await dialogContext.Context.SendActivityAsync(activity); 194 | } 195 | 196 | if (dialogContext.ActiveDialog != null) 197 | { 198 | await dialogContext.RepromptDialogAsync(); 199 | } 200 | 201 | return true; 202 | } 203 | 204 | protected async Task UpdateEntityState(RecognizerResult luisResult, ITurnContext turnContext) 205 | { 206 | try 207 | { 208 | if (luisResult.Entities != null && luisResult.Entities.HasValues) 209 | { 210 | var entityState = await _entityStateAccessor.GetAsync(turnContext, () => new EntityState()); 211 | var entities = luisResult.Entities; 212 | 213 | foreach (var entityMapping in _botSettings.Entities) 214 | { 215 | var luisEntity = entityMapping.Value; 216 | foreach (var entity in luisEntity.Patterns) 217 | { 218 | if (entities[entity] != null) 219 | { 220 | entityState.AddOrUpdate(entityMapping.Key, luisEntity.PostProcess((string)entities[entity][0])); 221 | } 222 | } 223 | } 224 | 225 | await _entityStateAccessor.SetAsync(turnContext, entityState); 226 | } 227 | } 228 | catch (Exception e) 229 | { 230 | _logger.LogError($"{nameof(UpdateEntityState)} failed with exception {e.ToString()}"); 231 | } 232 | } 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /BotFramework/Data/Luis/.luisrc: -------------------------------------------------------------------------------- 1 | { 2 | "authoringKey": "231bce45531f463bb77c8217e8534661", 3 | "versionId": "0.1", 4 | "region": "westus" 5 | } 6 | -------------------------------------------------------------------------------- /BotFramework/Data/Luis/Sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "intents": [ 3 | { 4 | "name": "Greeting" 5 | }, 6 | { 7 | "name": "Help" 8 | }, 9 | { 10 | "name": "Cancel" 11 | }, 12 | { 13 | "name": "Bye" 14 | }, 15 | { 16 | "name": "DelayedShipment" 17 | }, 18 | { 19 | "name": "ReplaceItem" 20 | }, 21 | { 22 | "name": "CustomerId" 23 | } 24 | ], 25 | "entities": [ 26 | { 27 | "name": "Id", 28 | "roles": [] 29 | } 30 | ], 31 | "composites": [], 32 | "closedLists": [], 33 | "regex_entities": [], 34 | "model_features": [], 35 | "regex_features": [], 36 | "utterances": [ 37 | { 38 | "text": "good evening", 39 | "intent": "Greeting", 40 | "entities": [] 41 | }, 42 | { 43 | "text": "good morning", 44 | "intent": "Greeting", 45 | "entities": [] 46 | }, 47 | { 48 | "text": "hello", 49 | "intent": "Greeting", 50 | "entities": [] 51 | }, 52 | { 53 | "text": "hello bot", 54 | "intent": "Greeting", 55 | "entities": [] 56 | }, 57 | { 58 | "text": "hey", 59 | "intent": "Greeting", 60 | "entities": [] 61 | }, 62 | { 63 | "text": "hey there", 64 | "intent": "Greeting", 65 | "entities": [] 66 | }, 67 | { 68 | "text": "hi", 69 | "intent": "Greeting", 70 | "entities": [] 71 | }, 72 | { 73 | "text": "hi bot", 74 | "intent": "Greeting", 75 | "entities": [] 76 | }, 77 | { 78 | "text": "hiya", 79 | "intent": "Greeting", 80 | "entities": [] 81 | }, 82 | { 83 | "text": "how are you", 84 | "intent": "Greeting", 85 | "entities": [] 86 | }, 87 | { 88 | "text": "how are you today", 89 | "intent": "Greeting", 90 | "entities": [] 91 | }, 92 | { 93 | "text": "what's your name", 94 | "intent": "Greeting", 95 | "entities": [] 96 | }, 97 | { 98 | "text": "who am i speaking to", 99 | "intent": "Greeting", 100 | "entities": [] 101 | }, 102 | { 103 | "text": "who am i speaking with", 104 | "intent": "Greeting", 105 | "entities": [] 106 | }, 107 | { 108 | "text": "who are you", 109 | "intent": "Greeting", 110 | "entities": [] 111 | }, 112 | { 113 | "text": "yo", 114 | "intent": "Greeting", 115 | "entities": [] 116 | }, 117 | { 118 | "text": "I need help", 119 | "intent": "Help", 120 | "entities": [] 121 | }, 122 | { 123 | "text": "please help", 124 | "intent": "Help", 125 | "entities": [] 126 | }, 127 | { 128 | "text": "what can you do", 129 | "intent": "Help", 130 | "entities": [] 131 | }, 132 | { 133 | "text": "?", 134 | "intent": "Help", 135 | "entities": [] 136 | }, 137 | { 138 | "text": "I'm lost", 139 | "intent": "Help", 140 | "entities": [] 141 | }, 142 | { 143 | "text": "lost", 144 | "intent": "Help", 145 | "entities": [] 146 | }, 147 | { 148 | "text": "confused", 149 | "intent": "Help", 150 | "entities": [] 151 | }, 152 | { 153 | "text": "frustrated", 154 | "intent": "Help", 155 | "entities": [] 156 | }, 157 | { 158 | "text": "what are your capabilities", 159 | "intent": "Help", 160 | "entities": [] 161 | }, 162 | { 163 | "text": "help me", 164 | "intent": "Help", 165 | "entities": [] 166 | }, 167 | { 168 | "text": "help", 169 | "intent": "Help", 170 | "entities": [] 171 | }, 172 | { 173 | "text": "how do I interact with you", 174 | "intent": "Help", 175 | "entities": [] 176 | }, 177 | { 178 | "text": "help please", 179 | "intent": "Help", 180 | "entities": [] 181 | }, 182 | { 183 | "text": "i'm stuck", 184 | "intent": "Help", 185 | "entities": [] 186 | }, 187 | { 188 | "text": "i don't understand", 189 | "intent": "Help", 190 | "entities": [] 191 | }, 192 | { 193 | "text": "what can I say", 194 | "intent": "Help", 195 | "entities": [] 196 | }, 197 | { 198 | "text": "what can you help me with", 199 | "intent": "Help", 200 | "entities": [] 201 | }, 202 | { 203 | "text": "what can you do", 204 | "intent": "Help", 205 | "entities": [] 206 | }, 207 | { 208 | "text": "what do i do now", 209 | "intent": "Help", 210 | "entities": [] 211 | }, 212 | { 213 | "text": "why doesn't this work", 214 | "intent": "Help", 215 | "entities": [] 216 | }, 217 | { 218 | "text": "can you help me", 219 | "intent": "Help", 220 | "entities": [] 221 | }, 222 | { 223 | "text": "cancel", 224 | "intent": "Cancel", 225 | "entities": [] 226 | }, 227 | { 228 | "text": "stop what you are doing", 229 | "intent": "Cancel", 230 | "entities": [] 231 | }, 232 | { 233 | "text": "please cancel", 234 | "intent": "Cancel", 235 | "entities": [] 236 | }, 237 | { 238 | "text": "I don't want to continue", 239 | "intent": "Cancel", 240 | "entities": [] 241 | }, 242 | { 243 | "text": "quit", 244 | "intent": "Cancel", 245 | "entities": [] 246 | }, 247 | { 248 | "text": "stop", 249 | "intent": "Cancel", 250 | "entities": [] 251 | }, 252 | { 253 | "text": "abort", 254 | "intent": "Cancel", 255 | "entities": [] 256 | }, 257 | { 258 | "text": "don't do it", 259 | "intent": "Cancel", 260 | "entities": [] 261 | }, 262 | { 263 | "text": "don't do that", 264 | "intent": "Cancel", 265 | "entities": [] 266 | }, 267 | { 268 | "text": "i would like to cancel", 269 | "intent": "Cancel", 270 | "entities": [] 271 | }, 272 | { 273 | "text": "i will not give you my name", 274 | "intent": "Cancel", 275 | "entities": [] 276 | }, 277 | { 278 | "text": "i won't give you my name", 279 | "intent": "Cancel", 280 | "entities": [] 281 | }, 282 | { 283 | "text": "no way, I won't give you my location", 284 | "intent": "Cancel", 285 | "entities": [] 286 | }, 287 | { 288 | "text": "I'm not going to tell you where i live", 289 | "intent": "Cancel", 290 | "entities": [] 291 | }, 292 | { 293 | "text": "I will not give you my location or name", 294 | "intent": "Cancel", 295 | "entities": [] 296 | }, 297 | { 298 | "text": "no way", 299 | "intent": "Cancel", 300 | "entities": [] 301 | }, 302 | { 303 | "text": "please stop", 304 | "intent": "Cancel", 305 | "entities": [] 306 | }, 307 | { 308 | "text": "I'm bailing", 309 | "intent": "Cancel", 310 | "entities": [] 311 | }, 312 | { 313 | "text": "Bye", 314 | "intent": "Bye", 315 | "entities": [] 316 | }, 317 | { 318 | "text": "Talk to agent", 319 | "intent": "Bye", 320 | "entities": [] 321 | }, 322 | { 323 | "text": "I want to talk to human", 324 | "intent": "Bye", 325 | "entities": [] 326 | }, 327 | { 328 | "text": "no, thank you", 329 | "intent": "Bye", 330 | "entities": [] 331 | }, 332 | { 333 | "text": "that's all I need for today", 334 | "intent": "Bye", 335 | "entities": [] 336 | }, 337 | { 338 | "text": "My order is delayed", 339 | "intent": "DelayedShipment", 340 | "entities": [] 341 | }, 342 | { 343 | "text": "shipment is delayed", 344 | "intent": "DelayedShipment", 345 | "entities": [] 346 | }, 347 | { 348 | "text": "why my order is not shipped", 349 | "intent": "DelayedShipment", 350 | "entities": [] 351 | }, 352 | { 353 | "text": "The headphones shipped to me are broken", 354 | "intent": "ReplaceItem", 355 | "entities": [] 356 | }, 357 | { 358 | "text": "The item is broken", 359 | "intent": "ReplaceItem", 360 | "entities": [] 361 | }, 362 | { 363 | "text": "The item is not working", 364 | "intent": "ReplaceItem", 365 | "entities": [] 366 | }, 367 | { 368 | "text": "The wireless speakers are not working", 369 | "intent": "ReplaceItem", 370 | "entities": [] 371 | }, 372 | { 373 | "text": "I want to replace my item", 374 | "intent": "ReplaceItem", 375 | "entities": [] 376 | }, 377 | { 378 | "text": "I want to replace", 379 | "intent": "ReplaceItem", 380 | "entities": [] 381 | }, 382 | { 383 | "text": "I want to return my item", 384 | "intent": "ReplaceItem", 385 | "entities": [] 386 | }, 387 | { 388 | "text": "I want to return the headphones", 389 | "intent": "ReplaceItem", 390 | "entities": [] 391 | }, 392 | { 393 | "text": "My customer Id is 12345", 394 | "intent": "CustomerId", 395 | "entities": [ 396 | { 397 | "entity": "Id", 398 | "startPos": 18, 399 | "endPos": 22 400 | } 401 | ] 402 | }, 403 | { 404 | "text": "It is 234", 405 | "intent": "CustomerId", 406 | "entities": [ 407 | { 408 | "entity": "Id", 409 | "startPos": 6, 410 | "endPos": 8 411 | } 412 | ] 413 | }, 414 | { 415 | "text": "My Id is 4678", 416 | "intent": "CustomerId", 417 | "entities": [ 418 | { 419 | "entity": "Id", 420 | "startPos": 9, 421 | "endPos": 12 422 | } 423 | ] 424 | }, 425 | { 426 | "text": "Account id 890", 427 | "intent": "CustomerId", 428 | "entities": [ 429 | { 430 | "entity": "Id", 431 | "startPos": 11, 432 | "endPos": 13 433 | } 434 | ] 435 | } 436 | ], 437 | "patterns": [], 438 | "patternAnyEntities": [], 439 | "prebuiltEntities": [], 440 | "luis_schema_version": "3.0.0", 441 | "versionId": "0.1", 442 | "name": "SampleBot", 443 | "desc": "", 444 | "culture": "en-us" 445 | } -------------------------------------------------------------------------------- /BotFramework/Data/Luis/Sample.lu: -------------------------------------------------------------------------------- 1 | # Greeting 2 | - good evening 3 | - good morning 4 | - hello 5 | - hello bot 6 | - hey 7 | - hey there 8 | - hi 9 | - hi bot 10 | - hiya 11 | - how are you 12 | - how are you today 13 | - what's your name 14 | - who am i speaking to 15 | - who am i speaking with 16 | - who are you 17 | - yo 18 | 19 | # Help 20 | - I need help 21 | - please help 22 | - what can you do 23 | - ? 24 | - I'm lost 25 | - lost 26 | - confused 27 | - frustrated 28 | - what are your capabilities 29 | - help me 30 | - help 31 | - how do I interact with you 32 | - help please 33 | - i'm stuck 34 | - i don't understand 35 | - what can I say 36 | - what can you help me with 37 | - what can you do 38 | - what do i do now 39 | - why doesn't this work 40 | - can you help me 41 | 42 | # Cancel 43 | - cancel 44 | - stop what you are doing 45 | - please cancel 46 | - I don't want to continue 47 | - quit 48 | - stop 49 | - abort 50 | - don't do it 51 | - don't do that 52 | - i would like to cancel 53 | - i will not give you my name 54 | - i won't give you my name 55 | - no way, I won't give you my location 56 | - I'm not going to tell you where i live 57 | - I will not give you my location or name 58 | - no way 59 | - please stop 60 | - I'm bailing 61 | 62 | # Bye 63 | - Bye 64 | - Talk to agent 65 | - I want to talk to human 66 | - no, thank you 67 | - that's all I need for today 68 | 69 | # DelayedShipment 70 | - My order is delayed 71 | - shipment is delayed 72 | - why my order is not shipped 73 | 74 | # ReplaceItem 75 | - The headphones shipped to me are broken 76 | - The item is broken 77 | - The item is not working 78 | - The wireless speakers are not working 79 | - I want to replace my item 80 | - I want to replace 81 | - I want to return my item 82 | - I want to return the headphones 83 | 84 | # CustomerId 85 | - My customer Id is {Id=12345} 86 | - It is {Id=234} 87 | - My Id is {Id=4678} 88 | - Account id {Id=890} 89 | -------------------------------------------------------------------------------- /BotFramework/Data/Packages/CancelOrder.1.0.1-alpha.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UiPath/Chatbot-Samples/6e4fb400456c725070e8d3691ba17827baaac7aa/BotFramework/Data/Packages/CancelOrder.1.0.1-alpha.nupkg -------------------------------------------------------------------------------- /BotFramework/Data/Packages/CreatePurchaseOrder.1.0.1-alpha.1.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UiPath/Chatbot-Samples/6e4fb400456c725070e8d3691ba17827baaac7aa/BotFramework/Data/Packages/CreatePurchaseOrder.1.0.1-alpha.1.nupkg -------------------------------------------------------------------------------- /BotFramework/Data/Packages/CreateSalesOrder.1.0.1-alpha.1.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UiPath/Chatbot-Samples/6e4fb400456c725070e8d3691ba17827baaac7aa/BotFramework/Data/Packages/CreateSalesOrder.1.0.1-alpha.1.nupkg -------------------------------------------------------------------------------- /BotFramework/Data/Packages/GetItems.1.0.1-alpha.3.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UiPath/Chatbot-Samples/6e4fb400456c725070e8d3691ba17827baaac7aa/BotFramework/Data/Packages/GetItems.1.0.1-alpha.3.nupkg -------------------------------------------------------------------------------- /BotFramework/Dialogs/Dialogs.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | UiPath.ChatbotSamples.BotFramework.Dialogs 6 | UiPath.ChatbotSamples.BotFramework.Dialogs 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /BotFramework/Dialogs/MyBot.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder; 2 | using Microsoft.Extensions.Logging; 3 | using UiPath.ChatbotSamples.BotFramework.Actions; 4 | using UiPath.ChatbotSamples.BotFramework.Common; 5 | using UiPath.ChatbotSamples.BotFramework.Dialogs.MyDialog; 6 | 7 | namespace UiPath.ChatbotSamples.BotFramework.Dialogs 8 | { 9 | public class MyBot : MultiTurnBot 10 | { 11 | public MyBot(BotServices services, UserState userState, ConversationState conversationState, IMyRpaClient rpaClient, ILoggerFactory loggerFactory) 12 | :base(services, userState, conversationState, new MyBotSettings(), new MyIntents(), loggerFactory) 13 | { 14 | // Must make sure all the dialogs add to dialog set here. 15 | _dialogSet.AddDialogToSet(new GreetingDialog()); 16 | _dialogSet.AddDialogToSet(new GetCustomerProfileDialog(_entityStateAccessor)); 17 | _dialogSet.AddDialogToSet(new DelayedShipmentDialog(rpaClient, _entityStateAccessor)); 18 | _dialogSet.AddDialogToSet(new ReplaceItemDialog(rpaClient, _entityStateAccessor)); 19 | _dialogSet.AddDialogToSet(new CannotFindOrderDialog()); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /BotFramework/Dialogs/MyBotSettings.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UiPath.ChatbotSamples.BotFramework.Common; 3 | using UiPath.ChatbotSamples.BotFramework.Common.Models; 4 | 5 | namespace UiPath.ChatbotSamples.BotFramework.Dialogs 6 | { 7 | public sealed class MyBotSettings: BotSettings 8 | { 9 | /// 10 | /// Key in the bot config (.bot file) for the LUIS instance. 11 | /// In the .bot file, multiple instances of LUIS can be configured. 12 | /// 13 | public override string LuisConfiguration { 14 | get { 15 | return "Sample"; 16 | } 17 | } 18 | 19 | public override IReadOnlyDictionary Entities { get; } 20 | = new Dictionary 21 | { 22 | { "CustomerId", new LuisEntity(new List(){ "Id" }, NomalizeCustomerId) }, 23 | }; 24 | 25 | private static string NomalizeCustomerId(string value) 26 | { 27 | if (!string.IsNullOrEmpty(value)) 28 | { 29 | value = value.ToUpperInvariant(); 30 | } 31 | return value; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /BotFramework/Dialogs/MyDialog/Base/DialogBase.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | 3 | namespace UiPath.ChatbotSamples.BotFramework.Dialogs.MyDialog.Base 4 | { 5 | public class DialogBase: ComponentDialog 6 | { 7 | private readonly string _dialogId; 8 | 9 | protected DialogBase(string dialogId) 10 | : base(dialogId) 11 | { 12 | _dialogId = dialogId; 13 | } 14 | 15 | protected string GetWaterFallDialogId(string id) 16 | { 17 | return $"WaterFall{id}"; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /BotFramework/Dialogs/MyDialog/Base/StatefulDialogBase.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder; 2 | using Microsoft.Bot.Builder.Dialogs; 3 | using System.Threading.Tasks; 4 | using UiPath.ChatbotSamples.BotFramework.Common.Models; 5 | 6 | namespace UiPath.ChatbotSamples.BotFramework.Dialogs.MyDialog.Base 7 | { 8 | public class StatefulDialogBase: DialogBase 9 | { 10 | private readonly IStatePropertyAccessor _entityStateAccessor; 11 | 12 | public StatefulDialogBase(string dialogId, IStatePropertyAccessor entityStateAccessor) 13 | : base(dialogId) 14 | { 15 | _entityStateAccessor = entityStateAccessor; 16 | } 17 | 18 | protected async Task GetEntityStateAsync(WaterfallStepContext stepContext) 19 | { 20 | var entityState = await _entityStateAccessor.GetAsync(stepContext.Context, () => null); 21 | if (entityState == null) 22 | { 23 | entityState = new EntityState(); 24 | await _entityStateAccessor.SetAsync(stepContext.Context, entityState); 25 | } 26 | return entityState; 27 | } 28 | 29 | protected async Task TryGetEntityValueAsync(WaterfallStepContext stepContext, string key) 30 | { 31 | var entityState = await GetEntityStateAsync(stepContext); 32 | return entityState.TryGet(key); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /BotFramework/Dialogs/MyDialog/CannotFindOrderDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using UiPath.ChatbotSamples.BotFramework.Dialogs.MyDialog.Base; 5 | using UiPath.ChatbotSamples.BotFramework.Resources; 6 | using UiPath.ChatbotSamples.BotFramework.Utils; 7 | 8 | namespace UiPath.ChatbotSamples.BotFramework.Dialogs.MyDialog 9 | { 10 | public class CannotFindOrderDialog : DialogBase 11 | { 12 | public CannotFindOrderDialog() 13 | : base(nameof(CannotFindOrderDialog)) 14 | { 15 | var waterfallSteps = new WaterfallStep[] 16 | { 17 | NoUndeliveredOrderStepAsync, 18 | ProcessTalkToAgentStepAsync, 19 | }; 20 | AddDialog(new WaterfallDialog(GetWaterFallDialogId(nameof(CannotFindOrderDialog)), waterfallSteps)); 21 | AddDialog(new ConfirmPrompt(nameof(NoUndeliveredOrderStepAsync))); 22 | AddDialog(new TextPrompt(nameof(ProcessTalkToAgentStepAsync))); 23 | } 24 | 25 | private async Task NoUndeliveredOrderStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) 26 | { 27 | return await stepContext.PromptAsync(nameof(NoUndeliveredOrderStepAsync), Utils.CreateMessagePrompt(Resource.No_Undelivered_Order, Utils.GetYesNoOptions())); 28 | } 29 | 30 | private async Task ProcessTalkToAgentStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) 31 | { 32 | var message = stepContext.Context.Activity.AsMessageActivity().Text; 33 | if (message.OrdinalEquals(Resource.Yes)) 34 | { 35 | await stepContext.Context.SendActivityAsync(Resource.Talk_To_Agent); 36 | } 37 | else 38 | { 39 | await stepContext.Context.SendActivityAsync(Resource.Anything_Else); 40 | } 41 | await stepContext.EndDialogAsync(); 42 | return await stepContext.Parent.CancelAllDialogsAsync(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /BotFramework/Dialogs/MyDialog/DelayedShipmentDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder; 2 | using Microsoft.Bot.Builder.Dialogs; 3 | using System; 4 | using System.Linq; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using UiPath.ChatbotSamples.BotFramework.Actions; 8 | using UiPath.ChatbotSamples.BotFramework.Actions.Models; 9 | using UiPath.ChatbotSamples.BotFramework.Common.Models; 10 | using UiPath.ChatbotSamples.BotFramework.Dialogs.MyDialog.Base; 11 | using UiPath.ChatbotSamples.BotFramework.Resources; 12 | using UiPath.ChatbotSamples.BotFramework.Utils; 13 | 14 | namespace UiPath.ChatbotSamples.BotFramework.Dialogs.MyDialog 15 | { 16 | public class DelayedShipmentDialog : StatefulDialogBase 17 | { 18 | private readonly IMyRpaClient _myRpaClient; 19 | private readonly IStatePropertyAccessor _entityStateAccessor; 20 | 21 | public DelayedShipmentDialog(IMyRpaClient myRpaClient, IStatePropertyAccessor entityStateAccessor) 22 | : base(nameof(DelayedShipmentDialog), entityStateAccessor) 23 | { 24 | var waterfallSteps = new WaterfallStep[] 25 | { 26 | GetCustomerIdStepAsync, 27 | ListItemsStepAsync, 28 | ProcessItemSelectionStepAsync, 29 | PlaceOrderStepAsync, 30 | }; 31 | AddDialog(new WaterfallDialog(GetWaterFallDialogId(nameof(DelayedShipmentDialog)), waterfallSteps)); 32 | AddDialog(new TextPrompt(nameof(GetCustomerIdStepAsync))); 33 | AddDialog(new ChoicePrompt(nameof(ListItemsStepAsync))); 34 | AddDialog(new ChoicePrompt(nameof(ProcessItemSelectionStepAsync))); 35 | AddDialog(new TextPrompt(nameof(PlaceOrderStepAsync))); 36 | 37 | _myRpaClient = myRpaClient; 38 | _entityStateAccessor = entityStateAccessor; 39 | } 40 | 41 | private async Task GetCustomerIdStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) 42 | { 43 | return await stepContext.BeginDialogAsync(nameof(GetCustomerProfileDialog)); 44 | } 45 | 46 | private async Task ListItemsStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) 47 | { 48 | await stepContext.Context.SendActivityAsync(Resource.Working_On); 49 | var customerId = await TryGetEntityValueAsync(stepContext, "CustomerId"); 50 | stepContext.Values["CustomerId"] = customerId; 51 | 52 | var result = await _myRpaClient.GetItemsAsync(new GetItemsInput() { CustomerId = customerId }); 53 | 54 | if (result?.Items == null || result.Items.Length == 0) 55 | { 56 | return await stepContext.BeginDialogAsync(nameof(CannotFindOrderDialog)); 57 | } 58 | else 59 | { 60 | stepContext.Values["Items"] = result; 61 | var unDelivered = result.Items.Where(i => i.DeliveryDate >= DateTime.Today).Select(i => i.ItemName).ToList(); 62 | unDelivered.Add(Resource.None_Of_Above); 63 | return await stepContext.PromptAsync(nameof(ListItemsStepAsync), Utils.CreateMessagePrompt(Resource.Which_Undelivered, unDelivered)); 64 | } 65 | } 66 | 67 | private async Task ProcessItemSelectionStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) 68 | { 69 | var message = stepContext.Context.Activity.AsMessageActivity().Text; 70 | if (message.OrdinalEquals(Resource.None_Of_Above)) 71 | { 72 | return await stepContext.BeginDialogAsync(nameof(CannotFindOrderDialog)); 73 | } 74 | else 75 | { 76 | var matchedItems = ((GetItemsOutput)stepContext.Values["Items"]).Items.Where(i => i.ItemName.OrdinalEquals(message)).ToList(); 77 | if (matchedItems.Count == 0) 78 | { 79 | return await stepContext.BeginDialogAsync(nameof(CannotFindOrderDialog)); 80 | } 81 | 82 | stepContext.Values["SelectedItem"] = matchedItems[0]; 83 | 84 | return await stepContext.PromptAsync(nameof(ProcessItemSelectionStepAsync), Utils.CreateMessagePrompt(string.Format(Resource.Deliver_Status, matchedItems[0].DeliveryStatus), Utils.GetYesNoOptions())); 85 | } 86 | } 87 | 88 | private async Task PlaceOrderStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) 89 | { 90 | 91 | var message = stepContext.Context.Activity.AsMessageActivity().Text; 92 | if (message.OrdinalEquals(Resource.Yes)) 93 | { 94 | await stepContext.Context.SendActivityAsync(Resource.Working_On); 95 | var item = (Item)stepContext.Values["SelectedItem"]; 96 | var purchaseOrderOutput = await _myRpaClient.CreatePurchaseOrderAsync(new CreatePurchaseOrderInput() { ItemId = item.ItemId }); 97 | 98 | var cancelInput = new CancelOrderInput() 99 | { 100 | OrderId = item.OrderId, 101 | CancelReason = "Delayed shipment", 102 | }; 103 | var cancelOrderOutput = await _myRpaClient.CancelOrderAsync(cancelInput); 104 | var orderCancelMessage = string.Format(Resource.Order_Cancelled, item.OrderId, cancelOrderOutput.ReturnLabelLocation); 105 | await stepContext.Context.SendActivityAsync(orderCancelMessage); 106 | 107 | var salesOrderInput = new CreateSalesOrderInput() 108 | { 109 | CustomerId = (string)stepContext.Values["CustomerId"], 110 | ItemId = item.ItemId, 111 | Quantity = item.Quantity, 112 | }; 113 | var createSaledOrderOuput = await _myRpaClient.CreateSalesOrderAsync(salesOrderInput); 114 | var orderCreatedMessage = string.Format(Resource.Order_Created, createSaledOrderOuput.OrderId, createSaledOrderOuput.DeliveryDate.ToShortDateString()); 115 | await stepContext.Context.SendActivityAsync(orderCreatedMessage); 116 | } 117 | await stepContext.Context.SendActivityAsync(Resource.Anything_Else); 118 | return await stepContext.EndDialogAsync(); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /BotFramework/Dialogs/MyDialog/GetCustomerProfileDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder; 2 | using Microsoft.Bot.Builder.Dialogs; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using UiPath.ChatbotSamples.BotFramework.Common.Models; 6 | using UiPath.ChatbotSamples.BotFramework.Dialogs.MyDialog.Base; 7 | using UiPath.ChatbotSamples.BotFramework.Resources; 8 | 9 | namespace UiPath.ChatbotSamples.BotFramework.Dialogs.MyDialog 10 | { 11 | public class GetCustomerProfileDialog: StatefulDialogBase 12 | { 13 | private readonly IStatePropertyAccessor _entityStateAccessor; 14 | 15 | public GetCustomerProfileDialog(IStatePropertyAccessor entityStateAccessor) : base(nameof(GetCustomerProfileDialog), entityStateAccessor) 16 | { 17 | var waterfallSteps = new WaterfallStep[] 18 | { 19 | PromptForCustomerIdStepAsync, 20 | ProcessCustomerIdStepAsync, 21 | }; 22 | AddDialog(new WaterfallDialog(GetWaterFallDialogId(nameof(GetCustomerProfileDialog)), waterfallSteps)); 23 | AddDialog(new TextPrompt(nameof(PromptForCustomerIdStepAsync))); 24 | AddDialog(new TextPrompt(nameof(ProcessCustomerIdStepAsync))); 25 | 26 | _entityStateAccessor = entityStateAccessor; 27 | } 28 | private async Task PromptForCustomerIdStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) 29 | { 30 | var customerId = await GetCustomerIdAsync(stepContext); 31 | if (string.IsNullOrEmpty(customerId)) 32 | { 33 | return await stepContext.PromptAsync(nameof(PromptForCustomerIdStepAsync), Utils.CreateMessagePrompt(Resource.Ask_Customer_Id)); 34 | } 35 | else 36 | { 37 | return await stepContext.NextAsync(); 38 | } 39 | } 40 | 41 | private async Task ProcessCustomerIdStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) 42 | { 43 | var customerId = await GetCustomerIdAsync(stepContext); 44 | if (string.IsNullOrEmpty(customerId)) 45 | { 46 | // loop until get customer id 47 | return await stepContext.ReplaceDialogAsync(nameof(GetCustomerProfileDialog), cancellationToken); 48 | } 49 | return await stepContext.Parent.ContinueDialogAsync(); 50 | } 51 | 52 | private async Task GetCustomerIdAsync(WaterfallStepContext stepContext) 53 | { 54 | return await TryGetEntityValueAsync(stepContext, "CustomerId"); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /BotFramework/Dialogs/MyDialog/GreetingDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using UiPath.ChatbotSamples.BotFramework.Dialogs.MyDialog.Base; 5 | using UiPath.ChatbotSamples.BotFramework.Resources; 6 | 7 | namespace UiPath.ChatbotSamples.BotFramework.Dialogs.MyDialog 8 | { 9 | public class GreetingDialog : DialogBase 10 | { 11 | public GreetingDialog() 12 | : base(nameof(GreetingDialog)) 13 | { 14 | // Add control flow dialogs 15 | var waterfallSteps = new WaterfallStep[] 16 | { 17 | ShowHelpStepAsync, 18 | }; 19 | AddDialog(new WaterfallDialog(GetWaterFallDialogId(nameof(GreetingDialog)), waterfallSteps)); 20 | AddDialog(new TextPrompt(nameof(ShowHelpStepAsync))); 21 | } 22 | 23 | private async Task ShowHelpStepAsync( 24 | WaterfallStepContext stepContext, 25 | CancellationToken cancellationToken) 26 | { 27 | await stepContext.Context.SendActivityAsync(Resource.Help); 28 | return await stepContext.EndDialogAsync(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /BotFramework/Dialogs/MyDialog/ReplaceItemDialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder; 2 | using Microsoft.Bot.Builder.Dialogs; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using UiPath.ChatbotSamples.BotFramework.Actions; 9 | using UiPath.ChatbotSamples.BotFramework.Actions.Models; 10 | using UiPath.ChatbotSamples.BotFramework.Common.Models; 11 | using UiPath.ChatbotSamples.BotFramework.Dialogs.MyDialog.Base; 12 | using UiPath.ChatbotSamples.BotFramework.Resources; 13 | using UiPath.ChatbotSamples.BotFramework.Utils; 14 | 15 | namespace UiPath.ChatbotSamples.BotFramework.Dialogs.MyDialog 16 | { 17 | public class ReplaceItemDialog : StatefulDialogBase 18 | { 19 | private readonly IMyRpaClient _myRpaClient; 20 | private readonly IStatePropertyAccessor _entityStateAccessor; 21 | private const string c_customerId = "CustomerId"; 22 | 23 | public ReplaceItemDialog(IMyRpaClient myRPAClient, IStatePropertyAccessor entityStateAccessor) : base(nameof(ReplaceItemDialog), entityStateAccessor) 24 | { 25 | var waterfallSteps = new WaterfallStep[] 26 | { 27 | GetCustomerIdStepAsync, 28 | ListItemsStepAsync, 29 | ProcessItemSelectionStepAsync, 30 | AskReplacementStepAsync, 31 | PlaceOrderStepAsync, 32 | }; 33 | AddDialog(new WaterfallDialog(GetWaterFallDialogId(nameof(ReplaceItemDialog)), waterfallSteps)); 34 | AddDialog(new TextPrompt(nameof(GetCustomerIdStepAsync))); 35 | AddDialog(new ChoicePrompt(nameof(ListItemsStepAsync))); 36 | AddDialog(new TextPrompt(nameof(ProcessItemSelectionStepAsync))); 37 | AddDialog(new ChoicePrompt(nameof(AskReplacementStepAsync))); 38 | AddDialog(new TextPrompt(nameof(PlaceOrderStepAsync))); 39 | 40 | _myRpaClient = myRPAClient; 41 | _entityStateAccessor = entityStateAccessor; 42 | } 43 | 44 | private async Task GetCustomerIdStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) 45 | { 46 | return await stepContext.BeginDialogAsync(nameof(GetCustomerProfileDialog)); 47 | } 48 | 49 | private async Task ListItemsStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) 50 | { 51 | await stepContext.Context.SendActivityAsync(Resource.Working_On); 52 | var customerId = await TryGetEntityValueAsync(stepContext, c_customerId); 53 | stepContext.Values[c_customerId] = customerId; 54 | 55 | var result = await _myRpaClient.GetItemsAsync(new GetItemsInput() { CustomerId = customerId }); 56 | 57 | if (result?.Items == null || result.Items.Length == 0) 58 | { 59 | return await stepContext.BeginDialogAsync(nameof(CannotFindOrderDialog)); 60 | } 61 | else 62 | { 63 | stepContext.Values["Items"] = result; 64 | var delivered = result.Items.Where(i => i.DeliveryDate < DateTime.Today).Select(i => i.ItemName).ToList(); 65 | delivered.Add(Resource.None_Of_Above); 66 | return await stepContext.PromptAsync(nameof(ListItemsStepAsync), Utils.CreateMessagePrompt(Resource.Which_Delivered, delivered)); 67 | } 68 | } 69 | 70 | private async Task ProcessItemSelectionStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) 71 | { 72 | var message = stepContext.Context.Activity.AsMessageActivity().Text; 73 | if (message.OrdinalEquals(Resource.None_Of_Above)) 74 | { 75 | return await stepContext.BeginDialogAsync(nameof(CannotFindOrderDialog)); 76 | } 77 | else 78 | { 79 | var matchedItems = ((GetItemsOutput)stepContext.Values["Items"]).Items.Where(i => i.ItemName.OrdinalEquals(message)).ToList(); 80 | if (matchedItems.Count == 0) 81 | { 82 | return await stepContext.BeginDialogAsync(nameof(CannotFindOrderDialog)); 83 | } 84 | 85 | stepContext.Values["SelectedItem"] = matchedItems[0]; 86 | 87 | return await stepContext.PromptAsync(nameof(ProcessItemSelectionStepAsync), Utils.CreateMessagePrompt(Resource.Describe_Damage)); 88 | } 89 | } 90 | 91 | private async Task AskReplacementStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) 92 | { 93 | var damage = stepContext.Context.Activity.AsMessageActivity().Text; 94 | stepContext.Values["Damage"] = damage; 95 | return await stepContext.PromptAsync(nameof(AskReplacementStepAsync), Utils.CreateMessagePrompt(Resource.Return_Or_Replace, new List(){ Resource.Replace, Resource.Return })); 96 | } 97 | 98 | private async Task PlaceOrderStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) 99 | { 100 | var message = stepContext.Context.Activity.AsMessageActivity().Text; 101 | 102 | 103 | if (message.OrdinalEquals(Resource.Return) || message.OrdinalEquals(Resource.Replace)) 104 | { 105 | await stepContext.Context.SendActivityAsync(Resource.Working_On); 106 | var item = (Item)stepContext.Values["SelectedItem"]; 107 | 108 | var cancelInput = new CancelOrderInput() 109 | { 110 | OrderId = item.OrderId, 111 | CancelReason = (string)stepContext.Values["Damage"], 112 | }; 113 | var cancelOrderOutput = await _myRpaClient.CancelOrderAsync(cancelInput); 114 | var orderCancelMessage = string.Format(Resource.Order_Cancelled, item.OrderId, cancelOrderOutput.ReturnLabelLocation); 115 | await stepContext.Context.SendActivityAsync(orderCancelMessage); 116 | 117 | if (message.OrdinalEquals(Resource.Replace)) 118 | { 119 | var salesOrderInput = new CreateSalesOrderInput() 120 | { 121 | CustomerId = (string)stepContext.Values[c_customerId], 122 | ItemId = item.ItemId, 123 | Quantity = item.Quantity, 124 | }; 125 | var createSaledOrderOuput = await _myRpaClient.CreateSalesOrderAsync(salesOrderInput); 126 | var orderCreatedMessage = string.Format(Resource.Order_Created, createSaledOrderOuput.OrderId, createSaledOrderOuput.DeliveryDate.ToShortDateString()); 127 | await stepContext.Context.SendActivityAsync(orderCreatedMessage); 128 | } 129 | await stepContext.Context.SendActivityAsync(Resource.Anything_Else); 130 | } 131 | else 132 | { 133 | await stepContext.Context.SendActivityAsync(Resource.Cannot_Understand); 134 | } 135 | 136 | return await stepContext.EndDialogAsync(); 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /BotFramework/Dialogs/MyIntents.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Schema; 2 | using System.Collections.Generic; 3 | using UiPath.ChatbotSamples.BotFramework.Common; 4 | using UiPath.ChatbotSamples.BotFramework.Common.Models; 5 | using UiPath.ChatbotSamples.BotFramework.Dialogs.MyDialog; 6 | using UiPath.ChatbotSamples.BotFramework.Resources; 7 | 8 | namespace UiPath.ChatbotSamples.BotFramework.Dialogs 9 | { 10 | public sealed class MyIntents: IBotIntents 11 | { 12 | public MyIntents() 13 | { 14 | var noneIntent = new Intent("None", null, IntentType.None); 15 | Intents = IntentsToDictionary(new Intent[] { 16 | new Intent("Greeting", nameof(GreetingDialog), IntentType.Normal), 17 | new Intent("DelayedShipment", nameof(DelayedShipmentDialog), IntentType.Normal), 18 | new Intent("ReplaceItem", nameof(ReplaceItemDialog), IntentType.Normal), 19 | new TerminationIntent("Cancel", Utils.CreateMessageActivityArray(Resource.Cancel_Success), Utils.CreateMessageActivityArray(Resource.Cancel_Fail)), 20 | new TerminationIntent("Bye", Utils.CreateMessageActivityArray(Resource.Bye_With_Active_Dialog, Resource.Talk_To_Agent), Utils.CreateMessageActivityArray(Resource.Thank_You, Resource.Bye)), 21 | new DetourIntent("Help", Utils.CreateMessageActivityArray(Resource.Help, Resource.Talk_To_Agent)), 22 | noneIntent, 23 | }); 24 | DefaultIntent = noneIntent; 25 | } 26 | 27 | public IReadOnlyDictionary Intents { get; } 28 | 29 | public double IntentTriggerThreshold { get; } = 0.5; 30 | 31 | public Intent DefaultIntent { get; } 32 | 33 | public IActivity FallbackActivity 34 | { 35 | get 36 | { 37 | return Utils.CreateMessageActivity(Resource.Cannot_Understand); 38 | } 39 | } 40 | 41 | public IActivity WelcomeActivity 42 | { 43 | get 44 | { 45 | return Utils.CreateMessageActivity(Resource.Greeting); 46 | } 47 | } 48 | 49 | private Dictionary IntentsToDictionary(Intent[] intents) 50 | { 51 | var dictionary = new Dictionary(); 52 | foreach (var intent in intents) 53 | { 54 | dictionary.Add(intent.Name, intent); 55 | } 56 | return dictionary; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /BotFramework/Dialogs/Utils.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Builder.Dialogs.Choices; 3 | using Microsoft.Bot.Schema; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using UiPath.ChatbotSamples.BotFramework.Resources; 7 | 8 | namespace UiPath.ChatbotSamples.BotFramework.Dialogs 9 | { 10 | internal static class Utils 11 | { 12 | internal static PromptOptions CreateMessagePrompt(string message) 13 | { 14 | return new PromptOptions 15 | { 16 | Prompt = new Activity 17 | { 18 | Type = ActivityTypes.Message, 19 | Text = message, 20 | }, 21 | }; 22 | } 23 | 24 | internal static PromptOptions CreateMessagePrompt(string message, List choiceStrings) 25 | { 26 | var choices = new List(); 27 | choices.AddRange(choiceStrings.Select(s => new Choice(s))); 28 | 29 | return new PromptOptions 30 | { 31 | Prompt = new Activity 32 | { 33 | Type = ActivityTypes.Message, 34 | Text = message, 35 | }, 36 | Choices = choices, 37 | }; 38 | } 39 | 40 | internal static IMessageActivity CreateMessageActivity(string message) 41 | { 42 | var activity = Activity.CreateMessageActivity(); 43 | activity.Text = message; 44 | return activity; 45 | } 46 | 47 | internal static IActivity[] CreateMessageActivityArray(params string[] messages) 48 | { 49 | var activities = new IActivity[messages.Length]; 50 | for (var i = 0; i < messages.Length; i++) 51 | { 52 | activities[i] = CreateMessageActivity(messages[i]); 53 | } 54 | return activities; 55 | } 56 | 57 | internal static List GetYesNoOptions() 58 | { 59 | return new List() { Resource.Yes, Resource.No }; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /BotFramework/DialogsTest/DialogTest.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Reflection; 3 | using UiPath.ChatbotSamples.BotFramework.Dialogs; 4 | using Xunit; 5 | 6 | namespace UiPath.ChatbotSamples.BotFramework.DialogsTest 7 | { 8 | public class DialogTest 9 | { 10 | [Fact] 11 | public void ShouldHaveAtLeastOneDialog() 12 | { 13 | string name_space = "UiPath.ChatbotSamples.BotFramework.Dialogs.MyDialog"; 14 | var q = from t in Assembly.GetAssembly(typeof(MyBot)).GetTypes() 15 | where t.IsClass && t.Namespace == name_space && !t.IsNested 16 | select t; 17 | var result = q.ToList(); 18 | Assert.True(result.Count >= 1); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /BotFramework/DialogsTest/DialogsTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | UiPath.ChatbotSamples.BotFramework.DialogsTest 6 | UiPath.ChatbotSamples.BotFramework.DialogsTest 7 | 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /BotFramework/DialogsTest/IntentTest.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using UiPath.ChatbotSamples.BotFramework.Dialogs; 3 | using Xunit; 4 | 5 | namespace UiPath.ChatbotSamples.BotFramework.DialogsTest 6 | { 7 | public class IntentTest 8 | { 9 | private readonly MyIntents intents = new MyIntents(); 10 | 11 | [Fact] 12 | public void DefaultIntentShouldBeDefined() 13 | { 14 | Assert.NotNull(intents.DefaultIntent); 15 | } 16 | 17 | [Fact] 18 | public void FallbackActivityShouldBeDefined() 19 | { 20 | Assert.NotNull(intents.FallbackActivity); 21 | } 22 | 23 | [Fact] 24 | public void WelcomActivityShouldBeDefined() 25 | { 26 | Assert.NotNull(intents.WelcomeActivity); 27 | } 28 | 29 | [Fact] 30 | public void ShouldHaveAtLeastOneIntent() 31 | { 32 | Assert.NotNull(intents.Intents); 33 | Assert.True(intents.Intents.Keys.ToList().Count > 1); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient.Test/AuthHeadHandlerTest.cs: -------------------------------------------------------------------------------- 1 | using Moq; 2 | using Moq.Protected; 3 | using System.Linq; 4 | using System.Net.Http; 5 | using System.Threading; 6 | using UiPath.ChatbotSamples.BotFramework.OrchestratorClient.Auth; 7 | using Xunit; 8 | 9 | namespace UiPath.ChatbotSamples.BotFramework.OrchestratorClient.Test 10 | { 11 | public class AuthHeadHandlerTest 12 | { 13 | [Fact] 14 | public void BasicAuthShouldAddTokenHead() 15 | { 16 | var mockTokenService = MockHelper.CreateMockTokenService(); 17 | var mockMessageHandler = MockHelper.CreateMockMessagHandler(); 18 | 19 | var basicAuthHeadhandler = new BasicAuthHeadHandler(mockTokenService.Object, mockMessageHandler.Object); 20 | 21 | var httpClient = new HttpClient(basicAuthHeadhandler) 22 | { 23 | BaseAddress = new System.Uri("http://localhost"), 24 | }; 25 | 26 | httpClient.GetAsync(httpClient.BaseAddress).GetAwaiter().GetResult(); 27 | 28 | mockMessageHandler.Protected().Verify( 29 | "SendAsync", 30 | Times.Exactly(1), // we expected a single external request 31 | ItExpr.Is(req => 32 | req.Headers.Contains("Authorization") && 33 | req.Headers.GetValues("Authorization").FirstOrDefault() == $"Bearer {MockHelper.Token}"), 34 | ItExpr.IsAny()); 35 | 36 | } 37 | 38 | [Fact] 39 | public void CloudAuthShouldAddTokenHead() 40 | { 41 | var mockTokenService = MockHelper.CreateMockTokenService(); 42 | var mockMessageHandler = MockHelper.CreateMockMessagHandler(); 43 | const string serviceInstanceLogicalName = "TestLogicalName"; 44 | var cloudAuthHeadhandler = new CloudAuthHeadHandler(mockTokenService.Object, serviceInstanceLogicalName, mockMessageHandler.Object); 45 | 46 | var httpClient = new HttpClient(cloudAuthHeadhandler) 47 | { 48 | BaseAddress = new System.Uri("http://localhost"), 49 | }; 50 | 51 | httpClient.GetAsync(httpClient.BaseAddress).GetAwaiter().GetResult(); 52 | 53 | mockMessageHandler.Protected().Verify( 54 | "SendAsync", 55 | Times.Exactly(1), // we expected a single external request 56 | ItExpr.Is(req => 57 | req.Headers.Contains("Authorization") && 58 | req.Headers.GetValues("Authorization").FirstOrDefault() == $"Bearer {MockHelper.Token}" && 59 | req.Headers.Contains("X-UIPATH-TenantName") && 60 | req.Headers.GetValues("X-UIPATH-TenantName").FirstOrDefault() == serviceInstanceLogicalName), 61 | ItExpr.IsAny()); 62 | 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient.Test/ClientTest.cs: -------------------------------------------------------------------------------- 1 | using Moq; 2 | using Moq.Protected; 3 | using System; 4 | using System.Net; 5 | using System.Net.Http; 6 | using System.Threading; 7 | using UiPath.ChatbotSamples.BotFramework.OrchestratorClient.Auth; 8 | using UiPath.ChatbotSamples.BotFramework.OrchestratorClient.JobModels; 9 | using UiPath.ChatbotSamples.BotFramework.Test.Common; 10 | using Xunit; 11 | 12 | namespace UiPath.ChatbotSamples.BotFramework.OrchestratorClient.Test 13 | { 14 | public class ClientTest 15 | { 16 | private readonly OrchestratorSettingOption _settingOption = new OrchestratorSettingOption( 17 | new OrchestratorSettings() 18 | { 19 | AuthMode = "Cloud", 20 | RefreshToken = "randometoken", 21 | ServiceInstanceLogicalName = "test", 22 | AccountLogicalName = "test", 23 | BaseUrl = "Https://platform.uipath.com", 24 | Strategy = "Specific", 25 | RobotIds = new long[] { 1, 2, 3 }, 26 | ProcessKeys = new ProcessKey[] { new ProcessKey() { Key = Guid.NewGuid().ToString(), Process = "test" } }, 27 | }); 28 | 29 | private readonly ITokenService _mockTokenService = MockHelper.CreateMockTokenService().Object; 30 | 31 | private readonly OrchestratorClient _orchestratorClient; 32 | 33 | public ClientTest() 34 | { 35 | _orchestratorClient = new OrchestratorClient(_mockTokenService, _settingOption); 36 | } 37 | 38 | [Fact] 39 | public void ThrowsIfFailedToStart() 40 | { 41 | var mockMessageHandler = MockHelper.CreateMockMessagHandler(HttpStatusCode.NotFound); 42 | var basicAuthHeadhandler = new BasicAuthHeadHandler(_mockTokenService, mockMessageHandler.Object); 43 | var httpClient = new HttpClient(basicAuthHeadhandler); 44 | 45 | Assert.Throws(() => _orchestratorClient.StartJobAsync(new StartJobInfo(), httpClient).GetAwaiter().GetResult()); 46 | } 47 | 48 | [Fact] 49 | public void SuccessIfStart() 50 | { 51 | var mockResponse = new ODataList() 52 | { 53 | Value = new StartJobResponse[] { 54 | new StartJobResponse() 55 | { 56 | Id = "1", 57 | Key = Guid.NewGuid().ToString(), 58 | State = "Started", 59 | }, 60 | }, 61 | }; 62 | var mockMessageHandler = MockHelper.CreateMockMessagHandler(HttpStatusCode.OK, Utils.GetPostBody(mockResponse)); 63 | 64 | var basicAuthHeadhandler = new BasicAuthHeadHandler(_mockTokenService, mockMessageHandler.Object); 65 | 66 | var httpClient = new HttpClient(basicAuthHeadhandler); 67 | 68 | var response = _orchestratorClient.StartJobAsync(new StartJobInfo(), httpClient).GetAwaiter().GetResult(); 69 | Assert.Equal(mockResponse.Value[0].Key, response.Key); 70 | } 71 | 72 | [Fact] 73 | public void ThrowsIfJobFaulted() 74 | { 75 | var mockResponse = new ODataList() 76 | { 77 | Value = new StartJobResponse[] { 78 | new StartJobResponse() 79 | { 80 | Id = "1", 81 | Key = Guid.NewGuid().ToString(), 82 | State = "Faulted", 83 | }, 84 | }, 85 | }; 86 | var mockMessageHandler = MockHelper.CreateMockMessagHandler(HttpStatusCode.OK, Utils.GetPostBody(mockResponse)); 87 | 88 | var basicAuthHeadhandler = new BasicAuthHeadHandler(_mockTokenService, mockMessageHandler.Object); 89 | 90 | var httpClient = new HttpClient(basicAuthHeadhandler); 91 | 92 | Assert.Throws(() => _orchestratorClient.WaitForJobCompletionAsync("1", httpClient).GetAwaiter().GetResult()); 93 | } 94 | 95 | [Fact] 96 | public void ThrowsIfJobStopped() 97 | { 98 | var mockResponse = new ODataList() 99 | { 100 | Value = new StartJobResponse[] { 101 | new StartJobResponse() 102 | { 103 | Id = "1", 104 | Key = Guid.NewGuid().ToString(), 105 | State = "Stopped", 106 | }, 107 | }, 108 | }; 109 | var mockMessageHandler = MockHelper.CreateMockMessagHandler(HttpStatusCode.OK, Utils.GetPostBody(mockResponse)); 110 | 111 | var basicAuthHeadhandler = new BasicAuthHeadHandler(_mockTokenService, mockMessageHandler.Object); 112 | 113 | var httpClient = new HttpClient(basicAuthHeadhandler); 114 | 115 | Assert.Throws(() => _orchestratorClient.WaitForJobCompletionAsync("1", httpClient).GetAwaiter().GetResult()); 116 | } 117 | 118 | [Fact] 119 | public void ThrowsIfJobTimeOut() 120 | { 121 | var mockResponse = new ODataList() 122 | { 123 | Value = new StartJobResponse[] { 124 | new StartJobResponse() 125 | { 126 | Id = "1", 127 | Key = Guid.NewGuid().ToString(), 128 | State = "Started", 129 | }, 130 | }, 131 | }; 132 | var mockMessageHandler = MockHelper.CreateMockMessagHandler(HttpStatusCode.OK, Utils.GetPostBody(mockResponse)); 133 | 134 | var basicAuthHeadhandler = new BasicAuthHeadHandler(_mockTokenService, mockMessageHandler.Object); 135 | 136 | var httpClient = new HttpClient(basicAuthHeadhandler); 137 | 138 | Assert.Throws(() => _orchestratorClient.WaitForJobCompletionAsync("1", httpClient).GetAwaiter().GetResult()); 139 | 140 | mockMessageHandler.Protected().Verify( 141 | "SendAsync", 142 | Times.Exactly(_settingOption.CurrentValue.StatusCheckMaxRetry), 143 | ItExpr.IsAny(), 144 | ItExpr.IsAny()); 145 | } 146 | 147 | [Fact] 148 | public void SuccessIfJobCompletedSuccess() 149 | { 150 | var mockResponse = new ODataList() 151 | { 152 | Value = new StartJobResponse[] { 153 | new StartJobResponse() 154 | { 155 | Id = "1", 156 | Key = Guid.NewGuid().ToString(), 157 | State = "Successful", 158 | }, 159 | }, 160 | }; 161 | var mockMessageHandler = MockHelper.CreateMockMessagHandler(HttpStatusCode.OK, Utils.GetPostBody(mockResponse)); 162 | 163 | var basicAuthHeadhandler = new BasicAuthHeadHandler(_mockTokenService, mockMessageHandler.Object); 164 | 165 | var httpClient = new HttpClient(basicAuthHeadhandler); 166 | 167 | _orchestratorClient.WaitForJobCompletionAsync("1", httpClient).GetAwaiter().GetResult(); 168 | } 169 | 170 | [Fact] 171 | public void ThrowsIfFailedToGetDetail() 172 | { 173 | var mockMessageHandler = MockHelper.CreateMockMessagHandler(HttpStatusCode.NotFound); 174 | var basicAuthHeadhandler = new BasicAuthHeadHandler(_mockTokenService, mockMessageHandler.Object); 175 | var httpClient = new HttpClient(basicAuthHeadhandler); 176 | 177 | Assert.Throws(() => _orchestratorClient.GetJobDetailAsync("1", httpClient).GetAwaiter().GetResult()); 178 | } 179 | 180 | [Fact] 181 | public void SuccessIfGetJobDetail() 182 | { 183 | var mockResponse = new StartJobResponse() 184 | { 185 | Id = "1", 186 | Key = Guid.NewGuid().ToString(), 187 | State = "Successful", 188 | OutputArguments = "{\"key\":\"value\"}", 189 | }; 190 | 191 | var mockMessageHandler = MockHelper.CreateMockMessagHandler(HttpStatusCode.OK, Utils.GetPostBody(mockResponse)); 192 | 193 | var basicAuthHeadhandler = new BasicAuthHeadHandler(_mockTokenService, mockMessageHandler.Object); 194 | 195 | var httpClient = new HttpClient(basicAuthHeadhandler); 196 | 197 | var response = _orchestratorClient.GetJobDetailAsync("1", httpClient).GetAwaiter().GetResult(); 198 | Assert.Equal(mockResponse.Key, response.Key); 199 | } 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient.Test/MockHelper.cs: -------------------------------------------------------------------------------- 1 | using Moq; 2 | using Moq.Protected; 3 | using System.Net; 4 | using System.Net.Http; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using UiPath.ChatbotSamples.BotFramework.OrchestratorClient.Auth; 8 | 9 | namespace UiPath.ChatbotSamples.BotFramework.OrchestratorClient.Test 10 | { 11 | public sealed class MockHelper 12 | { 13 | public static string Token => "Test Token"; 14 | 15 | public static Mock CreateMockTokenService() 16 | { 17 | var mockTokenService = new Mock(); 18 | 19 | mockTokenService.Setup(t => t.BasicAuthenticateAsync()) 20 | .Returns(Task.FromResult(new BasicAuthResponse() { Result = Token, })); 21 | 22 | mockTokenService.Setup(t => t.CloudAuthenticateAsync()) 23 | .Returns(Task.FromResult(new CloudAuthResponse() { access_token = Token, })); 24 | 25 | return mockTokenService; 26 | } 27 | 28 | public static Mock CreateMockMessagHandler(HttpStatusCode statusCode = HttpStatusCode.OK, HttpContent response = null) 29 | { 30 | var handlerMock = new Mock(MockBehavior.Strict); 31 | handlerMock 32 | .Protected() 33 | .Setup>( 34 | "SendAsync", 35 | ItExpr.IsAny(), 36 | ItExpr.IsAny() 37 | ) 38 | .ReturnsAsync(new HttpResponseMessage() 39 | { 40 | StatusCode = statusCode, 41 | Content = response, 42 | }) 43 | .Verifiable(); 44 | 45 | return handlerMock; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient.Test/OrchestratorClient.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | UiPath.ChatbotSamples.BotFramework.OrchestratorClient.Test 6 | UiPath.ChatbotSamples.BotFramework.OrchestratorClient.Test 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient.Test/OrchestratorSettingsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | 4 | namespace UiPath.ChatbotSamples.BotFramework.OrchestratorClient.Test 5 | { 6 | public class OrchestratorSettingsTest 7 | { 8 | [Fact] 9 | public void OnlyTwoAuthModeAllowed() 10 | { 11 | var setting = new OrchestratorSettings() 12 | { 13 | AuthMode = "another mode", 14 | TenancyName = "Default", 15 | UsernameOrEmailAddress = "Test", 16 | Password = "Test", 17 | BaseUrl = "Http://localhost", 18 | Strategy = "JobsCount", 19 | JobsCount = 1, 20 | ProcessKeys = new ProcessKey[] { new ProcessKey() { Key = Guid.NewGuid().ToString(), Process = "test" } }, 21 | }; 22 | 23 | Assert.False(setting.Validate()); 24 | } 25 | 26 | [Fact] 27 | public void UerNamePasswordRequiredForBasicAuth() 28 | { 29 | var setting = new OrchestratorSettings() 30 | { 31 | AuthMode = "Basic", 32 | BaseUrl = "Http://localhost", 33 | Strategy = "JobsCount", 34 | JobsCount = 1, 35 | ProcessKeys = new ProcessKey[] { new ProcessKey() { Key = Guid.NewGuid().ToString(), Process = "test" } }, 36 | }; 37 | 38 | Assert.False(setting.Validate()); 39 | } 40 | 41 | [Fact] 42 | public void UerTokenRequiredForCloudAuth() 43 | { 44 | var setting = new OrchestratorSettings() 45 | { 46 | AuthMode = "Cloud", 47 | BaseUrl = "Http://localhost", 48 | Strategy = "JobsCount", 49 | JobsCount = 1, 50 | ProcessKeys = new ProcessKey[] { new ProcessKey() { Key = Guid.NewGuid().ToString(), Process = "test" } }, 51 | }; 52 | 53 | Assert.False(setting.Validate()); 54 | } 55 | 56 | [Fact] 57 | public void UrlShouldBeValid() 58 | { 59 | var setting = new OrchestratorSettings() 60 | { 61 | AuthMode = "Basic", 62 | TenancyName = "Default", 63 | UsernameOrEmailAddress = "Test", 64 | Password = "Test", 65 | BaseUrl = "invalid", 66 | Strategy = "JobsCount", 67 | JobsCount = 1, 68 | ProcessKeys = new ProcessKey[] { new ProcessKey() { Key = Guid.NewGuid().ToString(), Process = "test" } }, 69 | }; 70 | 71 | Assert.False(setting.Validate()); 72 | } 73 | 74 | [Fact] 75 | public void TwoStrategyAllowed() 76 | { 77 | var setting = new OrchestratorSettings() 78 | { 79 | AuthMode = "Basic", 80 | TenancyName = "Default", 81 | UsernameOrEmailAddress = "Test", 82 | Password = "Test", 83 | BaseUrl = "Http://localhost", 84 | Strategy = "invalid", 85 | ProcessKeys = new ProcessKey[] { new ProcessKey() { Key = Guid.NewGuid().ToString(), Process = "test" } }, 86 | }; 87 | 88 | Assert.False(setting.Validate()); 89 | } 90 | 91 | [Fact] 92 | public void SpecificStrategyShouldHaveAtLeastOneRobot() 93 | { 94 | var setting = new OrchestratorSettings() 95 | { 96 | AuthMode = "Basic", 97 | TenancyName = "Default", 98 | UsernameOrEmailAddress = "Test", 99 | Password = "Test", 100 | BaseUrl = "Http://localhost", 101 | Strategy = "Specific", 102 | RobotIds = null, 103 | }; 104 | 105 | Assert.False(setting.Validate()); 106 | } 107 | 108 | [Fact] 109 | public void JobsCountStrategyShouldHavePositiveCount() 110 | { 111 | var setting = new OrchestratorSettings() 112 | { 113 | AuthMode = "Basic", 114 | TenancyName = "Default", 115 | UsernameOrEmailAddress = "Test", 116 | Password = "Test", 117 | BaseUrl = "Http://localhost", 118 | Strategy = "JobsCount", 119 | JobsCount = -1, 120 | }; 121 | 122 | Assert.False(setting.Validate()); 123 | } 124 | 125 | [Fact] 126 | public void ShouldHaveAtLeastOneProcess() 127 | { 128 | var setting = new OrchestratorSettings() 129 | { 130 | AuthMode = "Basic", 131 | TenancyName = "Default", 132 | UsernameOrEmailAddress = "Test", 133 | Password = "Test", 134 | BaseUrl = "Http://localhost", 135 | Strategy = "JobsCount", 136 | JobsCount = 1, 137 | ProcessKeys = null, 138 | }; 139 | 140 | Assert.False(setting.Validate()); 141 | } 142 | 143 | [Fact] 144 | public void ProcessKeyShouldBeGuid() 145 | { 146 | var setting = new OrchestratorSettings() 147 | { 148 | AuthMode = "Basic", 149 | TenancyName = "Default", 150 | UsernameOrEmailAddress = "Test", 151 | Password = "Test", 152 | BaseUrl = "Http://localhost", 153 | Strategy = "JobsCount", 154 | JobsCount = 1, 155 | ProcessKeys = new ProcessKey[] { new ProcessKey() { Key = "1234", Process = "test" } }, 156 | }; 157 | 158 | Assert.False(setting.Validate()); 159 | } 160 | 161 | [Fact] 162 | public void ValidBasicAuth() 163 | { 164 | var setting = new OrchestratorSettings() 165 | { 166 | AuthMode = "Basic", 167 | TenancyName = "Default", 168 | UsernameOrEmailAddress = "Test", 169 | Password = "Test", 170 | BaseUrl = "Http://localhost", 171 | Strategy = "JobsCount", 172 | JobsCount = 1, 173 | ProcessKeys = new ProcessKey[] { new ProcessKey() { Key = Guid.NewGuid().ToString(), Process = "test" } }, 174 | }; 175 | 176 | Assert.True(setting.Validate()); 177 | } 178 | 179 | [Fact] 180 | public void ValidCloudAuth() 181 | { 182 | var setting = new OrchestratorSettings() 183 | { 184 | AuthMode = "Cloud", 185 | RefreshToken = "randometoken", 186 | ServiceInstanceLogicalName = "test", 187 | AccountLogicalName = "test", 188 | BaseUrl = "Https://platform.uipath.com", 189 | Strategy = "Specific", 190 | RobotIds = new long[] { 1, 2, 3}, 191 | ProcessKeys = new ProcessKey[] { new ProcessKey() { Key = Guid.NewGuid().ToString(), Process = "test" } }, 192 | }; 193 | 194 | Assert.True(setting.Validate()); 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient.Test/UtilsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | 4 | namespace UiPath.ChatbotSamples.BotFramework.OrchestratorClient.Test 5 | { 6 | public class UtilsTest 7 | { 8 | [Fact] 9 | public void GetValidUrlWithTrailingBaseUrl() 10 | { 11 | var baseUrl = "http://localhost/"; 12 | var jobKey = "123"; 13 | Assert.True(Uri.IsWellFormedUriString(Utils.GetBasicAuthUrl(baseUrl), UriKind.Absolute)); 14 | Assert.True(Uri.IsWellFormedUriString(Utils.GetJobDetailUrl(baseUrl, jobKey), UriKind.Absolute)); 15 | // job status url contains parameter, need to remove those before check 16 | var jobStatusUrl = Utils.GetJobStatusUrl(baseUrl, jobKey); 17 | Assert.True(Uri.IsWellFormedUriString(jobStatusUrl.Substring(0, jobStatusUrl.IndexOf('$')), UriKind.Absolute)); 18 | Assert.True(Uri.IsWellFormedUriString(Utils.GetStartjobUrl(baseUrl), UriKind.Absolute)); 19 | } 20 | 21 | [Fact] 22 | public void GetValidUrlWithNoneTrailingBaseUrl() 23 | { 24 | var baseUrl = "http://localhost"; 25 | var jobKey = "123"; 26 | 27 | var authUrl = Utils.GetBasicAuthUrl(baseUrl); 28 | var jobDetailUrl = Utils.GetJobDetailUrl(baseUrl, jobKey); 29 | // job status url contains parameter, need to remove those before check 30 | var jobStatusUrl = Utils.GetJobStatusUrl(baseUrl, jobKey); 31 | var startJobUrl = Utils.GetStartjobUrl(baseUrl); 32 | 33 | Assert.True(Uri.IsWellFormedUriString(authUrl, UriKind.Absolute)); 34 | Assert.True(Uri.IsWellFormedUriString(jobDetailUrl, UriKind.Absolute)); 35 | Assert.True(Uri.IsWellFormedUriString(jobStatusUrl.Substring(0, jobStatusUrl.IndexOf('$')), UriKind.Absolute)); 36 | Assert.True(Uri.IsWellFormedUriString(startJobUrl, UriKind.Absolute)); 37 | 38 | var trailedBaseUrl = $"{baseUrl}/"; 39 | Assert.StartsWith(trailedBaseUrl, authUrl); 40 | Assert.StartsWith(trailedBaseUrl, jobDetailUrl); 41 | Assert.StartsWith(trailedBaseUrl, jobStatusUrl); 42 | Assert.StartsWith(trailedBaseUrl, startJobUrl); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient/Auth/BasicAuthHeadHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using System.Net.Http.Headers; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace UiPath.ChatbotSamples.BotFramework.OrchestratorClient.Auth 7 | { 8 | public class BasicAuthHeadHandler : DelegatingHandler 9 | { 10 | private readonly ITokenService _tokenService; 11 | 12 | public BasicAuthHeadHandler(ITokenService tokenService) 13 | : base(new HttpClientHandler()) 14 | { 15 | _tokenService = tokenService; 16 | } 17 | 18 | public BasicAuthHeadHandler(ITokenService tokenService, HttpMessageHandler innerHandler) 19 | : base(innerHandler) 20 | { 21 | _tokenService = tokenService; 22 | } 23 | 24 | protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 25 | { 26 | var token = await _tokenService.BasicAuthenticateAsync(); 27 | request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.Result); 28 | 29 | return await base.SendAsync(request, cancellationToken); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient/Auth/BasicAuthResponse.cs: -------------------------------------------------------------------------------- 1 | namespace UiPath.ChatbotSamples.BotFramework.OrchestratorClient.Auth 2 | { 3 | public class BasicAuthResponse 4 | { 5 | // Do Not change this. It is hard coded on Orchestrator side. 6 | public string Result { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient/Auth/BasicAuthSettings.cs: -------------------------------------------------------------------------------- 1 | namespace UiPath.ChatbotSamples.BotFramework.OrchestratorClient.Auth 2 | { 3 | public class BasicAuthSettings 4 | { 5 | public string TenancyName { get; set; } 6 | public string UsernameOrEmailAddress { get; set; } 7 | public string Password { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient/Auth/CloudAuthHeadHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using System.Net.Http.Headers; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace UiPath.ChatbotSamples.BotFramework.OrchestratorClient.Auth 7 | { 8 | public class CloudAuthHeadHandler : DelegatingHandler 9 | { 10 | private readonly ITokenService _tokenService; 11 | private readonly string _serviceInstanceLogicalName; 12 | 13 | public CloudAuthHeadHandler(ITokenService tokenService, string serviceInstanceLogicalName) 14 | : base(new HttpClientHandler()) 15 | { 16 | _tokenService = tokenService; 17 | _serviceInstanceLogicalName = serviceInstanceLogicalName; 18 | } 19 | 20 | public CloudAuthHeadHandler(ITokenService tokenService, string serviceInstanceLogicalName, HttpMessageHandler innerHandler) 21 | : base(innerHandler) 22 | { 23 | _tokenService = tokenService; 24 | _serviceInstanceLogicalName = serviceInstanceLogicalName; 25 | } 26 | 27 | protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 28 | { 29 | var token = await _tokenService.CloudAuthenticateAsync(); 30 | request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.access_token); 31 | request.Headers.Add("X-UIPATH-TenantName", _serviceInstanceLogicalName); 32 | 33 | 34 | return await base.SendAsync(request, cancellationToken); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient/Auth/CloudAuthResponse.cs: -------------------------------------------------------------------------------- 1 | namespace UiPath.ChatbotSamples.BotFramework.OrchestratorClient.Auth 2 | { 3 | public class CloudAuthResponse 4 | { 5 | public string access_token { get; set; } 6 | 7 | public string refresh_token { get; set; } 8 | 9 | public string id_token { get; set; } 10 | 11 | public string scope { get; set;} 12 | 13 | public string expires_in { get; set;} 14 | 15 | public string token_type { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient/Auth/CloudAuthSettings.cs: -------------------------------------------------------------------------------- 1 | namespace UiPath.ChatbotSamples.BotFramework.OrchestratorClient.Auth 2 | { 3 | public class CloudAuthSettings 4 | { 5 | public string grant_type => "refresh_token"; 6 | 7 | public string client_id => "5v7PmPJL6FOGu6RB8I1Y4adLBhIwovQN"; 8 | 9 | public string refresh_token { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient/Auth/ITokenService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace UiPath.ChatbotSamples.BotFramework.OrchestratorClient.Auth 4 | { 5 | public interface ITokenService 6 | { 7 | Task BasicAuthenticateAsync(); 8 | 9 | Task CloudAuthenticateAsync(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient/Auth/TokenService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Options; 2 | using System.Net.Http; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using UiPath.ChatbotSamples.BotFramework.Utils; 6 | 7 | namespace UiPath.ChatbotSamples.BotFramework.OrchestratorClient.Auth 8 | { 9 | public class TokenService : ITokenService 10 | { 11 | private readonly BasicAuthSettings _basicAuthSettings; 12 | private readonly CloudAuthSettings _cloudAuthSettings; 13 | private readonly string _baseUrl; 14 | 15 | public TokenService(IOptionsMonitor orchestratorSettingAccessor) 16 | { 17 | var orchestratorSettings = orchestratorSettingAccessor.CheckNullReference().CurrentValue; 18 | 19 | _basicAuthSettings = new BasicAuthSettings() 20 | { 21 | TenancyName = orchestratorSettings.TenancyName, 22 | UsernameOrEmailAddress = orchestratorSettings.UsernameOrEmailAddress, 23 | Password = orchestratorSettings.Password, 24 | }; 25 | 26 | _cloudAuthSettings = new CloudAuthSettings() 27 | { 28 | refresh_token = orchestratorSettings.RefreshToken, 29 | }; 30 | 31 | _baseUrl = orchestratorSettings.BaseUrl; 32 | } 33 | 34 | public async Task BasicAuthenticateAsync() 35 | { 36 | using (HttpClient client = new HttpClient()) 37 | { 38 | var tokenResponse = await client.PostAsync(Utils.GetBasicAuthUrl(_baseUrl), Utils.GetPostBody(_basicAuthSettings), new CancellationToken()); 39 | return await tokenResponse.Content.ReadAsAsync(); 40 | } 41 | } 42 | 43 | public async Task CloudAuthenticateAsync() 44 | { 45 | using (HttpClient client = new HttpClient()) 46 | { 47 | var tokenResponse = await client.PostAsync(Utils.CloudAuthUrl, Utils.GetPostBody(_cloudAuthSettings), new CancellationToken()); 48 | return await tokenResponse.Content.ReadAsAsync(); 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient/IOrchestratorClient.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using UiPath.ChatbotSamples.BotFramework.OrchestratorClient.JobModels; 3 | 4 | namespace UiPath.ChatbotSamples.BotFramework.OrchestratorClient 5 | { 6 | public interface IOrchestratorClient 7 | { 8 | Task ExecuteJobAsync(StartJobInfo jobInfo); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient/JobModels/ODataList.cs: -------------------------------------------------------------------------------- 1 | namespace UiPath.ChatbotSamples.BotFramework.OrchestratorClient.JobModels 2 | { 3 | public class ODataList 4 | { 5 | public T[] Value { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient/JobModels/StartJobBody.cs: -------------------------------------------------------------------------------- 1 | namespace UiPath.ChatbotSamples.BotFramework.OrchestratorClient.JobModels 2 | { 3 | public class StartJobBody 4 | { 5 | // DO NOT Change this name. It is hard coded on Orchestrator side. 6 | public StartJobInfo startInfo { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient/JobModels/StartJobInfo.cs: -------------------------------------------------------------------------------- 1 | namespace UiPath.ChatbotSamples.BotFramework.OrchestratorClient.JobModels 2 | { 3 | public class StartJobInfo 4 | { 5 | public string ReleaseKey { get; set; } 6 | 7 | public string Strategy { get; set; } 8 | 9 | public int JobsCount { get; set; } 10 | 11 | public long[] RobotIds { get; set; } 12 | 13 | public string InputArguments { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient/JobModels/StartJobResponse.cs: -------------------------------------------------------------------------------- 1 | namespace UiPath.ChatbotSamples.BotFramework.OrchestratorClient.JobModels 2 | { 3 | public class StartJobResponse 4 | { 5 | public string Id { get; set; } 6 | public string Key { get; set; } 7 | public string State { get; set; } 8 | public string OutputArguments { get; set; } = null; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient/OrchestratorClient.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Options; 2 | using System; 3 | using System.Net; 4 | using System.Net.Http; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using UiPath.ChatbotSamples.BotFramework.OrchestratorClient.Auth; 8 | using UiPath.ChatbotSamples.BotFramework.OrchestratorClient.JobModels; 9 | using UiPath.ChatbotSamples.BotFramework.Utils; 10 | 11 | namespace UiPath.ChatbotSamples.BotFramework.OrchestratorClient 12 | { 13 | public class OrchestratorClient : IOrchestratorClient 14 | { 15 | private readonly HttpClient _client; 16 | private readonly string _baseUrl; 17 | private readonly int _statusCheckInterval; 18 | private readonly int _statusMaxRetry; 19 | 20 | public OrchestratorClient(ITokenService tokenService, IOptionsMonitor orchestratorSettingsAccessor) 21 | { 22 | var orchestratorSettings = orchestratorSettingsAccessor.CheckNullReference().CurrentValue; 23 | if (!orchestratorSettings.Validate()) 24 | { 25 | throw new ArgumentException("Orchestrator setting invalid"); 26 | } 27 | 28 | if (orchestratorSettings.AuthMode.OrdinalEquals("Basic")) 29 | { 30 | _client = new HttpClient(new BasicAuthHeadHandler(tokenService.CheckNullReference())); 31 | } 32 | else 33 | { 34 | _client = new HttpClient(new CloudAuthHeadHandler(tokenService.CheckNullReference(), orchestratorSettings.ServiceInstanceLogicalName)); 35 | } 36 | 37 | _baseUrl = orchestratorSettings.CheckNullReference().BaseUrl; 38 | _statusCheckInterval = orchestratorSettings.StatusCheckInterval; 39 | _statusMaxRetry = orchestratorSettings.StatusCheckMaxRetry; 40 | } 41 | 42 | public async Task ExecuteJobAsync(StartJobInfo jobInfo) 43 | { 44 | var jobResponse = await StartJobAsync(jobInfo); 45 | await WaitForJobCompletionAsync(jobResponse.Key); 46 | return await GetJobDetailAsync(jobResponse.Id); 47 | } 48 | 49 | // Depending on user scenario, if the robot is very busy and start job may fail. So a retry will need to be added here. 50 | public async Task StartJobAsync(StartJobInfo jobInfo, HttpClient client = null) 51 | { 52 | StartJobBody body = new StartJobBody() { startInfo = jobInfo }; 53 | var startJobResponseList = await HttpCallAsync>(Utils.GetStartjobUrl(_baseUrl), HttpMethod.Post, Utils.GetPostBody(body), client); 54 | return startJobResponseList.Value[0]; 55 | } 56 | 57 | public async Task WaitForJobCompletionAsync(string jobKey, HttpClient client = null) 58 | { 59 | int count = 0; 60 | while (count++ < _statusMaxRetry) 61 | { 62 | await Task.Delay(_statusCheckInterval); 63 | 64 | var jobs = await HttpCallAsync>(Utils.GetJobStatusUrl(_baseUrl, jobKey), HttpMethod.Get, null, client); 65 | if (jobs?.Value?.Length == null) 66 | { 67 | continue; 68 | } 69 | 70 | var job = jobs.Value[0]; 71 | 72 | if (job.State.OrdinalEquals("Successful")) 73 | { 74 | return; 75 | } 76 | if (job.State.OrdinalEquals("Faulted") || job.State.OrdinalEquals("Stopped")) 77 | { 78 | throw new Exception($"Job {jobKey} completed with {job.State} state."); 79 | } 80 | } 81 | throw new Exception($"Job {jobKey} timedout after {_statusCheckInterval * _statusMaxRetry} ms."); 82 | } 83 | 84 | public async Task GetJobDetailAsync(string jobId, HttpClient client = null) 85 | { 86 | return await HttpCallAsync(Utils.GetJobDetailUrl(_baseUrl, jobId), HttpMethod.Get, null, client); 87 | } 88 | 89 | private void EnsureSuccessStatus(HttpResponseMessage response) 90 | { 91 | if (response?.StatusCode != HttpStatusCode.OK && response?.StatusCode != HttpStatusCode.Created) 92 | { 93 | throw new Exception("Orchestrator call failed"); 94 | } 95 | } 96 | 97 | private async Task HttpCallAsync(string url, HttpMethod httpMethod, HttpContent body = null, HttpClient client = null) where T: class 98 | { 99 | client = client ?? _client; 100 | 101 | HttpResponseMessage response = null; 102 | 103 | if (httpMethod == HttpMethod.Get) 104 | { 105 | response = await client.GetAsync(url, new CancellationToken()); 106 | } 107 | else if (httpMethod == HttpMethod.Post) 108 | { 109 | response = await client.PostAsync(url, body, new CancellationToken()); 110 | } 111 | 112 | EnsureSuccessStatus(response); 113 | 114 | return await (response).Content.ReadAsAsync(); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient/OrchestratorClient.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | true 6 | UiPath.ChatbotSamples.BotFramework.OrchestratorClient 7 | UiPath.ChatbotSamples.BotFramework.OrchestratorClient 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient/OrchestratorSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UiPath.ChatbotSamples.BotFramework.Utils; 3 | 4 | namespace UiPath.ChatbotSamples.BotFramework.OrchestratorClient 5 | { 6 | public class OrchestratorSettings 7 | { 8 | public string BaseUrl { get; set; } 9 | 10 | public string AuthMode { get; set; } 11 | 12 | public string TenancyName { get; set; } 13 | 14 | public string UsernameOrEmailAddress { get; set; } 15 | 16 | public string Password { get; set; } 17 | 18 | public string RefreshToken { get; set; } 19 | 20 | public string ServiceInstanceLogicalName { get; set; } 21 | 22 | public string AccountLogicalName { get; set; } 23 | 24 | public int StatusCheckMaxRetry { get; set; } = 60; 25 | 26 | public int StatusCheckInterval { get; set; } = 500; 27 | 28 | public string Strategy { get; set; } = "Specific"; 29 | 30 | public int JobsCount { get; set; } = 0; 31 | 32 | public long[] RobotIds { get; set; } 33 | 34 | public ProcessKey[] ProcessKeys { get; set; } 35 | 36 | public bool Validate() 37 | { 38 | if (!AuthMode.OrdinalEquals("Basic") && !AuthMode.OrdinalEquals("Cloud")) 39 | { 40 | return false; 41 | } 42 | 43 | if (AuthMode.OrdinalEquals("Basic")) 44 | { 45 | if (string.IsNullOrEmpty(TenancyName) || string.IsNullOrEmpty(UsernameOrEmailAddress) || string.IsNullOrEmpty(Password)) 46 | { 47 | return false; 48 | } 49 | } 50 | 51 | if (AuthMode.OrdinalEquals("Cloud")) 52 | { 53 | if (string.IsNullOrEmpty(RefreshToken) || string.IsNullOrEmpty(ServiceInstanceLogicalName) || string.IsNullOrEmpty(AccountLogicalName)) 54 | { 55 | return false; 56 | } 57 | } 58 | 59 | bool isUrl = Uri.IsWellFormedUriString(BaseUrl, UriKind.Absolute); 60 | if (!isUrl) 61 | { 62 | return false; 63 | } 64 | 65 | if (!Strategy.OrdinalEquals("Specific") && !Strategy.OrdinalEquals("JobsCount")) 66 | { 67 | return false; 68 | } 69 | 70 | if (Strategy.OrdinalEquals("Specific") && (RobotIds == null || RobotIds.Length == 0)) 71 | { 72 | return false; 73 | } 74 | 75 | if (Strategy.OrdinalEquals("JobsCount") && JobsCount <= 0) 76 | { 77 | return false; 78 | } 79 | 80 | if (ProcessKeys == null || ProcessKeys.Length == 0) 81 | { 82 | return false; 83 | } 84 | 85 | foreach (var processKey in ProcessKeys) 86 | { 87 | if (!Guid.TryParse(processKey.Key, out Guid newGuid)) 88 | { 89 | return false; 90 | } 91 | } 92 | 93 | return true; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient/ProcessKey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace UiPath.ChatbotSamples.BotFramework.OrchestratorClient 6 | { 7 | public class ProcessKey 8 | { 9 | public string Process { get; set; } 10 | 11 | public string Key { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /BotFramework/OrchestratorClient/Utils.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Net.Http; 4 | using System.Text; 5 | 6 | namespace UiPath.ChatbotSamples.BotFramework.OrchestratorClient 7 | { 8 | public static class Utils 9 | { 10 | public static string GetBasicAuthUrl(string baseUrl) 11 | { 12 | return ConcatUrl(baseUrl, _basicAuthUrl); 13 | } 14 | 15 | public static string CloudAuthUrl => "https://account.uipath.com/oauth/token"; 16 | 17 | public static string GetStartjobUrl(string baseUrl) 18 | { 19 | return ConcatUrl(baseUrl, _startJobUrl); 20 | } 21 | 22 | public static string GetJobStatusUrl(string baseUrl, string jobKey) 23 | { 24 | return ConcatUrl(baseUrl, "odata/Jobs?$top=3&$filter=Key eq " + jobKey); 25 | } 26 | 27 | public static string GetJobDetailUrl(string baseUrl, string jobId) 28 | { 29 | return ConcatUrl(baseUrl, "odata/Jobs(" + jobId + @")"); 30 | } 31 | 32 | public static StringContent GetPostBody(T t) 33 | { 34 | return new StringContent(SerializePostBody(t), Encoding.UTF8, "application/json"); 35 | } 36 | 37 | private static string SerializePostBody(T t) 38 | { 39 | return JsonConvert.SerializeObject(t, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); 40 | } 41 | 42 | private static string _basicAuthUrl => "api/account/authenticate"; 43 | 44 | private static string _startJobUrl => "odata/Jobs/UiPath.Server.Configuration.OData.StartJobs"; 45 | 46 | private static string ConcatUrl(string baseUrl, string routing) 47 | { 48 | if (string.IsNullOrEmpty(baseUrl) || string.IsNullOrEmpty(routing)) 49 | { 50 | throw new InvalidOperationException("cannot concat url with null or empty string"); 51 | } 52 | var delimiter = '/'; 53 | 54 | return $"{baseUrl.TrimEnd(delimiter)}{delimiter}{routing.TrimStart(delimiter)}"; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /BotFramework/README.md: -------------------------------------------------------------------------------- 1 | # SampleBot Solution Code Strucure 2 | SampleBot solution is written to create an implementation reference on how to integrate Microsoft Bot Framework and UiPath Orchestrator to build a virtual agent that can execute tasks using RPA technology. 3 | 4 | This document is a walk through of the code. 5 | 6 | # Prerequisites 7 | Refer to [this link](https://docs.microsoft.com/en-us/azure/bot-service/dotnet/bot-builder-dotnet-sdk-quickstart?view=azure-bot-service-4.0) for prerequisites. 8 | Bot Framework V4 template for C# is not required to run the sample code or create your bot project based on the sample code. It is required when generate a new blank bot project. 9 | 10 | # Entry point - SampleBot project 11 | This project is the entry point of the bot. Majority part of this project is generated from the bot framework template. 12 | What this project do: 13 | - Provides the API endpoint to listen to user input message and send bot response 14 | - Read settings 15 | - Inject the dependencies 16 | 17 | # Common project 18 | This project implements a common multi-turn bot. This bot is focusing on the conversation strategy and it doesn't couple with real life dialog flows. It includes below functionalities: 19 | - Define common models 20 | * EntityState: This is the data strucutre to store and retrieve any extracted entity 21 | * LuisEntity: It provides the ability to post process any entity value that extracted from Luis. For example, normalize names. 22 | * Intent: There are different types of intents defined. Normal intent is user intent for virtual agent to execute some dialog. DetourIntent (for example help) is the intent the makes the bot need to detour to another task (for example provide help text) and come back to resume the last dialog step. TerminationIntent (for example cancel) is the intent that makes the bot stop what it is doing and start like a new conversation. 23 | - Error handling. For any exception uncaught, reply with internal exception and clean up resources. 24 | - Define conversation strategy. For each conversation turn: 25 | * If it is conversation update(user join conversation), then send welcome activity 26 | * If it is message, then do the following: 27 | 1) Recognize intent using Luis 28 | 2) Add extracted entity (if any) to EntityState 29 | 3) If the intent is to terminate or detour, handle the interruption and return 30 | 4) Continue current active dialog with this intent 31 | 5) If no active dialog, then start the dialog mappted to this intent 32 | 33 | # Dialog project 34 | This project defines the customized settings for the sample scenario (My bot that handles replace item and cancel order). It includes 35 | - Intents. MyIntents defines the intent for the bot and the mapping dialog flow 36 | - MyDialogs. Dialogs in this folder defines the conversation flow for each dialog. To get entities from EntityState, inherit from StatefulDialogBase, otherwise DialogBase. 37 | - MyBotSettings. This file defines the LUIS configuration it wants to use and the postprocessing for LUIS entities 38 | - MyBot. This file adds all the dialogs to the dialog set. 39 | 40 | # Actions project 41 | This project is called by Dialog project to execute bot actions. In this sample code, all the actions talk to orchestrator to run a certain process. This project includes: 42 | - Models as interface between Dialog and Actions 43 | - Models as interface between Actions and Orchestrator. Note that the models may not necessarily the same as the models above. GetItemsOutputInternal.cs provides an example of this. It needs to be mapped to the external model for dialog consumption. 44 | - MyRpaClient that is to prepare the input parameter and call the Orchestrator client to run a certain process 45 | 46 | # OrchestratorClient project 47 | This project is to run a job in orchestrator and return the result. The public method it provides is ExecuteJobAsync, which first start job, then wait for job to complete, then get job detail and return. 48 | Note that when all robots are busy and have at least one job pending already, start job may fail. So depending on the user scenario and the robot settings, a retry may be need to add in this project to handle that situation. 49 | 50 | 51 | # Helpers - Utils and Resources project 52 | Utils project provides common functions for other projects to use, such as null check. 53 | Resources project put all the text message in resource file, so it can be easily update externally or extended for multiple lanugage support. 54 | -------------------------------------------------------------------------------- /BotFramework/Resources/Resource.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace UiPath.ChatbotSamples.BotFramework.Resources { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | public class Resource { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resource() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | public static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UiPath.ChatbotSamples.BotFramework.Resources.Resource", typeof(Resource).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | public static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to Is there anything else I can help you with?. 65 | /// 66 | public static string Anything_Else { 67 | get { 68 | return ResourceManager.GetString("Anything_Else", resourceCulture); 69 | } 70 | } 71 | 72 | /// 73 | /// Looks up a localized string similar to I can help to search for your order. Can you provide your customer Id?. 74 | /// 75 | public static string Ask_Customer_Id { 76 | get { 77 | return ResourceManager.GetString("Ask_Customer_Id", resourceCulture); 78 | } 79 | } 80 | 81 | /// 82 | /// Looks up a localized string similar to Bye! Have a good day!. 83 | /// 84 | public static string Bye { 85 | get { 86 | return ResourceManager.GetString("Bye", resourceCulture); 87 | } 88 | } 89 | 90 | /// 91 | /// Looks up a localized string similar to Sorry, I was not helpful.. 92 | /// 93 | public static string Bye_With_Active_Dialog { 94 | get { 95 | return ResourceManager.GetString("Bye_With_Active_Dialog", resourceCulture); 96 | } 97 | } 98 | 99 | /// 100 | /// Looks up a localized string similar to Sorry, I don't have anything to cancel.. 101 | /// 102 | public static string Cancel_Fail { 103 | get { 104 | return ResourceManager.GetString("Cancel_Fail", resourceCulture); 105 | } 106 | } 107 | 108 | /// 109 | /// Looks up a localized string similar to I have cancelled our last activity.. 110 | /// 111 | public static string Cancel_Success { 112 | get { 113 | return ResourceManager.GetString("Cancel_Success", resourceCulture); 114 | } 115 | } 116 | 117 | /// 118 | /// Looks up a localized string similar to Sorry, I didn't understand what you just said to me.. 119 | /// 120 | public static string Cannot_Understand { 121 | get { 122 | return ResourceManager.GetString("Cannot_Understand", resourceCulture); 123 | } 124 | } 125 | 126 | /// 127 | /// Looks up a localized string similar to Your order is not delivered because it is {0}. Do you want me to cancel this one and order from another supplier for you with no extra shipping fee? . 128 | /// 129 | public static string Deliver_Status { 130 | get { 131 | return ResourceManager.GetString("Deliver_Status", resourceCulture); 132 | } 133 | } 134 | 135 | /// 136 | /// Looks up a localized string similar to Can you describe the damage?. 137 | /// 138 | public static string Describe_Damage { 139 | get { 140 | return ResourceManager.GetString("Describe_Damage", resourceCulture); 141 | } 142 | } 143 | 144 | /// 145 | /// Looks up a localized string similar to Hi, how can I help you today?. 146 | /// 147 | public static string Greeting { 148 | get { 149 | return ResourceManager.GetString("Greeting", resourceCulture); 150 | } 151 | } 152 | 153 | /// 154 | /// Looks up a localized string similar to You can ask me about delayed shipments, broken items, etc.. 155 | /// 156 | public static string Help { 157 | get { 158 | return ResourceManager.GetString("Help", resourceCulture); 159 | } 160 | } 161 | 162 | /// 163 | /// Looks up a localized string similar to Sorry, something went wrong. Please retry later.. 164 | /// 165 | public static string Internal_Exception { 166 | get { 167 | return ResourceManager.GetString("Internal_Exception", resourceCulture); 168 | } 169 | } 170 | 171 | /// 172 | /// Looks up a localized string similar to No. 173 | /// 174 | public static string No { 175 | get { 176 | return ResourceManager.GetString("No", resourceCulture); 177 | } 178 | } 179 | 180 | /// 181 | /// Looks up a localized string similar to I don't see any other undelivered orders. Would you like to talk to an agent?. 182 | /// 183 | public static string No_Undelivered_Order { 184 | get { 185 | return ResourceManager.GetString("No_Undelivered_Order", resourceCulture); 186 | } 187 | } 188 | 189 | /// 190 | /// Looks up a localized string similar to None of above. 191 | /// 192 | public static string None_Of_Above { 193 | get { 194 | return ResourceManager.GetString("None_Of_Above", resourceCulture); 195 | } 196 | } 197 | 198 | /// 199 | /// Looks up a localized string similar to Your order with order number {0} has been cancelled. You can print your return label at {1}. Details have been sent to your email.. 200 | /// 201 | public static string Order_Cancelled { 202 | get { 203 | return ResourceManager.GetString("Order_Cancelled", resourceCulture); 204 | } 205 | } 206 | 207 | /// 208 | /// Looks up a localized string similar to Your new order has been placed with order number {0}. It is expected to deliver on {1}. Details have been sent to your email.. 209 | /// 210 | public static string Order_Created { 211 | get { 212 | return ResourceManager.GetString("Order_Created", resourceCulture); 213 | } 214 | } 215 | 216 | /// 217 | /// Looks up a localized string similar to Replace. 218 | /// 219 | public static string Replace { 220 | get { 221 | return ResourceManager.GetString("Replace", resourceCulture); 222 | } 223 | } 224 | 225 | /// 226 | /// Looks up a localized string similar to Return. 227 | /// 228 | public static string Return { 229 | get { 230 | return ResourceManager.GetString("Return", resourceCulture); 231 | } 232 | } 233 | 234 | /// 235 | /// Looks up a localized string similar to I've made a note of the damage. Do you want me to return or replace this item?. 236 | /// 237 | public static string Return_Or_Replace { 238 | get { 239 | return ResourceManager.GetString("Return_Or_Replace", resourceCulture); 240 | } 241 | } 242 | 243 | /// 244 | /// Looks up a localized string similar to To talk to a human agent, please call (123)456-7890.. 245 | /// 246 | public static string Talk_To_Agent { 247 | get { 248 | return ResourceManager.GetString("Talk_To_Agent", resourceCulture); 249 | } 250 | } 251 | 252 | /// 253 | /// Looks up a localized string similar to Thank you!. 254 | /// 255 | public static string Thank_You { 256 | get { 257 | return ResourceManager.GetString("Thank_You", resourceCulture); 258 | } 259 | } 260 | 261 | /// 262 | /// Looks up a localized string similar to I searched for your orders in the past 1 month. Below are the orders that have been delivered to you. Which one are you asking about?. 263 | /// 264 | public static string Which_Delivered { 265 | get { 266 | return ResourceManager.GetString("Which_Delivered", resourceCulture); 267 | } 268 | } 269 | 270 | /// 271 | /// Looks up a localized string similar to I searched for your orders in the past 1 month. Below are the orders that have not been delivered yet. Which one are you asking about?. 272 | /// 273 | public static string Which_Undelivered { 274 | get { 275 | return ResourceManager.GetString("Which_Undelivered", resourceCulture); 276 | } 277 | } 278 | 279 | /// 280 | /// Looks up a localized string similar to Working on it.... 281 | /// 282 | public static string Working_On { 283 | get { 284 | return ResourceManager.GetString("Working_On", resourceCulture); 285 | } 286 | } 287 | 288 | /// 289 | /// Looks up a localized string similar to Yes . 290 | /// 291 | public static string Yes { 292 | get { 293 | return ResourceManager.GetString("Yes", resourceCulture); 294 | } 295 | } 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /BotFramework/Resources/Resource.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 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 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Sorry, something went wrong. Please retry later. 122 | 123 | 124 | Sorry, I didn't understand what you just said to me. 125 | 126 | 127 | Hi, how can I help you today? 128 | 129 | 130 | Bye! Have a good day! 131 | 132 | 133 | Sorry, I was not helpful. 134 | 135 | 136 | I have cancelled our last activity. 137 | 138 | 139 | Sorry, I don't have anything to cancel. 140 | 141 | 142 | You can ask me about delayed shipments, broken items, etc. 143 | 144 | 145 | To talk to a human agent, please call (123)456-7890. 146 | 147 | 148 | Is there anything else I can help you with? 149 | 150 | 151 | I can help to search for your order. Can you provide your customer Id? 152 | 153 | 154 | Working on it... 155 | 156 | 157 | I don't see any other undelivered orders. Would you like to talk to an agent? 158 | 159 | 160 | I searched for your orders in the past 1 month. Below are the orders that have not been delivered yet. Which one are you asking about? 161 | 162 | 163 | I searched for your orders in the past 1 month. Below are the orders that have been delivered to you. Which one are you asking about? 164 | 165 | 166 | None of above 167 | 168 | 169 | Your order is not delivered because it is {0}. Do you want me to cancel this one and order from another supplier for you with no extra shipping fee? 170 | 171 | 172 | Yes 173 | 174 | 175 | No 176 | 177 | 178 | Thank you! 179 | 180 | 181 | Your new order has been placed with order number {0}. It is expected to deliver on {1}. Details have been sent to your email. 182 | 183 | 184 | Your order with order number {0} has been cancelled. You can print your return label at {1}. Details have been sent to your email. 185 | 186 | 187 | Can you describe the damage? 188 | 189 | 190 | I've made a note of the damage. Do you want me to return or replace this item? 191 | 192 | 193 | Return 194 | 195 | 196 | Replace 197 | 198 | -------------------------------------------------------------------------------- /BotFramework/Resources/Resources.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | UiPath.ChatbotSamples.BotFramework.Resources 6 | UiPath.ChatbotSamples.BotFramework.Resources 7 | 8 | 9 | 10 | 11 | True 12 | True 13 | Resource.resx 14 | 15 | 16 | 17 | 18 | 19 | PublicResXFileCodeGenerator 20 | Resource.Designer.cs 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /BotFramework/SampleBot.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.489 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleBot", "SampleBot\SampleBot.csproj", "{42280CCA-E5A4-4603-8A4B-D2716C8F7C7E}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dialogs", "Dialogs\Dialogs.csproj", "{8CE56832-D037-4633-A33A-00ABEBD031AD}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common", "Common\Common.csproj", "{DB636AB4-2E98-46D7-A252-F8740CAE72F8}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Actions", "Actions\Actions.csproj", "{3FBC4D71-8A16-40A7-82E9-5D5D74F6C8A6}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchestratorClient", "OrchestratorClient\OrchestratorClient.csproj", "{5B70CB3A-BDF0-4FD7-94E0-EC696B1077DF}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utils", "Utils\Utils.csproj", "{61FA8EE7-6EB2-4CBA-9E6E-4A0DD3E76031}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Resources", "Resources\Resources.csproj", "{F9BF4AF8-A5B2-4832-81DB-FEBED1F32A6E}" 19 | EndProject 20 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{99F836B8-C9D3-4E98-BBA1-2821996E9803}" 21 | EndProject 22 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Utils.Test", "Utils.Test\Utils.Test.csproj", "{3C7EEB10-0ECC-4A15-95B6-0EE6D2A0EAE6}" 23 | EndProject 24 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrchestratorClient.Test", "OrchestratorClient.Test\OrchestratorClient.Test.csproj", "{4111B415-EC6E-4802-B87F-44414FF6B51E}" 25 | EndProject 26 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DialogsTest", "DialogsTest\DialogsTest.csproj", "{C2AA898A-3BE0-4A92-A012-8C92A413F50E}" 27 | EndProject 28 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test.Common", "Test.Common\Test.Common.csproj", "{01DACA10-1C6F-4BD2-B3A6-83AC5DA7DBC3}" 29 | EndProject 30 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActionsTest", "ActionsTest\ActionsTest.csproj", "{CD291199-F8BE-4D88-AAB3-7A92BCE6081B}" 31 | EndProject 32 | Global 33 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 34 | Debug|Any CPU = Debug|Any CPU 35 | Release|Any CPU = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 38 | {42280CCA-E5A4-4603-8A4B-D2716C8F7C7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {42280CCA-E5A4-4603-8A4B-D2716C8F7C7E}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {42280CCA-E5A4-4603-8A4B-D2716C8F7C7E}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {42280CCA-E5A4-4603-8A4B-D2716C8F7C7E}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {8CE56832-D037-4633-A33A-00ABEBD031AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {8CE56832-D037-4633-A33A-00ABEBD031AD}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {8CE56832-D037-4633-A33A-00ABEBD031AD}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {8CE56832-D037-4633-A33A-00ABEBD031AD}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {DB636AB4-2E98-46D7-A252-F8740CAE72F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {DB636AB4-2E98-46D7-A252-F8740CAE72F8}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {DB636AB4-2E98-46D7-A252-F8740CAE72F8}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {DB636AB4-2E98-46D7-A252-F8740CAE72F8}.Release|Any CPU.Build.0 = Release|Any CPU 50 | {3FBC4D71-8A16-40A7-82E9-5D5D74F6C8A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {3FBC4D71-8A16-40A7-82E9-5D5D74F6C8A6}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {3FBC4D71-8A16-40A7-82E9-5D5D74F6C8A6}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {3FBC4D71-8A16-40A7-82E9-5D5D74F6C8A6}.Release|Any CPU.Build.0 = Release|Any CPU 54 | {5B70CB3A-BDF0-4FD7-94E0-EC696B1077DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 55 | {5B70CB3A-BDF0-4FD7-94E0-EC696B1077DF}.Debug|Any CPU.Build.0 = Debug|Any CPU 56 | {5B70CB3A-BDF0-4FD7-94E0-EC696B1077DF}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {5B70CB3A-BDF0-4FD7-94E0-EC696B1077DF}.Release|Any CPU.Build.0 = Release|Any CPU 58 | {61FA8EE7-6EB2-4CBA-9E6E-4A0DD3E76031}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 59 | {61FA8EE7-6EB2-4CBA-9E6E-4A0DD3E76031}.Debug|Any CPU.Build.0 = Debug|Any CPU 60 | {61FA8EE7-6EB2-4CBA-9E6E-4A0DD3E76031}.Release|Any CPU.ActiveCfg = Release|Any CPU 61 | {61FA8EE7-6EB2-4CBA-9E6E-4A0DD3E76031}.Release|Any CPU.Build.0 = Release|Any CPU 62 | {F9BF4AF8-A5B2-4832-81DB-FEBED1F32A6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 63 | {F9BF4AF8-A5B2-4832-81DB-FEBED1F32A6E}.Debug|Any CPU.Build.0 = Debug|Any CPU 64 | {F9BF4AF8-A5B2-4832-81DB-FEBED1F32A6E}.Release|Any CPU.ActiveCfg = Release|Any CPU 65 | {F9BF4AF8-A5B2-4832-81DB-FEBED1F32A6E}.Release|Any CPU.Build.0 = Release|Any CPU 66 | {3C7EEB10-0ECC-4A15-95B6-0EE6D2A0EAE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 67 | {3C7EEB10-0ECC-4A15-95B6-0EE6D2A0EAE6}.Debug|Any CPU.Build.0 = Debug|Any CPU 68 | {3C7EEB10-0ECC-4A15-95B6-0EE6D2A0EAE6}.Release|Any CPU.ActiveCfg = Release|Any CPU 69 | {3C7EEB10-0ECC-4A15-95B6-0EE6D2A0EAE6}.Release|Any CPU.Build.0 = Release|Any CPU 70 | {4111B415-EC6E-4802-B87F-44414FF6B51E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 71 | {4111B415-EC6E-4802-B87F-44414FF6B51E}.Debug|Any CPU.Build.0 = Debug|Any CPU 72 | {4111B415-EC6E-4802-B87F-44414FF6B51E}.Release|Any CPU.ActiveCfg = Release|Any CPU 73 | {4111B415-EC6E-4802-B87F-44414FF6B51E}.Release|Any CPU.Build.0 = Release|Any CPU 74 | {C2AA898A-3BE0-4A92-A012-8C92A413F50E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 75 | {C2AA898A-3BE0-4A92-A012-8C92A413F50E}.Debug|Any CPU.Build.0 = Debug|Any CPU 76 | {C2AA898A-3BE0-4A92-A012-8C92A413F50E}.Release|Any CPU.ActiveCfg = Release|Any CPU 77 | {C2AA898A-3BE0-4A92-A012-8C92A413F50E}.Release|Any CPU.Build.0 = Release|Any CPU 78 | {01DACA10-1C6F-4BD2-B3A6-83AC5DA7DBC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 79 | {01DACA10-1C6F-4BD2-B3A6-83AC5DA7DBC3}.Debug|Any CPU.Build.0 = Debug|Any CPU 80 | {01DACA10-1C6F-4BD2-B3A6-83AC5DA7DBC3}.Release|Any CPU.ActiveCfg = Release|Any CPU 81 | {01DACA10-1C6F-4BD2-B3A6-83AC5DA7DBC3}.Release|Any CPU.Build.0 = Release|Any CPU 82 | {CD291199-F8BE-4D88-AAB3-7A92BCE6081B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 83 | {CD291199-F8BE-4D88-AAB3-7A92BCE6081B}.Debug|Any CPU.Build.0 = Debug|Any CPU 84 | {CD291199-F8BE-4D88-AAB3-7A92BCE6081B}.Release|Any CPU.ActiveCfg = Release|Any CPU 85 | {CD291199-F8BE-4D88-AAB3-7A92BCE6081B}.Release|Any CPU.Build.0 = Release|Any CPU 86 | EndGlobalSection 87 | GlobalSection(SolutionProperties) = preSolution 88 | HideSolutionNode = FALSE 89 | EndGlobalSection 90 | GlobalSection(NestedProjects) = preSolution 91 | {3C7EEB10-0ECC-4A15-95B6-0EE6D2A0EAE6} = {99F836B8-C9D3-4E98-BBA1-2821996E9803} 92 | {4111B415-EC6E-4802-B87F-44414FF6B51E} = {99F836B8-C9D3-4E98-BBA1-2821996E9803} 93 | {C2AA898A-3BE0-4A92-A012-8C92A413F50E} = {99F836B8-C9D3-4E98-BBA1-2821996E9803} 94 | {01DACA10-1C6F-4BD2-B3A6-83AC5DA7DBC3} = {99F836B8-C9D3-4E98-BBA1-2821996E9803} 95 | {CD291199-F8BE-4D88-AAB3-7A92BCE6081B} = {99F836B8-C9D3-4E98-BBA1-2821996E9803} 96 | EndGlobalSection 97 | GlobalSection(ExtensibilityGlobals) = postSolution 98 | SolutionGuid = {F24F4025-388A-4FB9-954F-B47A559FB2E7} 99 | EndGlobalSection 100 | EndGlobal 101 | -------------------------------------------------------------------------------- /BotFramework/SampleBot/ConfigurationCredentialProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | // 4 | // Generated with Bot Builder V4 SDK Template for Visual Studio EmptyBot v4.3.0 5 | 6 | using Microsoft.Bot.Connector.Authentication; 7 | using Microsoft.Extensions.Configuration; 8 | 9 | namespace UiPath.ChatbotSamples.BotFramework.Bot 10 | { 11 | public class ConfigurationCredentialProvider : SimpleCredentialProvider 12 | { 13 | public ConfigurationCredentialProvider(IConfiguration configuration) 14 | : base(configuration["MicrosoftAppId"], configuration["MicrosoftAppPassword"]) 15 | { 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /BotFramework/SampleBot/Controllers/BotController.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | // 4 | // Generated with Bot Builder V4 SDK Template for Visual Studio EmptyBot v4.3.0 5 | 6 | using System.Threading.Tasks; 7 | using Microsoft.AspNetCore.Mvc; 8 | using Microsoft.Bot.Builder; 9 | using Microsoft.Bot.Builder.Integration.AspNet.Core; 10 | 11 | namespace UiPath.ChatbotSamples.BotFramework.Bot.Controllers 12 | { 13 | // This ASP Controller is created to handle a request. Dependency Injection will provide the Adapter and IBot 14 | // implementation at runtime. Multiple different IBot implementations running at different endpoints can be 15 | // achieved by specifying a more specific type for the bot constructor argument. 16 | [Route("api/messages")] 17 | [ApiController] 18 | public class BotController : ControllerBase 19 | { 20 | private readonly IBotFrameworkHttpAdapter Adapter; 21 | private readonly IBot Bot; 22 | 23 | public BotController(IBotFrameworkHttpAdapter adapter, IBot bot) 24 | { 25 | Adapter = adapter; 26 | Bot = bot; 27 | } 28 | 29 | [HttpPost] 30 | public async Task PostAsync() 31 | { 32 | // Delegate the processing of the HTTP POST to the adapter. 33 | // The adapter will invoke the bot. 34 | await Adapter.ProcessAsync(Request, Response, Bot); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /BotFramework/SampleBot/DeploymentTemplates/template-with-new-rg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "groupLocation": { 6 | "type": "string", 7 | "metadata": { 8 | "description": "Specifies the location of the Resource Group." 9 | } 10 | }, 11 | "groupName": { 12 | "type": "string", 13 | "metadata": { 14 | "description": "Specifies the name of the Resource Group." 15 | } 16 | }, 17 | "appId": { 18 | "type": "string", 19 | "metadata": { 20 | "description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings." 21 | } 22 | }, 23 | "appSecret": { 24 | "type": "string", 25 | "metadata": { 26 | "description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings." 27 | } 28 | }, 29 | "botId": { 30 | "type": "string", 31 | "metadata": { 32 | "description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable." 33 | } 34 | }, 35 | "botSku": { 36 | "type": "string", 37 | "metadata": { 38 | "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1." 39 | } 40 | }, 41 | "newAppServicePlanName": { 42 | "type": "string", 43 | "metadata": { 44 | "description": "The name of the App Service Plan." 45 | } 46 | }, 47 | "newAppServicePlanSku": { 48 | "type": "object", 49 | "defaultValue": { 50 | "name": "S1", 51 | "tier": "Standard", 52 | "size": "S1", 53 | "family": "S", 54 | "capacity": 1 55 | }, 56 | "metadata": { 57 | "description": "The SKU of the App Service Plan. Defaults to Standard values." 58 | } 59 | }, 60 | "newAppServicePlanLocation": { 61 | "type": "string", 62 | "metadata": { 63 | "description": "The location of the App Service Plan. Defaults to \"westus\"." 64 | } 65 | }, 66 | "newWebAppName": { 67 | "type": "string", 68 | "defaultValue": "", 69 | "metadata": { 70 | "description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"." 71 | } 72 | } 73 | }, 74 | "variables": { 75 | "appServicePlanName": "[parameters('newAppServicePlanName')]", 76 | "resourcesLocation": "[parameters('newAppServicePlanLocation')]", 77 | "webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]", 78 | "siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]", 79 | "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]" 80 | }, 81 | "resources": [ 82 | { 83 | "name": "[parameters('groupName')]", 84 | "type": "Microsoft.Resources/resourceGroups", 85 | "apiVersion": "2018-05-01", 86 | "location": "[parameters('groupLocation')]", 87 | "properties": { 88 | } 89 | }, 90 | { 91 | "type": "Microsoft.Resources/deployments", 92 | "apiVersion": "2018-05-01", 93 | "name": "storageDeployment", 94 | "resourceGroup": "[parameters('groupName')]", 95 | "dependsOn": [ 96 | "[resourceId('Microsoft.Resources/resourceGroups/', parameters('groupName'))]" 97 | ], 98 | "properties": { 99 | "mode": "Incremental", 100 | "template": { 101 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 102 | "contentVersion": "1.0.0.0", 103 | "parameters": {}, 104 | "variables": {}, 105 | "resources": [ 106 | { 107 | "comments": "Create a new App Service Plan", 108 | "type": "Microsoft.Web/serverfarms", 109 | "name": "[variables('appServicePlanName')]", 110 | "apiVersion": "2018-02-01", 111 | "location": "[variables('resourcesLocation')]", 112 | "sku": "[parameters('newAppServicePlanSku')]", 113 | "properties": { 114 | "name": "[variables('appServicePlanName')]" 115 | } 116 | }, 117 | { 118 | "comments": "Create a Web App using the new App Service Plan", 119 | "type": "Microsoft.Web/sites", 120 | "apiVersion": "2015-08-01", 121 | "location": "[variables('resourcesLocation')]", 122 | "kind": "app", 123 | "dependsOn": [ 124 | "[resourceId('Microsoft.Web/serverfarms/', variables('appServicePlanName'))]" 125 | ], 126 | "name": "[variables('webAppName')]", 127 | "properties": { 128 | "name": "[variables('webAppName')]", 129 | "serverFarmId": "[variables('appServicePlanName')]", 130 | "siteConfig": { 131 | "appSettings": [ 132 | { 133 | "name": "WEBSITE_NODE_DEFAULT_VERSION", 134 | "value": "10.14.1" 135 | }, 136 | { 137 | "name": "MicrosoftAppId", 138 | "value": "[parameters('appId')]" 139 | }, 140 | { 141 | "name": "MicrosoftAppPassword", 142 | "value": "[parameters('appSecret')]" 143 | } 144 | ], 145 | "cors": { 146 | "allowedOrigins": [ 147 | "https://botservice.hosting.portal.azure.net", 148 | "https://hosting.onecloud.azure-test.net/" 149 | ] 150 | } 151 | } 152 | } 153 | }, 154 | { 155 | "apiVersion": "2017-12-01", 156 | "type": "Microsoft.BotService/botServices", 157 | "name": "[parameters('botId')]", 158 | "location": "global", 159 | "kind": "bot", 160 | "sku": { 161 | "name": "[parameters('botSku')]" 162 | }, 163 | "properties": { 164 | "name": "[parameters('botId')]", 165 | "displayName": "[parameters('botId')]", 166 | "endpoint": "[variables('botEndpoint')]", 167 | "msaAppId": "[parameters('appId')]", 168 | "developerAppInsightsApplicationId": null, 169 | "developerAppInsightKey": null, 170 | "publishingCredentials": null, 171 | "storageResourceId": null 172 | }, 173 | "dependsOn": [ 174 | "[resourceId('Microsoft.Web/sites/', variables('webAppName'))]" 175 | ] 176 | } 177 | ], 178 | "outputs": {} 179 | } 180 | } 181 | } 182 | ] 183 | } -------------------------------------------------------------------------------- /BotFramework/SampleBot/DeploymentTemplates/template-with-preexisting-rg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "appId": { 6 | "type": "string", 7 | "metadata": { 8 | "description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings." 9 | } 10 | }, 11 | "appSecret": { 12 | "type": "string", 13 | "metadata": { 14 | "description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings. Defaults to \"\"." 15 | } 16 | }, 17 | "botId": { 18 | "type": "string", 19 | "metadata": { 20 | "description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable." 21 | } 22 | }, 23 | "botSku": { 24 | "defaultValue": "F0", 25 | "type": "string", 26 | "metadata": { 27 | "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1." 28 | } 29 | }, 30 | "newAppServicePlanName": { 31 | "type": "string", 32 | "defaultValue": "", 33 | "metadata": { 34 | "description": "The name of the new App Service Plan." 35 | } 36 | }, 37 | "newAppServicePlanSku": { 38 | "type": "object", 39 | "defaultValue": { 40 | "name": "S1", 41 | "tier": "Standard", 42 | "size": "S1", 43 | "family": "S", 44 | "capacity": 1 45 | }, 46 | "metadata": { 47 | "description": "The SKU of the App Service Plan. Defaults to Standard values." 48 | } 49 | }, 50 | "appServicePlanLocation": { 51 | "type": "string", 52 | "metadata": { 53 | "description": "The location of the App Service Plan." 54 | } 55 | }, 56 | "existingAppServicePlan": { 57 | "type": "string", 58 | "defaultValue": "", 59 | "metadata": { 60 | "description": "Name of the existing App Service Plan used to create the Web App for the bot." 61 | } 62 | }, 63 | "newWebAppName": { 64 | "type": "string", 65 | "defaultValue": "", 66 | "metadata": { 67 | "description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"." 68 | } 69 | } 70 | }, 71 | "variables": { 72 | "defaultAppServicePlanName": "[if(empty(parameters('existingAppServicePlan')), 'createNewAppServicePlan', parameters('existingAppServicePlan'))]", 73 | "useExistingAppServicePlan": "[not(equals(variables('defaultAppServicePlanName'), 'createNewAppServicePlan'))]", 74 | "servicePlanName": "[if(variables('useExistingAppServicePlan'), parameters('existingAppServicePlan'), parameters('newAppServicePlanName'))]", 75 | "resourcesLocation": "[parameters('appServicePlanLocation')]", 76 | "webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]", 77 | "siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]", 78 | "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]" 79 | }, 80 | "resources": [ 81 | { 82 | "comments": "Create a new App Service Plan if no existing App Service Plan name was passed in.", 83 | "type": "Microsoft.Web/serverfarms", 84 | "condition": "[not(variables('useExistingAppServicePlan'))]", 85 | "name": "[variables('servicePlanName')]", 86 | "apiVersion": "2018-02-01", 87 | "location": "[variables('resourcesLocation')]", 88 | "sku": "[parameters('newAppServicePlanSku')]", 89 | "properties": { 90 | "name": "[variables('servicePlanName')]" 91 | } 92 | }, 93 | { 94 | "comments": "Create a Web App using an App Service Plan", 95 | "type": "Microsoft.Web/sites", 96 | "apiVersion": "2015-08-01", 97 | "location": "[variables('resourcesLocation')]", 98 | "kind": "app", 99 | "dependsOn": [ 100 | "[resourceId('Microsoft.Web/serverfarms/', variables('servicePlanName'))]" 101 | ], 102 | "name": "[variables('webAppName')]", 103 | "properties": { 104 | "name": "[variables('webAppName')]", 105 | "serverFarmId": "[variables('servicePlanName')]", 106 | "siteConfig": { 107 | "appSettings": [ 108 | { 109 | "name": "WEBSITE_NODE_DEFAULT_VERSION", 110 | "value": "10.14.1" 111 | }, 112 | { 113 | "name": "MicrosoftAppId", 114 | "value": "[parameters('appId')]" 115 | }, 116 | { 117 | "name": "MicrosoftAppPassword", 118 | "value": "[parameters('appSecret')]" 119 | } 120 | ], 121 | "cors": { 122 | "allowedOrigins": [ 123 | "https://botservice.hosting.portal.azure.net", 124 | "https://hosting.onecloud.azure-test.net/" 125 | ] 126 | } 127 | } 128 | } 129 | }, 130 | { 131 | "apiVersion": "2017-12-01", 132 | "type": "Microsoft.BotService/botServices", 133 | "name": "[parameters('botId')]", 134 | "location": "global", 135 | "kind": "bot", 136 | "sku": { 137 | "name": "[parameters('botSku')]" 138 | }, 139 | "properties": { 140 | "name": "[parameters('botId')]", 141 | "displayName": "[parameters('botId')]", 142 | "endpoint": "[variables('botEndpoint')]", 143 | "msaAppId": "[parameters('appId')]", 144 | "developerAppInsightsApplicationId": null, 145 | "developerAppInsightKey": null, 146 | "publishingCredentials": null, 147 | "storageResourceId": null 148 | }, 149 | "dependsOn": [ 150 | "[resourceId('Microsoft.Web/sites/', variables('webAppName'))]" 151 | ] 152 | } 153 | ] 154 | } -------------------------------------------------------------------------------- /BotFramework/SampleBot/Program.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | // 4 | // Generated with Bot Builder V4 SDK Template for Visual Studio EmptyBot v4.3.0 5 | 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | 9 | namespace UiPath.ChatbotSamples.BotFramework.Bot 10 | { 11 | public class Program 12 | { 13 | public static void Main(string[] args) 14 | { 15 | CreateWebHostBuilder(args).Build().Run(); 16 | } 17 | 18 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 19 | WebHost.CreateDefaultBuilder(args) 20 | .UseStartup(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /BotFramework/SampleBot/SampleBot.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | UiPath.ChatbotSamples.BotFramework.Bot 6 | UiPath.ChatbotSamples.BotFramework.Bot 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Always 24 | 25 | 26 | 27 | 28 | 29 | Always 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /BotFramework/SampleBot/Startup.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | // 4 | // Generated with Bot Builder V4 SDK Template for Visual Studio EmptyBot v4.3.0 5 | 6 | using Microsoft.AspNetCore.Builder; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.AspNetCore.Mvc; 9 | using Microsoft.Bot.Builder; 10 | using Microsoft.Bot.Builder.Integration.AspNet.Core; 11 | using Microsoft.Bot.Configuration; 12 | using Microsoft.Bot.Connector.Authentication; 13 | using Microsoft.Extensions.Configuration; 14 | using Microsoft.Extensions.DependencyInjection; 15 | using System; 16 | using System.IO; 17 | using UiPath.ChatbotSamples.BotFramework.Actions; 18 | using UiPath.ChatbotSamples.BotFramework.Bot.Common; 19 | using UiPath.ChatbotSamples.BotFramework.Common; 20 | using UiPath.ChatbotSamples.BotFramework.Dialogs; 21 | using UiPath.ChatbotSamples.BotFramework.OrchestratorClient; 22 | using UiPath.ChatbotSamples.BotFramework.OrchestratorClient.Auth; 23 | 24 | namespace UiPath.ChatbotSamples.BotFramework.Bot 25 | { 26 | public class Startup 27 | { 28 | public Startup(IHostingEnvironment env) 29 | { 30 | var builder = new ConfigurationBuilder() 31 | .SetBasePath(env.ContentRootPath) 32 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) 33 | .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) 34 | .AddEnvironmentVariables(); 35 | 36 | Configuration = builder.Build(); 37 | } 38 | 39 | public IConfiguration Configuration { get; } 40 | 41 | // This method gets called by the runtime. Use this method to add services to the container. 42 | public void ConfigureServices(IServiceCollection services) 43 | { 44 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 45 | 46 | // Create the credential provider to be used with the Bot Framework Adapter. 47 | services.AddSingleton(); 48 | 49 | // Create the Bot Framework Adapter with error handling enabled. 50 | services.AddSingleton(); 51 | 52 | // Add Bot configuration. 53 | var botConfig = LoadBotConfiguration(); 54 | services.AddSingleton(sp => botConfig ?? throw new InvalidOperationException($"The .bot configuration file could not be loaded.")); 55 | 56 | // Add BotServices singleton. 57 | // Create the connected services from .bot file. 58 | services.AddSingleton(sp => new BotServices(botConfig)); 59 | 60 | // Memory Storage is for local bot debugging only. When the bot 61 | // is restarted, everything stored in memory will be gone. 62 | // For production bots use the Azure Blob or 63 | // Azure CosmosDB storage providers. 64 | IStorage dataStore = new MemoryStorage(); 65 | 66 | // Create and add conversation state. 67 | var conversationState = new ConversationState(dataStore); 68 | services.AddSingleton(conversationState); 69 | 70 | var userState = new UserState(dataStore); 71 | services.AddSingleton(userState); 72 | services.AddOptions(); 73 | 74 | //Inject settings and sericies. 75 | services.Configure(Configuration.GetSection("OrchestratorSettings")); 76 | services.AddSingleton(); 77 | services.AddSingleton(); 78 | services.AddSingleton(); 79 | 80 | // Inject bot. 81 | services.AddBot(); 82 | } 83 | 84 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 85 | public void Configure(IApplicationBuilder app, IHostingEnvironment env) 86 | { 87 | if (env.IsDevelopment()) 88 | { 89 | app.UseDeveloperExceptionPage(); 90 | } 91 | else 92 | { 93 | app.UseHsts(); 94 | } 95 | 96 | app.UseDefaultFiles(); 97 | app.UseStaticFiles(); 98 | 99 | app.UseMvc(); 100 | } 101 | 102 | private BotConfiguration LoadBotConfiguration() 103 | { 104 | var secretKey = Configuration.GetSection("botFileSecret")?.Value; 105 | var botFilePath = Configuration.GetSection("botFilePath")?.Value; 106 | if (!File.Exists(botFilePath)) 107 | { 108 | throw new FileNotFoundException($"The .bot configuration file was not found. botFilePath: {botFilePath}"); 109 | } 110 | 111 | // Loads .bot configuration file and adds a singleton that your Bot can access through dependency injection. 112 | BotConfiguration botConfig = null; 113 | try 114 | { 115 | botConfig = BotConfiguration.Load(botFilePath, secretKey); 116 | } 117 | catch 118 | { 119 | var msg = @"Error reading bot file. Please ensure you have valid botFilePath and botFileSecret set for your environment. 120 | - You can find the botFilePath and botFileSecret in the Azure App Service application settings. 121 | - If you are running this bot locally, consider adding a appsettings.json file with botFilePath and botFileSecret. 122 | - See https://aka.ms/about-bot-file to learn more about .bot file its use and bot configuration. 123 | "; 124 | throw new InvalidOperationException(msg); 125 | } 126 | 127 | return botConfig; 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /BotFramework/SampleBot/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /BotFramework/SampleBot/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "botFilePath": "samplebot.bot", 3 | "botFileSecret": null, 4 | "OrchestratorSettings": { 5 | "AuthMode": "Cloud", 6 | "RefreshToken": "", 7 | "ServiceInstanceLogicalName": "", 8 | "AccountLogicalName": "", 9 | "BaseUrl": "https://platform.uipath.com", 10 | "StatusCheckMaxRetry": 60, 11 | "StatusCheckInterval": 500, 12 | "Strategy": "Specific", 13 | "RobotIds": [ 123 ], 14 | "ProcessKeys": [ 15 | { 16 | "Process": "CreatePurchaseOrder", 17 | "Key": "" 18 | }, 19 | { 20 | "Process": "CreateSalesOrder", 21 | "Key": "" 22 | }, 23 | { 24 | "Process": "GetItems", 25 | "Key": "" 26 | }, 27 | { 28 | "Process": "CancelOrder", 29 | "Key": "" 30 | } 31 | ] 32 | } 33 | } -------------------------------------------------------------------------------- /BotFramework/SampleBot/samplebot.bot: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sample", 3 | "description": "", 4 | "services": [ 5 | { 6 | "type": "endpoint", 7 | "appId": "", 8 | "appPassword": "", 9 | "endpoint": "http://localhost:3979/api/messages", 10 | "id": "23a5a680-87c0-11e9-9c8b-ad4f3822eef1", 11 | "name": "http://localhost:3979/api/messages" 12 | }, 13 | { 14 | "type": "luis", 15 | "name": "Sample", 16 | "appId": "44dddc72-a17d-43e6-b972-2fff9098afd8", 17 | "authoringKey": "231bce45531f463bb77c8217e8534661", 18 | "version": "0.1", 19 | "region": "westus" 20 | } 21 | ], 22 | "padlock": "", 23 | "version": "2.0", 24 | "overrides": null 25 | } 26 | -------------------------------------------------------------------------------- /BotFramework/Test.Common/OrchestratorSettingOption.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Options; 2 | using System; 3 | using UiPath.ChatbotSamples.BotFramework.OrchestratorClient; 4 | 5 | namespace UiPath.ChatbotSamples.BotFramework.Test.Common 6 | { 7 | public class OrchestratorSettingOption : IOptionsMonitor 8 | { 9 | public OrchestratorSettingOption(OrchestratorSettings setting) 10 | { 11 | CurrentValue = setting; 12 | } 13 | 14 | public OrchestratorSettings CurrentValue { get; } 15 | 16 | public OrchestratorSettings Get(string name) 17 | { 18 | throw new NotImplementedException(); 19 | } 20 | 21 | public IDisposable OnChange(Action listener) 22 | { 23 | throw new NotImplementedException(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /BotFramework/Test.Common/Test.Common.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | UiPath.ChatbotSamples.BotFramework.Test.Common 6 | UiPath.ChatbotSamples.BotFramework.Test.Common 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /BotFramework/Utils.Test/CheckNullReferenceTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | 4 | namespace UiPath.ChatbotSamples.BotFramework.Utils.Test 5 | { 6 | public class CheckNullReferenceTest 7 | { 8 | [Fact] 9 | public void ReturnNullWhenNull() 10 | { 11 | TestType t = null; 12 | Assert.Throws(() => t.CheckNullReference()); 13 | } 14 | 15 | [Fact] 16 | public void ReturnValueWhenNotNull() 17 | { 18 | TestType origin = new TestType() 19 | { 20 | Test = "test string", 21 | }; 22 | var result = origin.CheckNullReference(); 23 | Assert.Equal(origin, result); 24 | Assert.Equal(origin.Test, result.Test); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /BotFramework/Utils.Test/OrdinalEqualTest.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace UiPath.ChatbotSamples.BotFramework.Utils.Test 4 | { 5 | public class OrdinalEqualTest 6 | { 7 | [Fact] 8 | public void EqualWhenSame() 9 | { 10 | var testString = "test string"; 11 | Assert.True(testString.OrdinalEquals(testString)); 12 | } 13 | 14 | [Fact] 15 | public void EqualWhenCaseDifferent() 16 | { 17 | var testString1 = "test string"; 18 | var testString2 = "Test String"; 19 | Assert.True(testString1.OrdinalEquals(testString2)); 20 | } 21 | 22 | [Fact] 23 | public void NotEqualWhenDifferent() 24 | { 25 | var testString1 = "test string 1"; 26 | var testString2 = "Test String 2"; 27 | Assert.False(testString1.OrdinalEquals(testString2)); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /BotFramework/Utils.Test/TestType.cs: -------------------------------------------------------------------------------- 1 | namespace UiPath.ChatbotSamples.BotFramework.Utils.Test 2 | { 3 | public class TestType 4 | { 5 | public string Test { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /BotFramework/Utils.Test/Utils.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | UiPath.ChatbotSamples.BotFramework.Utils.Test 6 | UiPath.ChatbotSamples.BotFramework.Utils.Test 7 | 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /BotFramework/Utils/CommonExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UiPath.ChatbotSamples.BotFramework.Utils 4 | { 5 | public static class CommonExtensions 6 | { 7 | public static T CheckNullReference(this T t) 8 | { 9 | if (t == null) 10 | { 11 | throw new ArgumentNullException(nameof(t)); 12 | } 13 | 14 | return t; 15 | } 16 | 17 | public static bool OrdinalEquals(this string s1, string s2) 18 | { 19 | return string.Equals(s1, s2, StringComparison.OrdinalIgnoreCase); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /BotFramework/Utils/Utils.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | UiPath.ChatbotSamples.BotFramework.Utils 6 | UiPath.ChatbotSamples.BotFramework.Utils 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Chatbot-Samples 2 | Welcome to the UIPath Chatbot samples repository. Here you will find example of using Microsoft BotFramework to build a chatbot that connects to UiPath RPA. 3 | -------------------------------------------------------------------------------- /User guide.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UiPath/Chatbot-Samples/6e4fb400456c725070e8d3691ba17827baaac7aa/User guide.pdf --------------------------------------------------------------------------------