├── .deployment ├── .gitignore ├── App_Start └── WebApiConfig.cs ├── Assets ├── BotChannelDirectLine.PNG ├── BotChannelPassword.PNG ├── BotChannelRegistration.PNG ├── BotChannelSettings.PNG ├── BotWebPage.PNG ├── BotWebPageVerify.PNG ├── CreateBotChannelRegistration.PNG ├── CreateQnAPair.PNG ├── CreateQnAService.PNG ├── CreateTranslatorTextAPI.PNG ├── DeployToAzure.PNG ├── DeployToAzureSuccess.PNG ├── GetQnAKeys.PNG ├── QnAPublish.PNG ├── TranslatorTextKeys.PNG ├── TranslatorTextRegistration.PNG ├── UpdateBotChannel.PNG └── VerifyAppSettings.PNG ├── Content └── botchat.css ├── Controllers ├── DirectLineController.cs └── MessagesController.cs ├── Dialogs ├── LanguageConst.cs ├── QandADialog.cs ├── RootDialog.cs └── SetLanguage.cs ├── Docs └── azure-deploy-parameters.md ├── EmergencyServicesBot.csproj ├── EmergencyServicesBot.sln ├── Global.asax ├── Global.asax.cs ├── LICENSE ├── PostDeployScripts ├── IncludeSources.targets ├── githubProject.json.template ├── prepareSrc.cmd ├── publish.cmd.template ├── publishProfile.xml.template ├── publishSettings.xml.template ├── runGulp.cmd ├── setupGithubRemoteRepo.cmd ├── setupVsoRemoteRepo.cmd └── vsoProject.json.template ├── Properties └── AssemblyInfo.cs ├── Resources ├── Resources.en-US.Designer.cs ├── Resources.en-US.resx ├── Resources.es-US.Designer.cs ├── Resources.es-US.resx ├── Resources.fr-FR.Designer.cs ├── Resources.fr-FR.resx ├── Resources.zh-CN.Designer.cs └── Resources.zh-CN.resx ├── Scripts └── botchat.js ├── Web.Debug.config ├── Web.Release.config ├── Web.config ├── azuredeploy.json ├── build.cmd ├── default.htm ├── deploy.cmd ├── packages.config ├── publish.cmd └── readme.md /.deployment: -------------------------------------------------------------------------------- 1 | [config] 2 | command = deploy.cmd -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | -------------------------------------------------------------------------------- /App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Serialization; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Web.Http; 7 | 8 | namespace EmergencyServicesBot 9 | { 10 | public static class WebApiConfig 11 | { 12 | public static void Register(HttpConfiguration config) 13 | { 14 | // Json settings 15 | config.Formatters.JsonFormatter.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; 16 | config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 17 | config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented; 18 | JsonConvert.DefaultSettings = () => new JsonSerializerSettings() 19 | { 20 | ContractResolver = new CamelCasePropertyNamesContractResolver(), 21 | Formatting = Newtonsoft.Json.Formatting.Indented, 22 | NullValueHandling = NullValueHandling.Ignore, 23 | }; 24 | 25 | // Web API configuration and services 26 | 27 | // Web API routes 28 | config.MapHttpAttributeRoutes(); 29 | 30 | config.Routes.MapHttpRoute( 31 | name: "DefaultApi", 32 | routeTemplate: "api/{controller}/{id}", 33 | defaults: new { id = RouteParameter.Optional } 34 | ); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Assets/BotChannelDirectLine.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTBox/EmergencyServicesBot/69e91f06c6e66d4c397978209c232d7371c95ec6/Assets/BotChannelDirectLine.PNG -------------------------------------------------------------------------------- /Assets/BotChannelPassword.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTBox/EmergencyServicesBot/69e91f06c6e66d4c397978209c232d7371c95ec6/Assets/BotChannelPassword.PNG -------------------------------------------------------------------------------- /Assets/BotChannelRegistration.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTBox/EmergencyServicesBot/69e91f06c6e66d4c397978209c232d7371c95ec6/Assets/BotChannelRegistration.PNG -------------------------------------------------------------------------------- /Assets/BotChannelSettings.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTBox/EmergencyServicesBot/69e91f06c6e66d4c397978209c232d7371c95ec6/Assets/BotChannelSettings.PNG -------------------------------------------------------------------------------- /Assets/BotWebPage.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTBox/EmergencyServicesBot/69e91f06c6e66d4c397978209c232d7371c95ec6/Assets/BotWebPage.PNG -------------------------------------------------------------------------------- /Assets/BotWebPageVerify.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTBox/EmergencyServicesBot/69e91f06c6e66d4c397978209c232d7371c95ec6/Assets/BotWebPageVerify.PNG -------------------------------------------------------------------------------- /Assets/CreateBotChannelRegistration.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTBox/EmergencyServicesBot/69e91f06c6e66d4c397978209c232d7371c95ec6/Assets/CreateBotChannelRegistration.PNG -------------------------------------------------------------------------------- /Assets/CreateQnAPair.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTBox/EmergencyServicesBot/69e91f06c6e66d4c397978209c232d7371c95ec6/Assets/CreateQnAPair.PNG -------------------------------------------------------------------------------- /Assets/CreateQnAService.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTBox/EmergencyServicesBot/69e91f06c6e66d4c397978209c232d7371c95ec6/Assets/CreateQnAService.PNG -------------------------------------------------------------------------------- /Assets/CreateTranslatorTextAPI.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTBox/EmergencyServicesBot/69e91f06c6e66d4c397978209c232d7371c95ec6/Assets/CreateTranslatorTextAPI.PNG -------------------------------------------------------------------------------- /Assets/DeployToAzure.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTBox/EmergencyServicesBot/69e91f06c6e66d4c397978209c232d7371c95ec6/Assets/DeployToAzure.PNG -------------------------------------------------------------------------------- /Assets/DeployToAzureSuccess.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTBox/EmergencyServicesBot/69e91f06c6e66d4c397978209c232d7371c95ec6/Assets/DeployToAzureSuccess.PNG -------------------------------------------------------------------------------- /Assets/GetQnAKeys.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTBox/EmergencyServicesBot/69e91f06c6e66d4c397978209c232d7371c95ec6/Assets/GetQnAKeys.PNG -------------------------------------------------------------------------------- /Assets/QnAPublish.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTBox/EmergencyServicesBot/69e91f06c6e66d4c397978209c232d7371c95ec6/Assets/QnAPublish.PNG -------------------------------------------------------------------------------- /Assets/TranslatorTextKeys.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTBox/EmergencyServicesBot/69e91f06c6e66d4c397978209c232d7371c95ec6/Assets/TranslatorTextKeys.PNG -------------------------------------------------------------------------------- /Assets/TranslatorTextRegistration.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTBox/EmergencyServicesBot/69e91f06c6e66d4c397978209c232d7371c95ec6/Assets/TranslatorTextRegistration.PNG -------------------------------------------------------------------------------- /Assets/UpdateBotChannel.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTBox/EmergencyServicesBot/69e91f06c6e66d4c397978209c232d7371c95ec6/Assets/UpdateBotChannel.PNG -------------------------------------------------------------------------------- /Assets/VerifyAppSettings.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTBox/EmergencyServicesBot/69e91f06c6e66d4c397978209c232d7371c95ec6/Assets/VerifyAppSettings.PNG -------------------------------------------------------------------------------- /Content/botchat.css: -------------------------------------------------------------------------------- 1 | /* reset */ 2 | body .wc-app, .wc-app button, .wc-app input, .wc-app textarea { 3 | font-family: "Segoe UI", sans-serif; 4 | font-size: 15px; 5 | } 6 | 7 | .wc-app button { 8 | background-color: #2AAA8A; 9 | border: 1px solid #cccccc; 10 | border-radius: 1px; 11 | color: #ffffff; 12 | cursor: pointer; 13 | transition: color .2s ease, background-color .2s ease; 14 | } 15 | 16 | .wc-app h1, .wc-app h2, .wc-app h3, .wc-app h4, .wc-app p, .wc-app ul, .wc-app ol { 17 | margin: 0; 18 | padding: 0; 19 | } 20 | 21 | .wc-app audio, .wc-app video { 22 | display: block; 23 | } 24 | 25 | /* docking */ 26 | .wc-hidden { 27 | visibility: hidden; 28 | } 29 | 30 | .wc-header { 31 | background-color: #2AAA8A; 32 | box-shadow: 0 1px rgba(0, 0, 0, 0.2); 33 | box-sizing: content-box; 34 | color: #ffffff; 35 | font-weight: 500; 36 | height: 30px; 37 | left: 0; 38 | letter-spacing: 0.5px; 39 | padding: 8px 8px 0 8px; 40 | position: absolute; 41 | right: 0; 42 | top: 0; 43 | z-index: 1; 44 | } 45 | 46 | .wc-time { 47 | color: #999999; 48 | margin-bottom: 10px; 49 | } 50 | 51 | .wc-message-groups { 52 | bottom: 50px; 53 | left: 0; 54 | transform: translateY(0); 55 | outline: 0; 56 | overflow-x: hidden; 57 | overflow-y: scroll; 58 | padding: 10px; 59 | position: absolute; 60 | right: 0; 61 | top: 38px; 62 | transition: transform 0.2s cubic-bezier(0, 0, 0.5, 1); 63 | } 64 | 65 | .wc-message-groups.no-header { 66 | top: 0; 67 | } 68 | 69 | .wc-message-group-content { 70 | overflow: hidden; 71 | } 72 | 73 | .wc-suggested-actions { 74 | background-color: #f9f9f9; 75 | bottom: 50px; 76 | height: 0; 77 | left: 0; 78 | overflow: hidden; 79 | position: absolute; 80 | right: 0; 81 | transition: height 0.2s cubic-bezier(0, 0, 0.5, 1); 82 | } 83 | 84 | .wc-suggested-actions .wc-hscroll > ul { 85 | height: 40px; 86 | padding: 2px 3px; 87 | } 88 | 89 | .wc-suggested-actions .wc-hscroll > ul > li { 90 | display: inline-block; 91 | margin: 2px; 92 | max-width: 40%; 93 | } 94 | 95 | .wc-suggested-actions .wc-hscroll > ul > li button { 96 | background-color: #fff; 97 | color: #2AAA8A; 98 | min-height: 32px; 99 | overflow: hidden; 100 | padding: 0 16px; 101 | text-overflow: ellipsis; 102 | white-space: nowrap; 103 | width: 100%; 104 | } 105 | 106 | .wc-suggested-actions .wc-hscroll > ul > li button:focus, 107 | .wc-suggested-actions .wc-hscroll > ul > li button:hover { 108 | background-color: #fff; 109 | border-color: #2AAA8A; 110 | color: #2AAA8A; 111 | } 112 | 113 | .wc-suggested-actions .wc-hscroll > ul > li button:active { 114 | background-color: #2AAA8A; 115 | border-color: #2AAA8A; 116 | color: #ffffff; 117 | } 118 | 119 | .wc-suggested-actions button.scroll { 120 | background-color: #d2dde5; 121 | height: 40px; 122 | overflow: hidden; 123 | padding: 0; 124 | position: absolute; 125 | top: 0; 126 | width: 28px; 127 | } 128 | 129 | .wc-suggested-actions button.scroll:disabled { 130 | display: none; 131 | } 132 | 133 | .wc-suggested-actions button.scroll:focus, 134 | .wc-suggested-actions button.scroll:hover { 135 | background-color: #808c95; 136 | } 137 | 138 | .wc-suggested-actions button.scroll svg { 139 | fill: #ffffff; 140 | } 141 | 142 | .wc-suggested-actions button.scroll svg path { 143 | transform: translateY(6px); 144 | } 145 | 146 | .wc-suggested-actions button.scroll.previous { 147 | left: 0; 148 | } 149 | 150 | .wc-suggested-actions button.scroll.next { 151 | right: 0; 152 | } 153 | 154 | .wc-message-pane.show-actions .wc-message-groups { 155 | transform: translateY(-40px); 156 | } 157 | 158 | .wc-message-pane.show-actions .wc-suggested-actions { 159 | height: 40px; 160 | } 161 | 162 | /* views */ 163 | .wc-chatview-panel { 164 | background-color: white; 165 | overflow: hidden; 166 | position: absolute; 167 | right: 0; 168 | left: 0; 169 | top: 0; 170 | bottom: 0; 171 | } 172 | 173 | /* messages */ 174 | .wc-message-wrapper { 175 | animation: animationFrames 2s; 176 | animation-iteration-count: 1; 177 | clear: both; 178 | margin-bottom: 10px; 179 | overflow: hidden; 180 | position: relative; 181 | /*transition: max-height 2s ease-in-out;*/ 182 | } 183 | 184 | @keyframes animationFrames { 185 | 0% { 186 | /*max-height: 0;*/ 187 | opacity: 0; 188 | } 189 | 190 | 20% { 191 | opacity: 1; 192 | } 193 | 194 | 100% { 195 | /*max-height: 2000px;*/ 196 | } 197 | } 198 | 199 | .wc-message { 200 | position: relative; 201 | } 202 | 203 | .wc-message-wrapper.carousel .wc-message { 204 | max-width: none; 205 | padding-right: 8px; 206 | } 207 | 208 | .wc-message svg.wc-message-callout { 209 | height: 22px; 210 | position: absolute; 211 | stroke: none; 212 | top: 12px; 213 | width: 6px; 214 | } 215 | 216 | .wc-message-content { 217 | border-radius: 2px; 218 | box-shadow: 0px 1px 1px 0px rgba(0, 0, 0, 0.2); 219 | padding: 8px; 220 | word-break: break-word; 221 | } 222 | 223 | .wc-message-content.clickable { 224 | cursor: pointer; 225 | } 226 | 227 | .wc-message-content.selected { 228 | box-shadow: 0px 1px 1px 0px #ffa333; 229 | } 230 | 231 | .wc-message-content img { 232 | max-height: 320px; 233 | max-width: 100%; 234 | } 235 | 236 | .wc-message-content .video iframe { 237 | border: 0; 238 | } 239 | 240 | .wc-message-content audio, .wc-message-content video { 241 | max-width: 100%; 242 | } 243 | 244 | .wc-message-content audio + h1, .wc-message-content video + h1 { 245 | margin-top: 11px; 246 | } 247 | 248 | .wc-message-from { 249 | clear: both; 250 | color: #767676; 251 | font-size: 11px; 252 | margin-top: 5px; 253 | } 254 | 255 | /* cards */ 256 | .wc-card { 257 | background-color: #ffffff; 258 | } 259 | 260 | .wc-card .non-adaptive-content { 261 | margin: 8px 8px 0 8px; 262 | } 263 | 264 | .wc-card button { 265 | background-color: transparent; 266 | color: #2AAA8A; 267 | min-height: 32px; 268 | width: 100%; 269 | padding: 0 16px; 270 | } 271 | 272 | .wc-card button:hover { 273 | background-color: transparent; 274 | border-color: #2AAA8A; 275 | color: #2AAA8A; 276 | } 277 | 278 | .wc-card button:active { 279 | background-color: #2AAA8A; 280 | border-color: #2AAA8A; 281 | color: #ffffff; 282 | } 283 | 284 | .wc-card.receipt table { 285 | border-collapse: collapse; 286 | width: 100%; 287 | } 288 | 289 | .wc-card.receipt th, .wc-card.receipt td { 290 | text-align: right; 291 | vertical-align: top; 292 | } 293 | 294 | .wc-card.receipt th:first-child, .wc-card.receipt td:first-child { 295 | text-align: left; 296 | } 297 | 298 | .wc-card.receipt th { 299 | color: #808c95; 300 | font-size: inherit; 301 | font-weight: normal; 302 | line-height: 1.75; 303 | } 304 | 305 | .wc-card.receipt thead tr:last-child th { 306 | padding-bottom: 16px; 307 | } 308 | 309 | .wc-card.receipt th[colspan="2"] { 310 | color: inherit; 311 | font-size: 15px; 312 | font-weight: 700; 313 | } 314 | 315 | .wc-card.receipt td { 316 | padding: 4px 8px 0 8px; 317 | } 318 | 319 | .wc-card.receipt td img { 320 | float: left; 321 | margin: 5px 8px 8px 0; 322 | max-height: 50px; 323 | max-width: 50px; 324 | } 325 | 326 | .wc-card.receipt div.title { 327 | font-weight: bolder; 328 | } 329 | 330 | .wc-card.receipt div.subtitle { 331 | font-weight: lighter; 332 | } 333 | 334 | .wc-card.receipt tbody tr, .wc-card.receipt tfoot tr { 335 | border-top: 1px solid #d2dde5; 336 | } 337 | 338 | .wc-card.receipt tbody tr:first-child, .wc-card.receipt tfoot tr:first-child { 339 | border-top-width: 2px; 340 | } 341 | 342 | .wc-card.receipt tfoot td { 343 | line-height: 2.25; 344 | } 345 | 346 | .wc-card.receipt tfoot .total { 347 | font-weight: bold; 348 | } 349 | 350 | .wc-card.thumbnail img { 351 | float: right; 352 | margin-bottom: 10px; 353 | margin-left: 10px; 354 | width: 100px; 355 | } 356 | 357 | .wc-card.signin h1 { 358 | margin: 10px 24px 16px 14px; 359 | } 360 | 361 | .wc-card.error { 362 | text-align: center; 363 | } 364 | 365 | .wc-card.error .error-icon { 366 | fill: #cccccc; 367 | height: 56px; 368 | margin-bottom: 2px; 369 | margin-top: 20px; 370 | padding-left: 12px; 371 | } 372 | 373 | .wc-card.error .error-text { 374 | color: #cccccc; 375 | font-weight: 600; 376 | letter-spacing: 0.5px; 377 | margin-bottom: 20px; 378 | text-align: inherit; 379 | } 380 | 381 | /* alternate chat sizes */ 382 | .wc-message { 383 | max-width: 91%; 384 | } 385 | 386 | .wc-card { 387 | border: 1px solid #d2dde5; 388 | width: 302px; 389 | } 390 | 391 | .wc-adaptive-card { 392 | width: 318px; 393 | } 394 | 395 | .wc-wide .wc-card { 396 | border: 1px solid #d2dde5; 397 | width: 398px; 398 | } 399 | 400 | .wc-wide .wc-adaptive-card { 401 | width: 414px; 402 | } 403 | 404 | .wc-narrow .wc-card { 405 | border: 1px solid #d2dde5; 406 | width: 198px; 407 | } 408 | 409 | .wc-narrow .wc-adaptive-card { 410 | width: 214px; 411 | } 412 | 413 | /* adaptive card adjustments from wc-card */ 414 | .wc-adaptive-card p { 415 | margin-left: 0; 416 | margin-right: 0; 417 | } 418 | 419 | /* list */ 420 | .wc-list > .wc-card { 421 | margin-top: 8px; 422 | } 423 | 424 | .wc-list > .wc-card:first-child { 425 | margin-top: 0; 426 | } 427 | 428 | /* horizontal scroll */ 429 | .wc-hscroll-outer { 430 | /* allow horizontal scrolling but hide the scrollbar */ 431 | overflow: hidden; 432 | } 433 | 434 | .wc-hscroll { 435 | /* allow horizontal scrolling but hide the scrollbar */ 436 | overflow-x: scroll; 437 | overflow-y: hidden; 438 | } 439 | 440 | .wc-hscroll > ul { 441 | white-space: nowrap; 442 | } 443 | 444 | .wc-hscroll > ul > li { 445 | display: inline-block; 446 | vertical-align: top; 447 | white-space: normal; 448 | } 449 | 450 | /* carousel */ 451 | .wc-carousel { 452 | position: relative; 453 | } 454 | 455 | .wc-carousel button.scroll { 456 | background-color: #d2dde5; 457 | height: 28px; 458 | overflow: hidden; 459 | padding: 0; 460 | position: absolute; 461 | top: 50%; 462 | width: 28px; 463 | } 464 | 465 | .wc-carousel button.scroll:disabled { 466 | display: none; 467 | } 468 | 469 | .wc-carousel button.scroll:focus, 470 | .wc-carousel button.scroll:hover { 471 | background-color: #808c95; 472 | } 473 | 474 | .wc-carousel button.scroll svg { 475 | fill: #ffffff; 476 | } 477 | 478 | .wc-carousel button.scroll.previous { 479 | left: -16px; 480 | } 481 | 482 | .wc-carousel button.scroll.next { 483 | right: -16px; 484 | } 485 | 486 | .wc-carousel .wc-hscroll > ul { 487 | margin-left: -4px; 488 | } 489 | 490 | .wc-carousel .wc-hscroll > ul > li { 491 | padding: 0 4px; 492 | } 493 | 494 | .wc-carousel .wc-hscroll > ul > li:last-child { 495 | padding-right: 0; 496 | } 497 | 498 | .wc-carousel li p { 499 | min-height: 4em; 500 | white-space: normal; 501 | } 502 | 503 | .wc-carousel li .wc-adaptive-card p { 504 | min-height: initial; 505 | } 506 | 507 | /* from me */ 508 | .wc-message-from-me { 509 | float: right; 510 | margin-right: 6px; 511 | } 512 | 513 | .wc-message-from-me.wc-message-from { 514 | text-align: right; 515 | } 516 | 517 | .wc-message-from-me .wc-message-content { 518 | background-color: #2AAA8A; 519 | color: #ffffff; 520 | } 521 | 522 | .wc-message-from-me svg.wc-message-callout path { 523 | fill: #2AAA8A; 524 | } 525 | 526 | .wc-message-from-me svg.wc-message-callout path.point-left { 527 | display: none; 528 | } 529 | 530 | .wc-message-from-me svg.wc-message-callout { 531 | right: -6px; 532 | } 533 | 534 | /* from bot */ 535 | .wc-message-from-bot { 536 | float: left; 537 | margin-left: 8px; 538 | } 539 | 540 | .wc-message-from-bot .wc-message-content { 541 | background-color: #eceff1; 542 | color: #000000; 543 | } 544 | 545 | .wc-message-from-bot svg.wc-message-callout path { 546 | fill: #eceff1; 547 | } 548 | 549 | .wc-message-from-bot svg.wc-message-callout path.point-right { 550 | display: none; 551 | } 552 | 553 | .wc-message-from-bot svg.wc-message-callout { 554 | left: -6px; 555 | } 556 | 557 | /* console */ 558 | .wc-console { 559 | border: 5px solid #dbdee1; 560 | bottom: 0; 561 | box-sizing: border-box; 562 | height: 50px; 563 | left: 0; 564 | position: absolute; 565 | right: 0; 566 | } 567 | 568 | .wc-console > .wc-upload, 569 | .wc-console > .wc-textbox, 570 | .wc-console > .wc-send, 571 | .wc-console > .wc-mic { 572 | position: absolute; 573 | top: 0; 574 | vertical-align: middle; 575 | } 576 | 577 | .wc-console label, .wc-console button { 578 | cursor: pointer; 579 | display: inline-block; 580 | height: 40px; 581 | } 582 | 583 | .wc-console svg { 584 | fill: #8a8a8a; 585 | margin: 11px; 586 | } 587 | 588 | .wc-console input[type=text], 589 | .wc-console textarea { 590 | border: none; 591 | height: 100%; 592 | outline: none; 593 | padding: 0; 594 | resize: none; 595 | width: 100%; 596 | } 597 | 598 | .wc-console.has-text .wc-send svg { 599 | fill: #2AAA8A; 600 | } 601 | 602 | .wc-console .wc-upload { 603 | cursor: pointer; 604 | position: relative; 605 | } 606 | 607 | .wc-console .wc-upload svg { 608 | height: 18px; 609 | width: 26px; 610 | } 611 | 612 | .wc-console #wc-upload-input { 613 | font-size: 0; 614 | height: 0; 615 | left: 0; 616 | opacity: 0; 617 | outline: 0; 618 | position: absolute; 619 | top: 0; 620 | width: 0; 621 | } 622 | 623 | .wc-console .wc-send { 624 | right: 0; 625 | } 626 | 627 | .wc-console .wc-send.hidden { 628 | visibility: hidden; 629 | } 630 | 631 | .wc-console .wc-textbox { 632 | bottom: 0; 633 | left: 48px; 634 | right: 49px; 635 | } 636 | 637 | .wc-console .wc-textbox input { 638 | background-color: transparent; 639 | } 640 | 641 | .wc-console .wc-mic, 642 | .wc-console .wc-send { 643 | background-color: transparent; 644 | border: 0; 645 | padding: 0; 646 | right: 0; 647 | } 648 | 649 | .wc-console .wc-mic.hidden, 650 | .wc-console .wc-send.hidden { 651 | visibility: hidden; 652 | } 653 | 654 | .wc-console .wc-send svg { 655 | height: 18px; 656 | width: 27px; 657 | } 658 | 659 | .wc-console .wc-mic.active path#micFilling { 660 | fill: #4e3787; 661 | } 662 | 663 | .wc-console .wc-mic.inactive path#micFilling { 664 | visibility: hidden; 665 | } 666 | 667 | .wc-console.has-text .wc-send svg { 668 | fill: #2AAA8A; 669 | } 670 | 671 | /* animation */ 672 | .wc-typing { 673 | background-image: url("data:image/gif;base64,R0lGODlhQAAYAPYBAOzv8evu8Ort7+fq7Ons7ujr7eXo6uTn6ebp6+Xn6ebo6uzu8OPm6OTm6OPm5+Tn6N/i4+Ll59/i5N7h4+Hk5uDj5evu7+Hk5d/h49PV18PFx7/BwsfJysXHyMLExdja3Nfa28vNz72/wL7Awc/S08TGyMDCw9TW2NbY2t3g4trd39bZ2szO0M7Q0dnb3djb3Nvd39ve4Nnc3dze4Nrc3t7g4tzf4dXX2d3f4d7h4tnc3tve383P0MrMzs7Q0sjKzNLU1s/R08jKy9DT1NfZ293g4efp68bIyby+v9bZ27q8vdHT1c7R0uvt78nLzM/R0tjb3ens7bO0tbS2t7GztK+xsrW3uK6vsLe4utfa3L/Awtzf4MnLzamqq5WWl66wsbm7vNrd3uXo6a2ur6yurp2en6KjpKusrZ+goKeoqers7urt7peXmIGBgYSEhHx8fJmamqipqnZ2doqLi8XHyY2NjpGSkpOUlJiYmZOTlI+QkJqbm4eIiJucnIuMjP///yH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTM4IDc5LjE1OTgyNCwgMjAxNi8wOS8xNC0wMTowOTowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTcgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MEY0RUJCMDNENkM4MTFFNkI5RENGRDgzMjAyQjU3QzUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MEY0RUJCMDRENkM4MTFFNkI5RENGRDgzMjAyQjU3QzUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowRjRFQkIwMUQ2QzgxMUU2QjlEQ0ZEODMyMDJCNTdDNSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowRjRFQkIwMkQ2QzgxMUU2QjlEQ0ZEODMyMDJCNTdDNSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgH//v38+/r5+Pf29fTz8vHw7+7t7Ovq6ejn5uXk4+Lh4N/e3dzb2tnY19bV1NPS0dDPzs3My8rJyMfGxcTDwsHAv769vLu6ubi3trW0s7KxsK+urayrqqmop6alpKOioaCfnp2cm5qZmJeWlZSTkpGQj46NjIuKiYiHhoWEg4KBgH9+fXx7enl4d3Z1dHNycXBvbm1sa2ppaGdmZWRjYmFgX15dXFtaWVhXVlVUU1JRUE9OTUxLSklIR0ZFRENCQUA/Pj08Ozo5ODc2NTQzMjEwLy4tLCsqKSgnJiUkIyIhIB8eHRwbGhkYFxYVFBMSERAPDg0MCwoJCAcGBQQDAgEAACH5BAUOAAEAIf4YT3B0aW1pemVkIHdpdGggZXpnaWYuY29tACwAAAAAQAAYAAACJoSPqcvtD6OctNqLs968+w+G4kiW5omm6sq27gvH8kzX9o3n+l0AACH5BAUHAAAALD4ACQACAAYAAAIERI5oBQAh+QQFBwAOACwsAAcAFAAJAAAEJRDISZetmJQ9uhcZyClGSY4hYjbHKqYs06ooLLuBPeM6b2u1SgQAIfkEBQcADQAsGAAHACUACQAABlFAgHAYKBqJyKRS+ahMchKnoSBYWq0Mp+rzimEiiUH1SjaCd64VioaTEo5lbA3GvaWjCmpcfnl27TZ4ent8Mmo6KW6EhXSIEGCDi4VZCG+SSUEAIfkEBQcABgAsEQAHABcACQAAB0aAAIKDAoSGh4RELD8sHwkBiJEoIUKVPSAHkJGJQZaWNQSbnJ6VMAOigzI8pDwxp6gCKUOMnhKFsAgvi59GuKkZPhkTBb6BACH5BAUHAAQALA8ABwANAAkAAAYsQICwYAgMj0LWZokSIAedpTSDREmvxaHyyjy2uN2hCqwZVLmqp2DF0bAkzyAAIfkEBRUABQAsDgAHAAoACQAABidAgDBgGAyHH5NoyUAunxuEcANdkqbVJTa7hZ6EkFFVcAx5ShmCMAgAIfkEBQcAAAAsLAAHABAACQAABC0QyClXpZWUwbsOU/AhSWmQGyiOB+Mq3rJ6zosKM22n+d2YqQzvBOOFcMOgJAIAIfkEBQcAAwAsIQAHABsACQAAB0yAAIKDAgGEh4iJKStLGTARBYmShDgoJEyYNBSRk4ouQJiZNQeFnYc5II2hJzMOBKanH6qiDK+wlBlBqxMGhreDE6m0Ub+xjBi9xYKBACH5BAUHAAYALB0ABwAQAAkAAAc+gACCAgoFg4eIGSNKSj0MiIckjJNHB5AAEiaTkzcBkC8im4w8BJ+hoj6lkBqiSC+XAUmnowOwsT8bJSiGl4EAIfkEBQcAAgAsHAAHAAsACQAABy2AAIIGDIOGRVhXijeHVYqPjIIej5QBglKUjweXmYoIkp0ClpyVhghCiRoUhoEAIfkEBRUAAgAsGwAHAAoACQAAByyAAIKCB4ODMlNkijKHY4qPMIJgj49ggpSUAQBXmIqSnRuEVJgMhiWJJQmCgQAh+QQFBwAAACwxAAcADQAJAAAEJRDICQKdgQyktrBS5h0G91WfSXYaWqjrK8rx6LL1cpf8GaY2SQQAIfkEBQcABgAsKwAHABMACQAABzyAAIKDBISGhzwbSB4ZCIeIIouLQI6PgjcmkpI4AZYDJCOak4WWQ1qiJi8Cnh8eqBiel6GbpLEgHIycsYEAIfkEBQcAAgAsKQAHAA0ACQAABzyAAIIBEhQBg4gBXHh1dWUZiQCLjZQriWWUlF2Hghh2mY14A4MVn6B7UYhmoHVSnJ13maiROFdse1YMiYEAIfkEBQcAAwAsDgAHACUACQAAB2OAA4KCBYOGh4iIETBRAI6PIIYNiZSHKWxymVUCkIcmlaAHbpmkcQCVQ6CUUqStKaeJR6qJcK2kdI6zugNltplOjh6IN7uGJr5vB44YngTFgwKYrRuPwR4dAwLPklNzbV4sj4EAIfkEBQcACgAsCwAHACcACQAAB2qAAIKDBAEACoiJiouMiQ4qBYSCLiw/LCsHho2bjF6KhpM9lpY3D4KcnAmMXaGjozECh6izihMFM5Wul5G0vQodBDY8uruyvqhcARVAxDgWxscbjQatrwOnx4mem8EZLUApkdDZClKJPIKBACH5BAUHAAoALAQABwASAAkAAAdCgACCCwEWhoOIiQMRKTQxFQYCioiMMi8fMJAEk5sQKpegjIWTEzqhNhcIo4qlpxSqnBWZroSxpi4zr5KcnTWei5yBACH5BAUHAAYALAAABwAQAAkAAAMRCLrc/rCIEIe9tOKpd/dfqCUAIfkEBRUAAQAsAAAIAAcACAAAAgaEj6nL7VAAIfkEBQcABQAsGwAHAAoACQAABzSAAIICDAODgyxSX1UeDYiLkEoIABJUkJBDADeXkZqci0eUU58ogkBVlxoEgzobVkpLqwCBACH5BAUHAAMALBMABwARAAkAAAdPgACCgwIEhoSIhTJBRx08RBSFiRQvTBoimI8VkoQ4GU4bmKFLMAqTS42iji8RAYgwqCOqQS4Mpz2hmUAxCJMqQ6mrrYmuOShPLSc2Bq6EgQAh+QQFBwAJACwHAAcAGgAJAAAHWIAAgoMBhYaEiImDFTNZRC44FAiHiomMK0A+mTaSlJWCEUVQJySaNzoYDAWfiJIfGUymkBEDrIuusLI1tJ6VDrs3pUMoKhANBLa3W4+lLym8ycqczwer0YEAIfkEBQcACwAsAAAHABYACQAABS4gIAbkaJ5oVF3HIJTpqWJ048bpVKsFjM+7xA03WgV7xOIRmUQ4jKxh0/V0+oghACH5BAUVAAEALAAABwAKAAkAAAIIhI+py+0PYQEAIfkEBQcABQAsJgAHAAwACQAAB0qAAIIEYhQGAYODN1R1bnpXN4iCGV98bZd+ZxmIFx54l6B4SodhYI2gbXZUOQAwSnqoqVMYrSV9sWUbA5NYp7IwiRgaZndmHhWDgQAh+QQFBwAHACwZAAcAGAAJAAAHboAAgoMEhYYBhImJMUMcGyNII0JANQWKimEkHUpVaWlUSkdLOFGXgxIoIY9dZWZnWCYsRBGmgikZPyJSnmhdU1pOJxKIpkW4kLy+kjfDtRWpHlhnvWOwPB8NxMXHVGevyxBqtacgQSFcTsFQtOOBACH5BAUHAAMALAkABwAgAAkAAAdtgACCgwKFhoSIiYoFFWEZPi1PJDczEQSLmIONQCElGyYckZUFmZg2KEwcHkqsPUsfEwYBpYkRMDc8R59gHkJBKzgNh7SCtriqvL2/KQzDxGIpK0FOq0jKsLLPiConLLrXNs3atS6UqDQS2eMAgQAh+QQFBwALACwAAAcAGQAJAAAHUIAAAYNNhIKHiImIDDk6ICoSCQSKlIg1MjcnmRAHk5WKFTErmqQURoafghA0KKStoQKplpCuMRUKsbKqW0S1EQOoshispQa5urtFMxO/x8iBACH5BAUHAAIALAAABwALAAkAAAIJhI+py+3xolwFACH5BAUHAAEALAAACQABAAUAAAIChF0AOw=="); 674 | background-repeat: no-repeat; 675 | height: 20px; 676 | width: 64px; 677 | } 678 | 679 | .wc-animate-scroll { 680 | left: 0; 681 | position: absolute; 682 | transition: left .8s ease; 683 | } 684 | 685 | .wc-animate-scroll-rapid { 686 | left: 0; 687 | position: absolute; 688 | transition: left .4s ease; 689 | } 690 | 691 | .wc-animate-scroll-near { 692 | left: 0; 693 | position: absolute; 694 | transition: left .3s ease-in-out; 695 | } 696 | 697 | /* text formats */ 698 | .format-markdown > p { 699 | margin-bottom: 0px; 700 | } 701 | 702 | .format-markdown code { 703 | white-space: pre-wrap; 704 | } 705 | 706 | .format-markdown + div { 707 | margin-top: 8px; 708 | } 709 | 710 | .format-markdown ol { 711 | padding-left: 30px; 712 | /* numbers are right-aligned to the period */ 713 | } 714 | 715 | .format-markdown ul { 716 | padding-left: 33px; 717 | } 718 | 719 | /* browser scrollbar customization */ 720 | .wc-app ::-webkit-scrollbar { 721 | width: 8px; 722 | } 723 | 724 | .wc-app ::-webkit-scrollbar * { 725 | background-color: transparent; 726 | } 727 | 728 | .wc-app ::-webkit-scrollbar-thumb { 729 | background-color: #dbdee1; 730 | } 731 | 732 | /* download button for Unknown media */ 733 | .wc-icon-download { 734 | display: inline-block; 735 | height: 20px; 736 | margin-left: 8px; 737 | vertical-align: top; 738 | width: 12px; 739 | } 740 | 741 | .wc-text-download { 742 | display: inline-block; 743 | font-weight: 500; 744 | text-decoration: none; 745 | } 746 | 747 | .wc-message-from-bot a.wc-link-download:link, .wc-message-from-bot a.wc-link-download:visited { 748 | color: #000000; 749 | opacity: 1; 750 | } 751 | 752 | .wc-message-from-bot a.wc-link-download:link .wc-icon-download, .wc-message-from-bot a.wc-link-download:visited .wc-icon-download { 753 | background-image: url('data:image/svg+xml;utf8,'); 754 | background-repeat: no-repeat; 755 | } 756 | 757 | .wc-message-from-bot a.wc-link-download:hover { 758 | color: #2AAA8A; 759 | opacity: 1; 760 | } 761 | 762 | .wc-message-from-bot a.wc-link-download:hover .wc-icon-download { 763 | background-image: url('data:image/svg+xml;utf8,'); 764 | background-repeat: no-repeat; 765 | } 766 | 767 | .wc-message-from-bot a.wc-link-download:active { 768 | color: #2AAA8A; 769 | opacity: 0.8; 770 | } 771 | 772 | .wc-message-from-bot a.wc-link-download:active .wc-icon-download { 773 | background-image: url('data:image/svg+xml;utf8,'); 774 | background-repeat: no-repeat; 775 | } 776 | 777 | .wc-message-from-me a.wc-link-download:link, .wc-message-from-me a.wc-link-download:visited { 778 | color: #ffffff; 779 | opacity: 1; 780 | } 781 | 782 | .wc-message-from-me a.wc-link-download:link .wc-icon-download, .wc-message-from-me a.wc-link-download:visited .wc-icon-download { 783 | background-image: url('data:image/svg+xml;utf8,'); 784 | background-repeat: no-repeat; 785 | } 786 | 787 | .wc-message-from-me a.wc-link-download:hover { 788 | color: #ffffff; 789 | opacity: 0.8; 790 | } 791 | 792 | .wc-message-from-me a.wc-link-download:hover .wc-icon-download { 793 | background-image: url('data:image/svg+xml;utf8,'); 794 | background-repeat: no-repeat; 795 | } 796 | 797 | .wc-message-from-me a.wc-link-download:active { 798 | color: #ffffff; 799 | opacity: 0.6; 800 | } 801 | 802 | .wc-message-from-me a.wc-link-download:active .wc-icon-download { 803 | background-image: url('data:image/svg+xml;utf8,'); 804 | background-repeat: no-repeat; 805 | } 806 | -------------------------------------------------------------------------------- /Controllers/DirectLineController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | using System.Web.Http; 9 | 10 | namespace EmergencyServicesBot.Controllers 11 | { 12 | public class DirectLineController : ApiController 13 | { 14 | 15 | public async Task Get() 16 | { 17 | var botName = ConfigurationManager.AppSettings["BotName"]; 18 | var directLineSecret = ConfigurationManager.AppSettings["DirectLineSecret"]; 19 | 20 | return (new BotSrc() { BotName = botName, DirectLineSecret = directLineSecret }); 21 | } 22 | } 23 | 24 | public class BotSrc 25 | { 26 | public string BotName { get; set; } 27 | public string DirectLineSecret { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Controllers/MessagesController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Web.Http; 4 | using System.Linq; 5 | using Microsoft.Bot.Connector; 6 | using Microsoft.Bot.Builder.Dialogs; 7 | using System.Web.Http.Description; 8 | using System.Net.Http; 9 | using System.Diagnostics; 10 | using Microsoft.Bot.Builder.Dialogs.Internals; 11 | using Autofac; 12 | using EmergencyServicesBot.Dialogs; 13 | 14 | namespace EmergencyServicesBot.Controllers 15 | { 16 | [BotAuthentication] 17 | public class MessagesController : ApiController 18 | { 19 | /// 20 | /// POST: api/Messages 21 | /// receive a message from a user and send replies 22 | /// 23 | /// 24 | [ResponseType(typeof(void))] 25 | public virtual async Task Post([FromBody] Activity activity) 26 | { 27 | // check if activity is of type message 28 | if (activity.GetActivityType() == ActivityTypes.Message) 29 | { 30 | await Conversation.SendAsync(activity, () => new RootDialog()); 31 | } 32 | else 33 | { 34 | HandleSystemMessage(activity); 35 | } 36 | return new HttpResponseMessage(System.Net.HttpStatusCode.Accepted); 37 | } 38 | 39 | private async Task HandleSystemMessage(Activity message) 40 | { 41 | if (message.Type == ActivityTypes.DeleteUserData) 42 | { 43 | // Implement user deletion here 44 | // If we handle user deletion, return a real message 45 | } 46 | else if (message.Type == ActivityTypes.ConversationUpdate) 47 | { 48 | // Handle conversation state changes, like members being added and removed 49 | // Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info 50 | // Not available in all channels 51 | 52 | IConversationUpdateActivity update = message; 53 | using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) 54 | { 55 | var client = scope.Resolve(); 56 | if (update.MembersAdded.Any()) 57 | { 58 | foreach (var newMember in update.MembersAdded) 59 | { 60 | if (newMember.Id == message.Recipient.Id) 61 | { 62 | await RootDialog.SendWelcomeMessage(message); 63 | } 64 | } 65 | } 66 | } 67 | } 68 | else if (message.Type == ActivityTypes.ContactRelationUpdate) 69 | { 70 | // Handle add/remove from contact lists 71 | // Activity.From + Activity.Action represent what happened 72 | } 73 | else if (message.Type == ActivityTypes.Typing) 74 | { 75 | // Handle knowing tha the user is typing 76 | } 77 | else if (message.Type == ActivityTypes.Ping) 78 | { 79 | } 80 | 81 | return null; 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /Dialogs/LanguageConst.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Web; 6 | 7 | namespace EmergencyServicesBot.Dialogs 8 | { 9 | internal static class LanguageConst 10 | { 11 | //TODO change this to have dictionary with language info object (culture + id + display name) 12 | 13 | //Set the language to be used; you can change this on-demand to change the langauage across the app 14 | //You will pass this everytime you request a value from the resx file 15 | internal static readonly CultureInfo ciEnglish = new CultureInfo("en-US"); 16 | internal static readonly CultureInfo ciSpanish = new CultureInfo("es-US"); 17 | internal static readonly CultureInfo ciChinese = new CultureInfo("zh-CN"); 18 | internal static readonly CultureInfo ciFrench = new CultureInfo("fr-FR"); 19 | 20 | internal const string enLanguageId = "en"; 21 | internal const string esLanguageId = "es"; 22 | internal const string frLanguageId = "fr"; 23 | internal const string zhLanguageId = "zh-CN"; 24 | 25 | internal const string enLanguageName = "English"; 26 | internal const string esLanguageName = "Español"; 27 | internal const string zhLanguageName = "中文"; 28 | internal const string frLanguageName = "Français"; 29 | 30 | internal static readonly string[] languages = new[] { enLanguageName, esLanguageName, zhLanguageName, frLanguageName }; 31 | internal static readonly string[] smsLanguages = new[] { $"1 -{enLanguageName}", $"2 -{esLanguageName}", $"3 -{zhLanguageName}", $"4 -{frLanguageName}" }; 32 | } 33 | } -------------------------------------------------------------------------------- /Dialogs/QandADialog.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using Microsoft.Bot.Connector; 3 | using Newtonsoft.Json.Linq; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Configuration; 7 | using System.Diagnostics; 8 | using System.Globalization; 9 | using System.Linq; 10 | using System.Net.Http; 11 | using System.Net.Http.Headers; 12 | using System.Reflection; 13 | using System.Resources; 14 | using System.Threading.Tasks; 15 | using System.Web; 16 | using System.Xml.Linq; 17 | 18 | namespace EmergencyServicesBot.Dialogs 19 | { 20 | [Serializable] 21 | public class QandADialog : IDialog 22 | { 23 | static ResourceManager translateDialog = new ResourceManager("EmergencyServicesBot.Resources.Resources", Assembly.GetExecutingAssembly()); 24 | 25 | public Task StartAsync(IDialogContext context) 26 | { 27 | var message = translateDialog.GetString("AskQuestion", context.UserData.GetValue("cultureInfo")); 28 | PromptDialog.Text(context, QuestionAsked, message); 29 | 30 | return Task.CompletedTask; 31 | } 32 | 33 | private async Task QuestionAsked(IDialogContext context, IAwaitable result) 34 | { 35 | await QuestionAskedImpl(context, await result); 36 | } 37 | 38 | private async Task QuestionAskedviaMessageAsync(IDialogContext context, IAwaitable result) 39 | { 40 | var msg = (IMessageActivity)(await result); 41 | 42 | await QuestionAskedImpl(context, msg.Text); 43 | } 44 | 45 | private async Task QuestionAskedImpl(IDialogContext context, string question) 46 | { 47 | if (IsDoneCommand(question)) 48 | { 49 | context.Done(default(object)); 50 | } 51 | else 52 | { 53 | 54 | using (var qnaClient = new HttpClient { BaseAddress = new Uri(ConfigurationManager.AppSettings[@"QnAendpoint"] + "/qnamaker/knowledgebases/" + ConfigurationManager.AppSettings[@"QnAKnowledgebaseId"] + "/generateAnswer" )}) 55 | { 56 | qnaClient.DefaultRequestHeaders.Add("Authorization", $"EndpointKey {ConfigurationManager.AppSettings[@"QnASubscriptionKey"]}"); 57 | 58 | string ApiKey = ConfigurationManager.AppSettings[@"TranslatorApiKey"]; 59 | string targetLang = context.UserData.GetValue(@"userLanguage"); 60 | string knowledgeBaseLang = ConfigurationManager.AppSettings[@"QnAKnowledgebaseLanguage"]; 61 | var accessToken = await GetAuthenticationToken(ApiKey); 62 | 63 | var httpResponse = new HttpResponseMessage(); 64 | 65 | if (targetLang != knowledgeBaseLang) 66 | { 67 | var translatedQuestion = await TranslateText(question, knowledgeBaseLang, accessToken); 68 | httpResponse = await qnaClient.PostAsJsonAsync(string.Empty, new { question = translatedQuestion }); 69 | } 70 | else 71 | { 72 | httpResponse = await qnaClient.PostAsJsonAsync(string.Empty, new { question = question }); 73 | } 74 | 75 | try 76 | { 77 | var qnaResponse = JObject.Parse(await httpResponse.Content.ReadAsStringAsync()); 78 | Trace.TraceInformation($@"QnA Response to ""{question}"": {qnaResponse.ToString()}"); 79 | 80 | var answerThreshold = double.Parse(ConfigurationManager.AppSettings[@"QnAanswerThreshold"]); 81 | Trace.TraceInformation($@"Answer threshold: {answerThreshold}"); 82 | 83 | var possibleAnswers = qnaResponse[@"answers"] 84 | .Where(answer => answer.Value(@"score") >= answerThreshold) 85 | .Select(answer => answer.Value(@"answer")); 86 | 87 | if (possibleAnswers.Any()) 88 | { 89 | var initialComment = translateDialog.GetString("AnswerQuestion", context.UserData.GetValue("cultureInfo")); 90 | await context.PostAsync(initialComment) 91 | .ContinueWith(t => 92 | { 93 | foreach (var a in possibleAnswers) 94 | { 95 | //// don't use await here because we want to block until these messages come out 96 | //context.PostAsync(a).GetAwaiter().GetResult(); 97 | 98 | Task.Run(async () => 99 | { 100 | string output; 101 | 102 | if (targetLang != knowledgeBaseLang) 103 | output = await TranslateText(a, targetLang, accessToken); 104 | else 105 | output = a; 106 | 107 | Console.WriteLine(output); 108 | 109 | context.PostAsync(output).GetAwaiter().GetResult(); 110 | 111 | }).Wait(); 112 | 113 | } 114 | }); 115 | } 116 | else 117 | { 118 | await context.PostAsync(translateDialog.GetString("NoAnswer", context.UserData.GetValue("cultureInfo"))); 119 | } 120 | 121 | PromptDialog.Text(context, QuestionAsked, translateDialog.GetString("NewQuestion", context.UserData.GetValue("cultureInfo"))); 122 | } 123 | catch (Exception ex) 124 | { 125 | Trace.TraceError($@"QnA endpoint error for question ""{question}"" : {ex.ToString()}"); 126 | 127 | PromptDialog.Text(context, QuestionAsked, translateDialog.GetString("ErrorQnA", context.UserData.GetValue("cultureInfo"))); 128 | } 129 | } 130 | } 131 | } 132 | 133 | static async Task TranslateText(string inputText, string language, string accessToken) 134 | { 135 | string url = "http://api.microsofttranslator.com/v2/Http.svc/Translate"; 136 | string query = $"?text={System.Net.WebUtility.UrlEncode(inputText)}&to={language}&contentType=text/plain"; 137 | 138 | using (var client = new HttpClient()) 139 | { 140 | client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); 141 | var response = await client.GetAsync(url + query); 142 | var result = await response.Content.ReadAsStringAsync(); 143 | 144 | if (!response.IsSuccessStatusCode) 145 | return "Hata: " + result; 146 | 147 | var translatedText = XElement.Parse(result).Value; 148 | return translatedText; 149 | } 150 | } 151 | 152 | static async Task GetAuthenticationToken(string key) 153 | { 154 | string endpoint = "https://api.cognitive.microsoft.com/sts/v1.0/issueToken"; 155 | 156 | using (var client = new HttpClient()) 157 | { 158 | client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", key); 159 | var response = await client.PostAsync(endpoint, null); 160 | var token = await response.Content.ReadAsStringAsync(); 161 | return token; 162 | } 163 | } 164 | 165 | //TODO change to use resources 166 | private bool IsDoneCommand(string commandText) => 167 | commandText.Equals(@"done", StringComparison.OrdinalIgnoreCase) 168 | || commandText.StartsWith(@"no", StringComparison.OrdinalIgnoreCase) 169 | || commandText.Equals(@"exit", StringComparison.OrdinalIgnoreCase) 170 | || commandText.Equals(@"quitter", StringComparison.OrdinalIgnoreCase) 171 | || commandText.Equals(@"quit", StringComparison.OrdinalIgnoreCase); 172 | } 173 | } -------------------------------------------------------------------------------- /Dialogs/RootDialog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | using Microsoft.Bot.Builder.Azure; 6 | using Microsoft.Bot.Builder.Dialogs; 7 | using Microsoft.Bot.Builder.CognitiveServices.QnAMaker; 8 | using Microsoft.Bot.Connector; 9 | using System.Collections.Generic; 10 | using System.Globalization; 11 | using System.Resources; 12 | using System.Reflection; 13 | using System.Xml.Linq; 14 | using System.Configuration; 15 | using System.Net.Http; 16 | using System.Web; 17 | using System.Diagnostics; 18 | using System.Net; 19 | using System.IO; 20 | using System.Text; 21 | 22 | namespace EmergencyServicesBot.Dialogs 23 | { 24 | [Serializable] 25 | public class RootDialog : IDialog 26 | { 27 | static ResourceManager translateDialog = new ResourceManager("EmergencyServicesBot.Resources.Resources", Assembly.GetExecutingAssembly()); 28 | 29 | private const string userDataCultureKey = @"cultureInfo"; 30 | 31 | public async Task StartAsync(IDialogContext context) 32 | { 33 | /* Wait until the first message is received from the conversation and call MessageReceviedAsync 34 | * to process that message. */ 35 | context.Wait(this.MessageReceivedAsync); 36 | } 37 | 38 | private async Task MessageReceivedAsync(IDialogContext context, IAwaitable result) 39 | { 40 | /* When MessageReceivedAsync is called, it's passed an IAwaitable. To get the message, 41 | * await the result. */ 42 | var message = await result; 43 | 44 | var qnaSubscriptionKey = ConfigurationManager.AppSettings[@"QnASubscriptionKey"]; 45 | var qnaKBId = ConfigurationManager.AppSettings[@"QnAKnowledgebaseId"]; 46 | var translatorKey = ConfigurationManager.AppSettings[@"TranslatorApiKey"]; 47 | 48 | 49 | // QnA Subscription Key, KnowledgeBase Id, and TranslatorApiKey null verification 50 | if (string.IsNullOrEmpty(qnaSubscriptionKey) || string.IsNullOrEmpty(qnaKBId) || string.IsNullOrEmpty(translatorKey)) 51 | { 52 | await context.PostAsync("Please set QnAKnowledgebaseId, QnASubscriptionKey, and TranslatorApiKey in App Settings. Get them at https://qnamaker.ai and https://www.microsoft.com/en-us/translator/."); 53 | } 54 | else 55 | { 56 | await DetectAndSaveUserLanguageAsync(context, message.Text); 57 | 58 | string[] choices = GetMainMenuChoices(context); 59 | 60 | // detect language and set appropriate cultureInfo 61 | var welcomeMessage = translateDialog.GetString("Welcome", context.UserData.GetValue("cultureInfo")); 62 | 63 | PromptDialog.Choice(context, UserChoiceMade, choices, welcomeMessage); 64 | } 65 | 66 | } 67 | 68 | private static string[] GetMainMenuChoices(IDialogContext context) 69 | { 70 | string[] choices = new[] { translateDialog.GetString("GetAnswers", context.UserData.GetValue("cultureInfo")), translateDialog.GetString("SetLanguage", context.UserData.GetValue("cultureInfo")) }; 71 | if (context.Activity.ChannelId == ChannelIds.Sms) 72 | { // on SMS, communicate they can choose by replying with "1" or "2" 73 | choices = new[] { translateDialog.GetString("mobileGetAnswers", context.UserData.GetValue("cultureInfo")), translateDialog.GetString("mobileSetLanguage", context.UserData.GetValue("cultureInfo")) }; 74 | } 75 | 76 | return choices; 77 | } 78 | 79 | private async Task DetectAndSaveUserLanguageAsync(IDialogContext context, string userText) 80 | { 81 | if (!context.UserData.TryGetValue(@"userLanguage", out string userLanguage)) 82 | { 83 | 84 | string detectUri = string.Format(ConfigurationManager.AppSettings[@"TranslatorEndpoint"], "detect"); 85 | 86 | HttpWebRequest detectLanguageWebRequest = (HttpWebRequest)WebRequest.Create(detectUri); 87 | detectLanguageWebRequest.Headers.Add("Ocp-Apim-Subscription-Key", ConfigurationManager.AppSettings[@"TranslatorApiKey"]); 88 | detectLanguageWebRequest.ContentType = "application/json; charset=utf-8"; 89 | detectLanguageWebRequest.Method = "POST"; 90 | 91 | // Send request 92 | var serializer = new System.Web.Script.Serialization.JavaScriptSerializer(); 93 | string jsonText = serializer.Serialize(userText); 94 | 95 | string body = "[{ \"Text\": " + jsonText + " }]"; 96 | byte[] data = Encoding.UTF8.GetBytes(body); 97 | 98 | detectLanguageWebRequest.ContentLength = data.Length; 99 | 100 | using (var requestStream = detectLanguageWebRequest.GetRequestStream()) 101 | requestStream.Write(data, 0, data.Length); 102 | 103 | HttpWebResponse response = (HttpWebResponse)detectLanguageWebRequest.GetResponse(); 104 | 105 | // Read and parse JSON response 106 | var responseStream = response.GetResponseStream(); 107 | var jsonString = new StreamReader(responseStream, Encoding.GetEncoding("utf-8")).ReadToEnd(); 108 | dynamic jsonResponse = serializer.DeserializeObject(jsonString); 109 | 110 | // Fish out the detected language code 111 | var languageInfo = jsonResponse[0]; 112 | var detectedLanguage = languageInfo["language"]; 113 | context.UserData.SetValue(@"userLanguage", detectedLanguage); 114 | context.UserData.SetValue(userDataCultureKey, GetCultureInfoFromLanguageId(detectedLanguage)); 115 | 116 | } 117 | 118 | else 119 | { 120 | var userCulture = GetCultureInfoFromLanguageId(userLanguage); 121 | 122 | context.UserData.SetValue(userDataCultureKey, userCulture); 123 | } 124 | } 125 | 126 | private CultureInfo GetCultureInfoFromLanguageId(string languageId) 127 | { 128 | if (string.IsNullOrWhiteSpace(languageId)) 129 | { 130 | return LanguageConst.ciEnglish; 131 | } 132 | 133 | CultureInfo userCulture = LanguageConst.ciEnglish; 134 | 135 | switch (languageId) 136 | { 137 | case LanguageConst.esLanguageId: 138 | userCulture = LanguageConst.ciSpanish; 139 | break; 140 | case LanguageConst.zhLanguageId: 141 | userCulture = LanguageConst.ciChinese; 142 | break; 143 | case LanguageConst.frLanguageId: 144 | userCulture = LanguageConst.ciFrench; 145 | break; 146 | case LanguageConst.enLanguageId: 147 | default: 148 | userCulture = LanguageConst.ciEnglish; 149 | break; 150 | } 151 | 152 | return userCulture; 153 | } 154 | 155 | private async Task UserChoiceMade(IDialogContext context, IAwaitable result) 156 | { 157 | var choice = await result; 158 | 159 | //TODO change with resource entry directly 160 | if ((choice.IndexOf(@"get answers", 0, StringComparison.OrdinalIgnoreCase) != -1) || 161 | (choice.IndexOf(@"Obtener Respuestas", 0, StringComparison.OrdinalIgnoreCase) != -1) || 162 | (choice.IndexOf(@"Obtenir les réponses", 0, StringComparison.OrdinalIgnoreCase) != -1) || 163 | (choice.IndexOf(@"其他问题", 0, StringComparison.OrdinalIgnoreCase) != -1)) 164 | context.Call(new QandADialog(), DoneWithSubdialog); 165 | else if ((choice.IndexOf(@"Select language", 0, StringComparison.OrdinalIgnoreCase) != -1) || 166 | (choice.IndexOf(@"Seleccione el idioma", 0, StringComparison.OrdinalIgnoreCase) != -1) || 167 | (choice.IndexOf(@"Sélectionner la langue", 0, StringComparison.OrdinalIgnoreCase) != -1) || 168 | (choice.IndexOf(@"选择语言", 0, StringComparison.OrdinalIgnoreCase) != -1)) 169 | context.Call(new SetLanguage(), DoneWithSubdialog); 170 | } 171 | 172 | private Task DoneWithSubdialog(IDialogContext context, IAwaitable result) 173 | { 174 | var choices = GetMainMenuChoices(context); 175 | 176 | // detect language and set appropriate cultureInfo 177 | var message = translateDialog.GetString("NewQuestion", context.UserData.GetValue("cultureInfo")); 178 | 179 | PromptDialog.Choice(context, UserChoiceMade, choices, message); 180 | 181 | return Task.CompletedTask; 182 | } 183 | 184 | private async Task AfterAnswerAsync(IDialogContext context, IAwaitable result) 185 | { 186 | // wait for the next user message 187 | context.Wait(MessageReceivedAsync); 188 | } 189 | 190 | //This method is only called once and always uses English Language Resources 191 | public static async Task SendWelcomeMessage(IMessageActivity activity) 192 | { 193 | 194 | ConnectorClient client = new ConnectorClient(new Uri(activity.ServiceUrl)); 195 | 196 | var title = translateDialog.GetString("WelcomeTitle", LanguageConst.ciEnglish); 197 | 198 | IList cardsAttachment = new List(); 199 | var reply = ((Activity)activity).CreateReply(); 200 | 201 | CardImage CI = new CardImage 202 | { 203 | Url = translateDialog.GetString("WelcomeImageUrl", LanguageConst.ciEnglish), 204 | }; 205 | 206 | //TODO change by adding resources instead of hardcoded text 207 | var heroCard = new HeroCard 208 | { 209 | Title = title, 210 | Subtitle = "Hello. Hola. 你好. Bonjour.", 211 | Text = "Say \"hi\" to begin, diga \"hola\" para comenzar, 说“嗨”开始, dites \"Bonjour\" pour commencer", 212 | Images = new List { CI } 213 | }; 214 | 215 | 216 | cardsAttachment.Add(heroCard.ToAttachment()); 217 | 218 | reply.Attachments = cardsAttachment; 219 | 220 | await client.Conversations.ReplyToActivityAsync(reply); 221 | 222 | } 223 | } 224 | 225 | // For more information about this template visit http://aka.ms/azurebots-csharp-qnamaker 226 | [Serializable] 227 | public class BasicQnAMakerDialog : QnAMakerDialog 228 | { 229 | // Go to https://qnamaker.ai and feed data, train & publish your QnA Knowledgebase. 230 | // Parameters to QnAMakerService are: 231 | // Required: subscriptionKey, knowledgebaseId, 232 | // Optional: defaultMessage, scoreThreshold[Range 0.0 – 1.0] 233 | public BasicQnAMakerDialog() : base(new QnAMakerService(new QnAMakerAttribute(Utils.GetAppSetting("QnASubscriptionKey"), Utils.GetAppSetting("QnAKnowledgebaseId"), "I could not find an answer to your question. Please try again or contact Houston 311.", 0.5))) 234 | { } 235 | } 236 | } -------------------------------------------------------------------------------- /Dialogs/SetLanguage.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Bot.Builder.Dialogs; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Globalization; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Resources; 8 | using System.Threading.Tasks; 9 | using System.Web; 10 | 11 | namespace EmergencyServicesBot.Dialogs 12 | { 13 | [Serializable] 14 | public class SetLanguage : IDialog 15 | { 16 | static ResourceManager translateDialog = new ResourceManager("EmergencyServicesBot.Resources.Resources", Assembly.GetExecutingAssembly()); 17 | 18 | //Set the language to be used; you can change this on-demand to change the langauage across the app 19 | //You will pass this everytime you request a value from the resx file 20 | 21 | 22 | public async Task StartAsync(IDialogContext context) 23 | { 24 | PromptDialog.Choice( 25 | context, 26 | this.onLanguageSelect, 27 | context.Activity.ChannelId == ChannelIds.Sms ? LanguageConst.smsLanguages : LanguageConst.languages, 28 | translateDialog.GetString("SetLanguage", context.UserData.GetValue("cultureInfo"))); 29 | } 30 | 31 | private async Task onLanguageSelect(IDialogContext context, IAwaitable result) 32 | { 33 | // set the language preference in userData 34 | var selectedLanguage = await result; 35 | var choice = await result; 36 | CultureInfo culture; 37 | 38 | if (choice.IndexOf(LanguageConst.enLanguageName, 0, StringComparison.OrdinalIgnoreCase) != -1) 39 | { 40 | choice = LanguageConst.enLanguageId; 41 | selectedLanguage = LanguageConst.enLanguageName; 42 | culture = LanguageConst.ciEnglish; 43 | } 44 | else if (choice.IndexOf(LanguageConst.esLanguageName, 0, StringComparison.OrdinalIgnoreCase) != -1) 45 | { 46 | choice = LanguageConst.esLanguageId; 47 | selectedLanguage = LanguageConst.esLanguageName; 48 | culture = LanguageConst.ciSpanish; 49 | } 50 | else if (choice.IndexOf(LanguageConst.zhLanguageName, 0, StringComparison.OrdinalIgnoreCase) != -1) 51 | { 52 | choice = LanguageConst.zhLanguageId; 53 | selectedLanguage = LanguageConst.zhLanguageName; 54 | culture = LanguageConst.ciChinese; 55 | } 56 | else if (choice.IndexOf(LanguageConst.frLanguageName, 0, StringComparison.OrdinalIgnoreCase) != -1) 57 | { 58 | choice = LanguageConst.frLanguageId; 59 | selectedLanguage = LanguageConst.frLanguageName; 60 | culture = LanguageConst.ciFrench; 61 | } 62 | else 63 | { 64 | choice = LanguageConst.enLanguageId; 65 | selectedLanguage = LanguageConst.enLanguageName; 66 | culture = LanguageConst.ciEnglish; 67 | } 68 | 69 | context.UserData.SetValue(@"userLanguage", choice); 70 | context.UserData.SetValue(@"cultureInfo", culture); 71 | 72 | var message = translateDialog.GetString("SetLanguageConfirmation", culture); 73 | 74 | await context.PostAsync(message + " " + selectedLanguage); 75 | 76 | context.Done(context); 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /Docs/azure-deploy-parameters.md: -------------------------------------------------------------------------------- 1 | # Azure Deployment Parameters 2 | 3 | | Parameter | Description | | 4 | | -------------- | ------------| --------- | 5 | | Directory | Defaults to `Microsoft` | Required | 6 | | Subscription | [Get an azure subscription](https://azure.microsoft.com/en-us/free/). | Required | 7 | | Resource Group | Through `deploy to azure`, you may choose an existing resource group, or create a new one. | Required | 8 | | Site Name | The name used to prefix the Azure DNS record *sitename*.azurewebsites.net | Required | 9 | | Sku | The App Service Pricing Sky - See: https://azure.microsoft.com/en-us/pricing/details/app-service/windows/ | Required | 10 | | Worker Size | The size of the Azure Web App Instance (Small, Medium, Large) | Required | 11 | | Microsoft App Id | The App Id assigned when creating a Bot Channel Registration | Required | 12 | | Microsoft App Password | The password generated for the Bot Channel Registration | Required | 13 | | Bot Name | The name assigned to your bot when creating a Bot Channel Registration | Required | 14 | | Directline Secret | The generated secret when configuring the Directline channel | Required | 15 | | QnA Endpoint | The url to the created QnA Maker service endpoint (provided) | Required | 16 | | QnA Knowledgebase Id | The id of the QnA Maker knoweldge base | Required | 17 | | QnA Subscription Key | The subscription key for accessing your QnA maker instance| Required | 18 | | QnA Knowledgebase Language | The prefix for the language of the knowledgebase (default: 'en') | Required | 19 | | QnA Answer Threshold | Confidence threshold to return result from the knowledgebase (default: '30')| Required | 20 | | Translator Endpoint | The url to the Microsoft Translator service endpoint (provided) | Required | 21 | | Translator Api Key | Api Key for accessing the Microsoft Translator service | Required | 22 | -------------------------------------------------------------------------------- /EmergencyServicesBot.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | Debug 9 | AnyCPU 10 | 11 | 12 | 2.0 13 | {9C7E3A46-B60F-4052-9A21-DC8D1A942216} 14 | {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} 15 | Library 16 | Properties 17 | EmergencyServicesBot 18 | EmergencyServicesBot 19 | v4.6 20 | true 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | true 33 | full 34 | false 35 | bin\ 36 | DEBUG;TRACE 37 | prompt 38 | 4 39 | CS1998 40 | 41 | 42 | pdbonly 43 | true 44 | bin\ 45 | TRACE 46 | prompt 47 | 4 48 | CS1998 49 | 50 | 51 | 52 | packages\Autofac.4.6.2\lib\net45\Autofac.dll 53 | 54 | 55 | packages\Chronic.Signed.0.3.2\lib\net40\Chronic.dll 56 | True 57 | 58 | 59 | packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll 60 | 61 | 62 | packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll 63 | 64 | 65 | packages\Microsoft.Azure.DocumentDB.1.20.2\lib\net45\Microsoft.Azure.Documents.Client.dll 66 | 67 | 68 | packages\Microsoft.Azure.KeyVault.Core.2.0.4\lib\net45\Microsoft.Azure.KeyVault.Core.dll 69 | 70 | 71 | packages\Microsoft.Bot.Builder.3.14.0\lib\net46\Microsoft.Bot.Builder.dll 72 | 73 | 74 | packages\Microsoft.Bot.Builder.3.14.0\lib\net46\Microsoft.Bot.Builder.Autofac.dll 75 | 76 | 77 | packages\Microsoft.Bot.Builder.Azure.3.2.5\lib\net46\Microsoft.Bot.Builder.Azure.dll 78 | 79 | 80 | packages\Microsoft.Bot.Builder.CognitiveServices.1.1.2\lib\net46\Microsoft.Bot.Builder.CognitiveServices.QnAMaker.dll 81 | 82 | 83 | packages\Microsoft.Bot.Builder.History.3.14.0\lib\net46\Microsoft.Bot.Builder.History.dll 84 | 85 | 86 | packages\Microsoft.Bot.Connector.3.14.0\lib\net46\Microsoft.Bot.Connector.dll 87 | 88 | 89 | packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.8\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll 90 | 91 | 92 | 93 | packages\Microsoft.Data.Edm.5.8.3\lib\net40\Microsoft.Data.Edm.dll 94 | 95 | 96 | packages\Microsoft.Data.OData.5.8.3\lib\net40\Microsoft.Data.OData.dll 97 | 98 | 99 | packages\Microsoft.Data.Services.Client.5.8.3\lib\net40\Microsoft.Data.Services.Client.dll 100 | 101 | 102 | packages\Microsoft.IdentityModel.Logging.5.2.1\lib\net451\Microsoft.IdentityModel.Logging.dll 103 | 104 | 105 | packages\Microsoft.IdentityModel.Protocol.Extensions.1.0.4.403061554\lib\net45\Microsoft.IdentityModel.Protocol.Extensions.dll 106 | True 107 | 108 | 109 | packages\Microsoft.IdentityModel.Protocols.5.2.1\lib\net451\Microsoft.IdentityModel.Protocols.dll 110 | 111 | 112 | packages\Microsoft.IdentityModel.Protocols.OpenIdConnect.5.2.1\lib\net451\Microsoft.IdentityModel.Protocols.OpenIdConnect.dll 113 | 114 | 115 | packages\Microsoft.IdentityModel.Tokens.5.2.1\lib\net451\Microsoft.IdentityModel.Tokens.dll 116 | 117 | 118 | packages\Microsoft.Rest.ClientRuntime.2.3.10\lib\net452\Microsoft.Rest.ClientRuntime.dll 119 | 120 | 121 | packages\Microsoft.WindowsAzure.ConfigurationManager.3.2.3\lib\net40\Microsoft.WindowsAzure.Configuration.dll 122 | 123 | 124 | packages\WindowsAzure.Storage.9.0.0\lib\net45\Microsoft.WindowsAzure.Storage.dll 125 | 126 | 127 | packages\Newtonsoft.Json.11.0.1\lib\net45\Newtonsoft.Json.dll 128 | 129 | 130 | 131 | packages\System.IdentityModel.Tokens.Jwt.5.2.1\lib\net451\System.IdentityModel.Tokens.Jwt.dll 132 | 133 | 134 | 135 | 136 | 137 | packages\Microsoft.AspNet.WebApi.Client.5.2.4\lib\net45\System.Net.Http.Formatting.dll 138 | 139 | 140 | 141 | 142 | 143 | packages\System.Spatial.5.8.3\lib\net40\System.Spatial.dll 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | packages\Microsoft.AspNet.WebApi.Core.5.2.4\lib\net45\System.Web.Http.dll 156 | 157 | 158 | packages\Microsoft.AspNet.WebApi.WebHost.5.2.4\lib\net45\System.Web.Http.WebHost.dll 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | Designer 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | Global.asax 185 | 186 | 187 | 188 | Resources.fr-FR.resx 189 | True 190 | True 191 | 192 | 193 | True 194 | True 195 | Resources.en-US.resx 196 | 197 | 198 | True 199 | True 200 | Resources.es-US.resx 201 | 202 | 203 | True 204 | True 205 | Resources.zh-CN.resx 206 | 207 | 208 | 209 | 210 | Designer 211 | 212 | 213 | Web.config 214 | 215 | 216 | Web.config 217 | 218 | 219 | 220 | 221 | ResXFileCodeGenerator 222 | Resources.fr-FR.Designer.cs 223 | 224 | 225 | ResXFileCodeGenerator 226 | Resources.en-US.Designer.cs 227 | 228 | 229 | ResXFileCodeGenerator 230 | Resources.es-US.Designer.cs 231 | 232 | 233 | ResXFileCodeGenerator 234 | Resources.zh-CN.Designer.cs 235 | 236 | 237 | 238 | 10.0 239 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 240 | 241 | 242 | true 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | True 252 | True 253 | 3978 254 | / 255 | http://localhost:3984/ 256 | False 257 | False 258 | 259 | 260 | False 261 | 262 | 263 | 264 | 265 | 266 | 267 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 268 | 269 | 270 | 271 | 272 | 273 | 280 | 281 | 282 | 283 | -------------------------------------------------------------------------------- /EmergencyServicesBot.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EmergencyServicesBot", "EmergencyServicesBot.csproj", "{9C7E3A46-B60F-4052-9A21-DC8D1A942216}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {9C7E3A46-B60F-4052-9A21-DC8D1A942216}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {9C7E3A46-B60F-4052-9A21-DC8D1A942216}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {9C7E3A46-B60F-4052-9A21-DC8D1A942216}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {9C7E3A46-B60F-4052-9A21-DC8D1A942216}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application CodeBehind="Global.asax.cs" Inherits="EmergencyServicesBot.WebApiApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /Global.asax.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using System.Web.Http; 3 | using System.Configuration; 4 | using System.Reflection; 5 | using Microsoft.Bot.Builder.Azure; 6 | using Microsoft.Bot.Builder.Dialogs; 7 | using Microsoft.Bot.Builder.Dialogs.Internals; 8 | using Microsoft.Bot.Connector; 9 | 10 | namespace EmergencyServicesBot 11 | { 12 | public class WebApiApplication : System.Web.HttpApplication 13 | { 14 | protected void Application_Start() 15 | { 16 | // Bot Storage: This is a great spot to register the private state storage for your bot. 17 | // We provide adapters for Azure Table, CosmosDb, SQL Azure, or you can implement your own! 18 | // For samples and documentation, see: https://github.com/Microsoft/BotBuilder-Azure 19 | 20 | Conversation.UpdateContainer( 21 | builder => 22 | { 23 | builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly())); 24 | 25 | // Using Azure Table Storage 26 | //var store = new TableBotDataStore(ConfigurationManager.AppSettings["AzureWebJobsStorage"]); // requires Microsoft.BotBuilder.Azure Nuget package 27 | 28 | // To use CosmosDb or InMemory storage instead of the default table storage, uncomment the corresponding line below 29 | // var store = new DocumentDbBotDataStore("cosmos db uri", "cosmos db key"); // requires Microsoft.BotBuilder.Azure Nuget package 30 | var store = new InMemoryDataStore(); // volatile in-memory store 31 | 32 | builder.Register(c => store) 33 | .Keyed>(AzureModule.Key_DataStore) 34 | .AsSelf() 35 | .SingleInstance(); 36 | 37 | }); 38 | GlobalConfiguration.Configure(WebApiConfig.Register); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Microsoft 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PostDeployScripts/IncludeSources.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | $(CoreCompileDependsOn);IncludeSource 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /PostDeployScripts/githubProject.json.template: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{WEB_SITE_NAME}", 3 | "description": "{WEB_SITE_NAME} Azure Bot Service Code", 4 | "homepage": "https://github.com", 5 | "private": false, 6 | "has_issues": true, 7 | "has_projects": true, 8 | "has_wiki": true 9 | } -------------------------------------------------------------------------------- /PostDeployScripts/prepareSrc.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | SET password=%1 4 | SET repoName=srcRepo 5 | SET repoUrl=file:///%HOMEDRIVE:~0,1%/%HOMEPATH:~1%/site/%repoName% 6 | SET download=bot-src 7 | 8 | echo %repoUrl% 9 | 10 | rem cd to project root 11 | pushd ..\wwwroot 12 | 13 | rem init git 14 | call git init 15 | call git config user.name "botframework" 16 | call git config user.email "util@botframework.com" 17 | call git add . 18 | call git commit -m "prepare to download source" 19 | call git remote add srcRepo %repoUrl% 20 | popd 21 | 22 | rem init upstream 23 | pushd %HOME%\site 24 | mkdir srcRepo 25 | cd srcRepo 26 | call git init --bare 27 | popd 28 | 29 | rem push to upstream 30 | pushd ..\wwwroot 31 | call git push --set-upstream srcRepo master 32 | popd 33 | 34 | rem clone srcRepo 35 | pushd %HOME%\site 36 | call git clone %repoUrl% %download% 37 | rem delete .git 38 | cd %download% 39 | call rm -r -f .git 40 | popd 41 | 42 | rem prepare for publish 43 | pushd %HOME%\site\%download% 44 | mkdir Properties\PublishProfiles 45 | pushd Properties\PublishProfiles 46 | type ..\..\PostDeployScripts\publishProfile.xml.template | sed -e s/\{WEB_SITE_NAME\}/%WEBSITE_SITE_NAME%/g > %WEBSITE_SITE_NAME%-Web-Deploy.pubxml 47 | popd 48 | 49 | set SOLUTION_NAME= 50 | for /f "delims=" %%a in ('dir /b *.sln') do @set SOLUTION_NAME=%%a 51 | 52 | type PostDeployScripts\publish.cmd.template | sed -e s/\{SOLUTION_NAME\}/%SOLUTION_NAME%/g | sed -e s/\{PUBLISH_PROFILE\}/%WEBSITE_SITE_NAME%-Web-Deploy.pubxml/g | sed -e s/\{PASSWORD\}/%password%/g > publish.cmd 53 | type PostDeployScripts\publishSettings.xml.template | sed -e s/\{WEB_SITE_NAME\}/%WEBSITE_SITE_NAME%/g | sed -e s/\{PASSWORD\}/%password%/g > PostDeployScripts\%WEBSITE_SITE_NAME%.PublishSettings 54 | 55 | popd 56 | 57 | rem preare the zip file 58 | %HOMEDRIVE%\7zip\7za a %HOME%\site\%download%.zip %HOME%\site\%download%\* 59 | 60 | rem cleanup git stuff 61 | pushd ..\wwwroot 62 | call rm -r -f .git 63 | popd 64 | 65 | pushd %HOME%\site 66 | call rm -r -f %download% 67 | call rm -r -f %repoName% 68 | popd 69 | 70 | endlocal 71 | -------------------------------------------------------------------------------- /PostDeployScripts/publish.cmd.template: -------------------------------------------------------------------------------- 1 | nuget restore 2 | msbuild {SOLUTION_NAME} -p:DeployOnBuild=true -p:PublishProfile={PUBLISH_PROFILE} -p:Password={PASSWORD} 3 | 4 | -------------------------------------------------------------------------------- /PostDeployScripts/publishProfile.xml.template: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | MSDeploy 9 | AzureWebSite 10 | Release 11 | Any CPU 12 | http://{WEB_SITE_NAME}.azurewebsites.net 13 | True 14 | False 15 | {WEB_SITE_NAME}.scm.azurewebsites.net:443 16 | {WEB_SITE_NAME} 17 | 18 | True 19 | WMSVC 20 | True 21 | ${WEB_SITE_NAME} 22 | <_SavePWD>True 23 | <_DestinationType>AzureWebSite 24 | 25 | -------------------------------------------------------------------------------- /PostDeployScripts/publishSettings.xml.template: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /PostDeployScripts/runGulp.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | set DEPLOYMENT_SOURCE= 5 | set IN_PLACE_DEPLOYMENT=1 6 | 7 | if exist ..\wwwroot\deploy.cmd ( 8 | pushd ..\wwwroot 9 | rem call deploy.cmd 10 | popd 11 | ) 12 | 13 | rem kick of build of csproj 14 | 15 | echo record deployment timestamp 16 | date /t >> ..\deployment.log 17 | time /t >> ..\deployment.log 18 | echo ---------------------- >> ..\deployment.log 19 | echo Deployment done 20 | 21 | endlocal 22 | 23 | 24 | -------------------------------------------------------------------------------- /PostDeployScripts/setupGithubRemoteRepo.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | rem ------------------------------------------------------------------------------------------ 4 | rem setupVsoRemoteRepo [remoteUser] [personalAccessToken] [projName{optional}] 5 | rem create and populate VSO git repo for the ABS code instance 6 | rem 7 | rem remoteUser: user account name of the personal access token 8 | rem personalAccessToken: the personal access token used to access github REST API (requires repos scope) 9 | rem projName the name of the project to create (default to WEBSITE_SITE_NAME) 10 | rem ------------------------------------------------------------------------------------------ 11 | set remoteUrl=https://api.github.com 12 | set remoteUser=%1 13 | set remotePwd=%2 14 | set projName=%3 15 | if '%projName%'=='' set projName=%WEBSITE_SITE_NAME% 16 | set repoUrl=https://%remoteUser%:%remotePwd%@github.com/%remoteUser%/%projName%.git 17 | rem use curl to create project 18 | pushd ..\wwwroot 19 | type PostDeployScripts\githubProject.json.template | sed -e s/\{WEB_SITE_NAME\}/%projName%/g > %TEMP%\githubProject.json 20 | call curl -H "Content-Type: application/json" -u %remoteUser%:%remotePwd% -d "@%TEMP%\githubProject.json" -X POST %remoteUrl%/user/repos 21 | rem rm %TEMP%\githubProject.json 22 | popd 23 | 24 | popd 25 | rem cd to project root 26 | pushd ..\wwwroot 27 | 28 | rem init git 29 | call git init 30 | call git config user.name "%remoteUser%" 31 | call git config user.password "%remotePwd%" 32 | call git config user.email "util@botframework.com" 33 | call git add . 34 | call git commit -m "prepare to setup source control" 35 | call git push %repoUrl% master 36 | popd 37 | 38 | 39 | rem cleanup git stuff 40 | pushd ..\wwwroot 41 | call rm -r -f .git 42 | popd 43 | 44 | endlocal -------------------------------------------------------------------------------- /PostDeployScripts/setupVsoRemoteRepo.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | rem ------------------------------------------------------------------------------------------ 4 | rem setupVsoRemoteRepo [vsoRemote] [vsoUserName] [vsoPersonalAccessToken] [projName{optional}] 5 | rem create and populate VSO git repo for the ABS code instance 6 | rem 7 | rem vsoRmote: url of the VSO site (e.g. https://awesomebot.visualstudio.com ) 8 | rem vosUserName: user account name of the personal access token 9 | rem vsoPersonalAccessToken: the personal access token used to access VSO REST api 10 | rem projName the name of the project to create (default to WEBSITE_SITE_NAME) 11 | rem ------------------------------------------------------------------------------------------ 12 | set remoteUrl=%1 13 | set remoteUser=%2 14 | set remotePwd=%3 15 | set projName=%4 16 | if '%projName%'=='' set projName=%WEBSITE_SITE_NAME% 17 | set vstsRoot=%remoteUrl% 18 | set repoUrl=https://%remoteUser%:%remotePwd%@%remoteUrl:~8%/_git/%projName% 19 | set vstsCreateProject=https://%remoteUser%:%remotePwd%@%remoteUrl:~8%/defaultcollection/_apis/projects?api-version=3.0 20 | 21 | rem use curl to create project 22 | pushd ..\wwwroot 23 | type PostDeployScripts\vsoProject.json.template | sed -e s/\{WEB_SITE_NAME\}/%projName%/g > %TEMP%\vsoProject.json 24 | call curl -H "Content-Type: application/json" -d "@%TEMP%\vsoProject.json" -X POST %vstsCreateProject% 25 | rm %TEMP%\vsoProject.json 26 | rem sleep for 15 seconds for the creation to complete, this is a wild guess 27 | call sleep 15 28 | popd 29 | 30 | popd 31 | rem cd to project root 32 | pushd ..\wwwroot 33 | 34 | rem init git 35 | call git init 36 | call git config user.name "%remoteUser%" 37 | call git config user.password "%remotePwd%" 38 | call git config user.email "util@botframework.com" 39 | call git add . 40 | call git commit -m "prepare to setup source control" 41 | call git push %repoUrl% master 42 | popd 43 | 44 | 45 | rem cleanup git stuff 46 | pushd ..\wwwroot 47 | call rm -r -f .git 48 | popd 49 | 50 | endlocal -------------------------------------------------------------------------------- /PostDeployScripts/vsoProject.json.template: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{WEB_SITE_NAME}", 3 | "description": "{WEB_SITE_NAME} Azure Bot Service Code", 4 | "capabilities": { 5 | "versioncontrol": { 6 | "sourceControlType": "Git" 7 | }, 8 | "processTemplate": { 9 | "templateTypeId": "6b724908-ef14-45cf-84f8-768b5384da45" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("EmergencyServicesBot")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("EmergencyServicesBot")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("a8ba1066-5695-4d71-abb4-65e5a5e0c3d4")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /Resources/Resources.en-US.Designer.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTBox/EmergencyServicesBot/69e91f06c6e66d4c397978209c232d7371c95ec6/Resources/Resources.en-US.Designer.cs -------------------------------------------------------------------------------- /Resources/Resources.en-US.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 | Here’s an answer I think might help: 122 | 123 | 124 | Sure thing, how can I help? You can say things like “What documentation do I need to make an insurance claim?” or “How can I get ahold of FEMA for assistance?” 125 | 126 | 127 | Get Answers 128 | 129 | 130 | 1 - Get Answers 131 | 132 | 133 | 2 - 🌐 Select language 134 | 135 | 136 | Is there another question you have that I might be able to help with? 137 | 138 | 139 | 🌐 Select language 140 | 141 | 142 | Language set to 143 | 144 | 145 | Welcome to the Emergency Services bot! How may I assist you? 146 | 147 | 148 | I encountered an error asking for an answer to your question. Maybe another question will work better. Is there something else I might be able to help with? 149 | 150 | 151 | Sorry, I wasn't able to find any helpful answers. 152 | 153 | 154 | More Information 155 | 156 | 157 | https://pbs.twimg.com/profile_images/378800000210718633/4a028ce30e9238501be08f97ddbfd9f8_400x400.png 158 | 159 | 160 | Emergency Services Bot 161 | 162 | -------------------------------------------------------------------------------- /Resources/Resources.es-US.Designer.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTBox/EmergencyServicesBot/69e91f06c6e66d4c397978209c232d7371c95ec6/Resources/Resources.es-US.Designer.cs -------------------------------------------------------------------------------- /Resources/Resources.es-US.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 | He aquí una respuesta que podría ayudar: 122 | 123 | 124 | Claro, ¿cómo puedo ayudar? Usted puede decir cosas como "¿Qué documentación necesito para hacer una reclamación de seguro?" O "¿Cómo puedo obtener ayuda de FEMA para obtener ayuda?". 125 | 126 | 127 | Obtener Respuestas 128 | 129 | 130 | 1 - Obtener Respuestas 131 | 132 | 133 | 2 - 🌐 Seleccione el idioma 134 | 135 | 136 | ¿Hay alguna otra pregunta que pueda tener para ayudarme? 137 | 138 | 139 | 🌐 Seleccione el idioma 140 | 141 | 142 | Idioma establecido para 143 | 144 | 145 | ¡ Bienvenido al bot de servicios de emergencia! ¿en qué puedo ayudarle? 146 | 147 | 148 | Encontré un error pidiendo una respuesta a tu pregunta. Tal vez otra pregunta funcionará mejor. ¿Hay algo más en lo que pueda ayudar? 149 | 150 | 151 | Lo siento, no pude encontrar respuestas útiles. 152 | 153 | 154 | Más información 155 | 156 | 157 | https://pbs.twimg.com/profile_images/378800000210718633/4a028ce30e9238501be08f97ddbfd9f8_400x400.png 158 | 159 | 160 | Servicios de emergencia bot 161 | 162 | -------------------------------------------------------------------------------- /Resources/Resources.fr-FR.Designer.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTBox/EmergencyServicesBot/69e91f06c6e66d4c397978209c232d7371c95ec6/Resources/Resources.fr-FR.Designer.cs -------------------------------------------------------------------------------- /Resources/Resources.fr-FR.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 | Voici une réponse qui je pense vous aidera : 122 | 123 | 124 | Bien sûr, Comment puis-je vous aider ? Vous pouvez demander quelque chose comme “Quelle démarche dois-je faire pour une déclaration d'assurance ?” ou “Comment puis-je contacter un centre d'urgence pour une assistance ?” 125 | 126 | 127 | Obtenir les réponses 128 | 129 | 130 | 1 - Obtenir les réponses 131 | 132 | 133 | 2 - 🌐 Sélectionner la langue 134 | 135 | 136 | Auriez-vous d'autres questions auxquelles je pourrais répondre ? 137 | 138 | 139 | 🌐 Sélectionner la langue 140 | 141 | 142 | Langue définie à 143 | 144 | 145 | Bienvenue dans le bot de Service d'Urgences ! Comment puis-je vous aider ? 146 | 147 | 148 | J'ai rencontré une erreur dans la demande de réponse à votre question. Peut-être une autre question marchera mieux. Y-a-t-il quelque chose d'autre que je puisse faire pour vous aider ? 149 | 150 | 151 | Désolé, je n'ai pas trouvé de réponse correspondant à votre demande. 152 | 153 | 154 | Plus d'informations 155 | 156 | 157 | https://pbs.twimg.com/profile_images/378800000210718633/4a028ce30e9238501be08f97ddbfd9f8_400x400.png 158 | 159 | 160 | Service d'Urgences bot 161 | 162 | -------------------------------------------------------------------------------- /Resources/Resources.zh-CN.Designer.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HTBox/EmergencyServicesBot/69e91f06c6e66d4c397978209c232d7371c95ec6/Resources/Resources.zh-CN.Designer.cs -------------------------------------------------------------------------------- /Resources/Resources.zh-CN.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 | 这是我认为可能有帮助的答案: 122 | 123 | 124 | 没问题,我可以怎么帮您? 您可以说 “我需要提供什么文件,如果我要保险索赔” 或 “如何获得FEMA协助?” 125 | 126 | 127 | 其他问题 128 | 129 | 130 | 1 - 其他问题 131 | 132 | 133 | 2 - 🌐 选择语言 134 | 135 | 136 | 还有别的问题吗? 137 | 138 | 139 | 🌐 选择语言 140 | 141 | 142 | 语言设为 143 | 144 | 145 | 欢迎来到急救服务机器人!我能帮你什么? 146 | 147 | 148 | 我遇到一个错误,要求您回答您的问题。也许另一个问题会更好。还有什么我可以帮助吗? 149 | 150 | 151 | 对不起,我无法找到任何有用的答案。 152 | 153 | 154 | 更多信息 155 | 156 | 157 | https://pbs.twimg.com/profile_images/378800000210718633/4a028ce30e9238501be08f97ddbfd9f8_400x400.png 158 | 159 | 160 | 紧急服务 Bot 161 | 162 | -------------------------------------------------------------------------------- /Web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /Web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /Web.config: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /azuredeploy.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "siteName": { 6 | "type": "string", 7 | "metadata": { 8 | "description": "The name of the web app that you wish to create." 9 | } 10 | }, 11 | "hostingPlanName": { 12 | "type": "string", 13 | "metadata": { 14 | "description": "The name of the App Service plan to use for hosting the web app." 15 | } 16 | }, 17 | "sku": { 18 | "type": "string", 19 | "allowedValues": [ 20 | "D1", 21 | "B1", 22 | "B2", 23 | "B3", 24 | "S1", 25 | "S2", 26 | "S3", 27 | "P1", 28 | "P2", 29 | "P3", 30 | "P4" 31 | ], 32 | "defaultValue": "B1", 33 | "metadata": { 34 | "description": "The pricing tier for the hosting plan." 35 | } 36 | }, 37 | "workerSize": { 38 | "type": "string", 39 | "allowedValues": [ 40 | "0", 41 | "1", 42 | "2" 43 | ], 44 | "defaultValue": "0", 45 | "metadata": { 46 | "description": "The instance size of the hosting plan (small, medium, or large)." 47 | } 48 | }, 49 | "repoURL": { 50 | "type": "string", 51 | "defaultValue": "https://github.com/HTBox/EmergencyServicesBot", 52 | "metadata": { 53 | "description": "The URL for the GitHub repository that contains the project to deploy." 54 | } 55 | }, 56 | "branch": { 57 | "type": "string", 58 | "defaultValue": "master", 59 | "metadata": { 60 | "description": "The branch of the GitHub repository to use." 61 | } 62 | }, 63 | "MicrosoftAppId": { 64 | "type": "string", 65 | "defaultValue": "", 66 | "metadata": { 67 | "description": "The name of the bot as displayed in the bot channel registration under settings => Configuration => Microsoft App Id" 68 | } 69 | }, 70 | "MicrosoftAppPassword": { 71 | "type": "string", 72 | "defaultValue": "", 73 | "metadata": { 74 | "description": "The password for your bot as displayed in the bot channel registration under settings => Configuration => manage" 75 | } 76 | }, 77 | "BotName": { 78 | "type": "string", 79 | "defaultValue": "", 80 | "metadata": { 81 | "description": "The name of your bot as displayed in the bot channel registration under settings => Bot handle" 82 | } 83 | }, 84 | "DirectlineSecret": { 85 | "type": "string", 86 | "defaultValue": "", 87 | "metadata": { 88 | "description": "Your bot's direct line secret as displayed in the bot channel registration under channels => direct line" 89 | } 90 | }, 91 | "QnAendpoint": { 92 | "type": "string", 93 | "defaultValue": "https://westus.api.cognitive.microsoft.com/qnamaker/v2.0/knowledgebases/", 94 | "metadata": { 95 | "description": "The endpoint of your QnA service found in your created service @ qnamaker.ai under settings => Deployment Details" 96 | } 97 | }, 98 | "QnAKnowledgebaseId": { 99 | "type": "string", 100 | "defaultValue": "", 101 | "metadata": { 102 | "description": "The knowledgebase id of your QnA service found in your created service @ qnamaker.ai under settings => Deployment Details" 103 | } 104 | }, 105 | "QnASubscriptionKey": { 106 | "type": "string", 107 | "defaultValue": "", 108 | "metadata": { 109 | "description": "The subscription key for your QnA service found in your created service @ qnamaker.ai under settings => Deployment Details" 110 | } 111 | }, 112 | "QnAKnowledgebaseLanguage": { 113 | "type": "string", 114 | "defaultValue": "en", 115 | "metadata": { 116 | "description": "The knowledge base language, set using a valid supported language code from https://docs.microsofttranslator.com/languages.html" 117 | } 118 | }, 119 | "QnAanswerThreshold": { 120 | "type": "string", 121 | "defaultValue": "30", 122 | "metadata": { 123 | "description": "Use this value to tune accuracy of found answers in the QnAKnowledgebase, higher values = higher accuracy" 124 | } 125 | }, 126 | "TranslatorEndpoint": { 127 | "type": "string", 128 | "defaultValue": "https://api.microsofttranslator.com/V2/Http.svc", 129 | "metadata": { 130 | "description": "The endpoint for the translator service, this value is unlikely to change" 131 | } 132 | }, 133 | "TranslatorApiKey": { 134 | "type": "string", 135 | "defaultValue": "", 136 | "metadata": { 137 | "description": "The api key for your translation service" 138 | } 139 | } 140 | }, 141 | "resources": [ 142 | { 143 | "apiVersion": "2015-08-01", 144 | "name": "[parameters('hostingPlanName')]", 145 | "type": "Microsoft.Web/serverfarms", 146 | "location": "[resourceGroup().location]", 147 | "sku": { 148 | "name": "[parameters('sku')]", 149 | "capacity": "[parameters('workerSize')]" 150 | }, 151 | "properties": { 152 | "name": "[parameters('hostingPlanName')]" 153 | } 154 | }, 155 | { 156 | "apiVersion": "2015-08-01", 157 | "name": "[parameters('siteName')]", 158 | "type": "Microsoft.Web/sites", 159 | "location": "[resourceGroup().location]", 160 | "dependsOn": [ 161 | "[resourceId('Microsoft.Web/serverfarms', parameters('hostingPlanName'))]" 162 | ], 163 | "properties": { 164 | "serverFarmId": "[parameters('hostingPlanName')]", 165 | "siteConfig": { 166 | "AlwaysOn": true 167 | } 168 | }, 169 | "resources": [ 170 | { 171 | "apiVersion": "2015-08-01", 172 | "name": "web", 173 | "type": "sourcecontrols", 174 | "dependsOn": [ 175 | "[resourceId('Microsoft.Web/Sites', parameters('siteName'))]" 176 | ], 177 | "properties": { 178 | "RepoUrl": "[parameters('repoURL')]", 179 | "branch": "[parameters('branch')]", 180 | "IsManualIntegration": true, 181 | "defaultDocuments": "index.js" 182 | } 183 | }, 184 | { 185 | "apiVersion": "2015-08-01", 186 | "name": "appsettings", 187 | "type": "config", 188 | "dependsOn": [ 189 | "[resourceId('Microsoft.Web/Sites', parameters('siteName'))]" 190 | ], 191 | "properties": { 192 | "WEBSITE_NODE_DEFAULT_VERSION" : "6.9.1", 193 | "MicrosoftAppId" : "[parameters('MicrosoftAppId')]", 194 | "MicrosoftAppPassword" : "[parameters('MicrosoftAppPassword')]", 195 | "BotName" : "[parameters('BotName')]", 196 | "DirectLineSecret" : "[parameters('DirectLineSecret')]", 197 | "QnAendpoint" : "[parameters('QnAendpoint')]", 198 | "QnAKnowledgebaseId" : "[parameters('QnAKnowledgebaseId')]", 199 | "QnASubscriptionKey" : "[parameters('QnASubscriptionKey')]", 200 | "QnAKnowledgebaseLanguage" : "[parameters('QnAKnowledgebaseLanguage')]", 201 | "QnAanswerThreshold" : "[parameters('QnAanswerThreshold')]", 202 | "TranslatorEndpoint" : "[parameters('TranslatorEndpoint')]", 203 | "TranslatorApiKey" : "[parameters('TranslatorApiKey')]" 204 | } 205 | } 206 | ] 207 | } 208 | ] 209 | } -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | set DEPLOYMENT_SOURCE= 5 | set IN_PLACE_DEPLOYMENT=1 6 | 7 | if exist ..\wwwroot\deploy.cmd ( 8 | pushd ..\wwwroot 9 | call deploy.cmd 10 | popd 11 | ) 12 | 13 | endlocal 14 | 15 | -------------------------------------------------------------------------------- /default.htm: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Emergency Services Bot 5 | 6 | 7 | 8 | 18 | 19 | 20 | 21 | 22 |

Emergency Services Bot Template

23 |

A bot which provides a Question and Answer Service via web and mobile channels with Multi-lingual support.

24 |

Here are some handy links to get started:

25 |

26 |

33 |

34 | 35 |
36 |
37 | 38 |
39 |
40 | Show Bot 41 |
42 |
43 | 44 | 45 | 46 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /deploy.cmd: -------------------------------------------------------------------------------- 1 | @if "%SCM_TRACE_LEVEL%" NEQ "4" @echo off 2 | 3 | :: ---------------------- 4 | :: KUDU Deployment Script 5 | :: Version: 1.0.15 6 | :: ---------------------- 7 | 8 | :: Prerequisites 9 | :: ------------- 10 | 11 | :: Verify node.js installed 12 | where node 2>nul >nul 13 | IF %ERRORLEVEL% NEQ 0 ( 14 | echo Missing node.js executable, please install node.js, if already installed make sure it can be reached from current environment. 15 | goto error 16 | ) 17 | 18 | :: Setup 19 | :: ----- 20 | 21 | setlocal enabledelayedexpansion 22 | 23 | SET ARTIFACTS=%~dp0%..\artifacts 24 | 25 | IF NOT DEFINED DEPLOYMENT_SOURCE ( 26 | SET DEPLOYMENT_SOURCE=%~dp0%. 27 | ) 28 | 29 | IF NOT DEFINED DEPLOYMENT_TARGET ( 30 | SET DEPLOYMENT_TARGET=%ARTIFACTS%\wwwroot 31 | ) 32 | 33 | IF NOT DEFINED NEXT_MANIFEST_PATH ( 34 | SET NEXT_MANIFEST_PATH=%ARTIFACTS%\manifest 35 | 36 | IF NOT DEFINED PREVIOUS_MANIFEST_PATH ( 37 | SET PREVIOUS_MANIFEST_PATH=%ARTIFACTS%\manifest 38 | ) 39 | ) 40 | 41 | IF NOT DEFINED KUDU_SYNC_CMD ( 42 | :: Install kudu sync 43 | echo Installing Kudu Sync 44 | call npm install kudusync -g --silent 45 | IF !ERRORLEVEL! NEQ 0 goto error 46 | 47 | :: Locally just running "kuduSync" would also work 48 | SET KUDU_SYNC_CMD=%appdata%\npm\kuduSync.cmd 49 | ) 50 | IF NOT DEFINED DEPLOYMENT_TEMP ( 51 | SET DEPLOYMENT_TEMP=%temp%\___deployTemp%random% 52 | SET CLEAN_LOCAL_DEPLOYMENT_TEMP=true 53 | ) 54 | 55 | IF DEFINED CLEAN_LOCAL_DEPLOYMENT_TEMP ( 56 | IF EXIST "%DEPLOYMENT_TEMP%" rd /s /q "%DEPLOYMENT_TEMP%" 57 | mkdir "%DEPLOYMENT_TEMP%" 58 | ) 59 | 60 | IF DEFINED MSBUILD_PATH goto MsbuildPathDefined 61 | SET MSBUILD_PATH=%ProgramFiles(x86)%\MSBuild\14.0\Bin\MSBuild.exe 62 | :MsbuildPathDefined 63 | 64 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 65 | :: Deployment 66 | :: ---------- 67 | 68 | echo Handling .NET Web Application deployment. 69 | 70 | :: 1. Restore NuGet packages 71 | IF /I "EmergencyServicesBot.sln" NEQ "" ( 72 | call :ExecuteCmd nuget restore "%DEPLOYMENT_SOURCE%\EmergencyServicesBot.sln" 73 | IF !ERRORLEVEL! NEQ 0 goto error 74 | ) 75 | 76 | :: 2. Build to the temporary path 77 | IF /I "%IN_PLACE_DEPLOYMENT%" NEQ "1" ( 78 | call :ExecuteCmd "%MSBUILD_PATH%" "%DEPLOYMENT_SOURCE%\EmergencyServicesBot.csproj" /nologo /verbosity:m /t:Build /t:pipelinePreDeployCopyAllFilesToOneFolder /p:_PackageTempDir="%DEPLOYMENT_TEMP%";AutoParameterizationWebConfigConnectionStrings=false;Configuration=Release;UseSharedCompilation=false /p:SolutionDir="%DEPLOYMENT_SOURCE%\.\\" %SCM_BUILD_ARGS% 79 | ) ELSE ( 80 | call :ExecuteCmd "%MSBUILD_PATH%" "%DEPLOYMENT_SOURCE%\EmergencyServicesBot.csproj" /nologo /verbosity:m /t:Build /p:AutoParameterizationWebConfigConnectionStrings=false;Configuration=Release;UseSharedCompilation=false /p:SolutionDir="%DEPLOYMENT_SOURCE%\.\\" %SCM_BUILD_ARGS% 81 | ) 82 | 83 | IF !ERRORLEVEL! NEQ 0 goto error 84 | 85 | :: 3. KuduSync 86 | IF /I "%IN_PLACE_DEPLOYMENT%" NEQ "1" ( 87 | call :ExecuteCmd "%KUDU_SYNC_CMD%" -v 50 -f "%DEPLOYMENT_TEMP%" -t "%DEPLOYMENT_TARGET%" -n "%NEXT_MANIFEST_PATH%" -p "%PREVIOUS_MANIFEST_PATH%" -i ".git;.hg;.deployment;deploy.cmd" 88 | IF !ERRORLEVEL! NEQ 0 goto error 89 | ) 90 | 91 | :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 92 | goto end 93 | 94 | :: Execute command routine that will echo out when error 95 | :ExecuteCmd 96 | setlocal 97 | set _CMD_=%* 98 | call %_CMD_% 99 | if "%ERRORLEVEL%" NEQ "0" echo Failed exitCode=%ERRORLEVEL%, command=%_CMD_% 100 | exit /b %ERRORLEVEL% 101 | 102 | :error 103 | endlocal 104 | echo An error has occurred during web site deployment. 105 | call :exitSetErrorLevel 106 | call :exitFromFunction 2>nul 107 | 108 | :exitSetErrorLevel 109 | exit /b 1 110 | 111 | :exitFromFunction 112 | () 113 | 114 | :end 115 | endlocal 116 | echo Finished successfully. 117 | -------------------------------------------------------------------------------- /packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /publish.cmd: -------------------------------------------------------------------------------- 1 | nuget restore 2 | msbuild EmergencyServicesBot.sln -p:DeployOnBuild=true -p:PublishProfile=qnatesthou-Web-Deploy.pubxml -p:Password=hvcrMqEdiaM20RgfipBPLlXstyZvTw81Q0NuDnxzebdtYHpGt82FLagD3Lv1 3 | 4 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Emergency Services Bot 2 | 3 | A bot designed to allow for rapid deployment of a question and answer service with support for multiple languages and accessible over web and SMS. 4 | 5 | ## Background 6 | 7 | Disaster response scenarios require time-critical response to provide relief to affected individuals. Inspired by the aftermath of Hurricane Harvey, this project serves as a disaster response question and answer service to provide assistance for victims of natural disaster. The service allows for an operator to quickly plug in question and answer responses into a knowledge base which become auto-translated to end users of the service. The project also provides out of box support for Web and SMS allowing for increased availability to individuals who may not have internet access. 8 | 9 | ## Demo 10 | 11 | Try a live demonstration of the Emergency Services Bot by visiting https://emergencyservicesbot-test.azurewebsites.net/ or texting "hi" to +1-484-552-4268 12 | 13 | ## Prerequisites 14 | 15 | 1. An active [Microsoft Azure Subscription](http://azure.com) 16 | 2. A registered bot at [botframework.com](https://dev.botframework.com/) 17 | 3. An active QnA Service from [qnamaker.ai](http://qnamaker.ai) 18 | 4. An active [Translator Text API Key](https://www.microsoft.com/en-us/translator) 19 | 20 | ## QuickStart 21 | 22 | In some steps you will be asked to obtain and save a key value pair. Please be aware of this as you go through the steps and keep these values in a safe place. 23 | 24 | 1. Create or use an existing Azure Subscription 25 | 26 | Free subscriptions can be obtained @ https://azure.microsoft.com/en-us/free/ 27 | 28 | 2. Create an active QnA Service at [qnamaker.ai](http://qnamaker.ai) 29 | 30 | 31 | 32 | You may now begin adding questions to your knowledge base 33 | 34 | 35 | 36 | Save / Retrain, and Publish your changes when you are ready 37 | 38 | 39 | 40 | Obtain and save the following from values from Settings => Deployment Details 41 | 42 | (top value is for "QnAKnowledgebaseId", bottom is "QnASubscriptionKey") 43 | 44 | 45 | 46 | 3. Create a Translator Text API Service @ http://azure.com 47 | 48 | - 49 | 50 | Obtain and save the value for the "TranslatorApiKey" 51 | 52 | 53 | 54 | 4. Create a bot channel registration within your Azure subscription 55 | 56 | 57 | 58 | Within the Bot Channel Registration, click Settings to obtain and Save the value in "Bot handle" as "BotName" and the value in "Microsoft App Id" as "MicrosoftAppId" 59 | 60 | 61 | 62 | Near the "Microsoft App Id" click "Manage" then "Generate New Password" to obtain and Save as "MicrosoftAppPassword" 63 | 64 | 65 | 66 | Select "Channels", then add a DirectLine Channel and obtain and save one of the secret keys as "DirectLineSecret" 67 | 68 | 69 | 70 | 5. Verify that you have a saved value for "QnAKnowledgebaseId", "QnASubscriptionKey", "BotName", "MicrosoftAppId", "MicrosoftAppPassword", and "DirectLineSecret" 71 | 72 | Once you have all of these [values](./Docs/azure-deploy-parameters.md), click the "Deploy to Azure" Button below and supply the values where indicated: 73 | 74 | [![Deploy to Azure](http://azuredeploy.net/deploybutton.png)](https://azuredeploy.net/) 75 | 76 | 77 | 78 | Ensure that deployment is successful 79 | 80 | 81 | 82 | Head back to the bot channel from step 4, click Settings and update the value for Messaging Endpoint to your newly deployed url + "/api/messages" 83 | 84 | 85 | 86 | Reload your deployed website and you should see the bot operating using the configured knowledgebase: 87 | 88 | 89 | 90 | 91 | 92 | If you do not have success, verify that the appropriate values are being supplied for the website's AppSettings 93 | 94 | 95 | 96 | ## Enable Mobile SMS Channel 97 | 98 | To enable SMS messaging with the bot, follow the instructions @ https://docs.microsoft.com/en-us/bot-framework/bot-service-channel-connect-twilio 99 | 100 | For other channels, see: https://docs.microsoft.com/en-us/bot-framework/bot-service-manage-channels 101 | 102 | ## Developing Locally in Visual Studio 103 | 104 | 1. Clone this repo 105 | 2. Open EmergencyServicesBot.sln in Visual Studio 106 | 3. Add appropriate keypairs from the QuickStart steps into "Web.config" (Note: Local development does not require use of "MicrosoftAppId" and "MicrosoftAppPassword") 107 | 4. Build and run the bot 108 | 5. Download and run [botframework-emulator](https://emulator.botframework.com/) 109 | 6. Connect the emulator to http://localhost:{deploymentPort} 110 | 111 | ### Publishing to Azure from Visual Studio 112 | 113 | In Visual Studio, right click on EmergencyServicesBot and select 'Publish' 114 | 115 | ## Styling the bot 116 | 117 | Within the include default.htm, there is an example of a landing page for the bot: 118 | 119 | * Custom styling of the bot's look and feel are controlled by "/Content/botchat.css" 120 | * Custom bot behavior is controlled by "/Scripts/botchat.js" 121 | 122 | By modifying these files, you can give the bot a custom look and feel to accommodate your use case. 123 | 124 | 125 | 126 | --------------------------------------------------------------------------------