├── .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 |
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 | [](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 |
--------------------------------------------------------------------------------