|
13 | 13 |
|
14 | 14 | 'use strict'; |
15 | 15 |
|
16 | | -// This sample shows usage of the available SSML elements |
17 | | - |
18 | | -const { DialogflowApp } = require('actions-on-google'); |
19 | 16 | const functions = require('firebase-functions'); |
| 17 | +const app = require('./app'); |
20 | 18 |
|
21 | | -process.env.DEBUG = 'actions-on-google:*'; |
22 | | - |
23 | | -/** |
24 | | - * Sanitize template literal inputs by escaping characters into XML entities to use in SSML |
25 | | - * Also normalize the extra spacing for better text rendering in SSML |
26 | | - * A tag function used by ES6 tagged template literals |
27 | | - * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_template_literals |
28 | | - * |
29 | | - * @example |
30 | | - * const equation = '"1 + 1 > 1"'; |
31 | | - * const response = ssml` |
32 | | - * <speak> |
33 | | - * ${equation} |
34 | | - * </speak> |
35 | | - * `; |
36 | | - * // Equivalent to ssml`\n <speak>\n ${equation}\n </speak>\n` |
37 | | - * console.log(response); |
38 | | - * |
39 | | - * Prints: |
40 | | - * `<speak> |
41 | | - * "1 + 1 > 1" |
42 | | - * </speak>` |
43 | | - * Equivalent to |
44 | | - * '<speak>\n "1 + 1 > 1"\n</speak>' |
45 | | - * |
46 | | - * @param {TemplateStringsArray} template Non sanitized constant strings in the template literal |
47 | | - * @param {Array<string>} inputs Computed expressions to be sanitized surrounded by ${} |
48 | | - */ |
49 | | -const ssml = (template, ...inputs) => { |
50 | | - // Generate the raw escaped string |
51 | | - const raw = template.reduce((out, str, i) => i |
52 | | - ? out + ( |
53 | | - inputs[i - 1] |
54 | | - .replace(/&/g, '&') |
55 | | - .replace(/</g, '<') |
56 | | - .replace(/>/g, '>') |
57 | | - .replace(/"/g, '"') |
58 | | - ) + str |
59 | | - : str |
60 | | - ); |
61 | | - // Trim out new lines at the start and end but keep indentation |
62 | | - const trimmed = raw |
63 | | - .replace(/^\s*\n(\s*)<speak>/, '1ドル<speak>') |
64 | | - .replace(/<\/speak>\s+$/, '</speak>'); |
65 | | - // Remove extra indentation |
66 | | - const lines = trimmed.split('\n'); |
67 | | - const indent = /^\s*/.exec(lines[0])[0]; |
68 | | - const match = new RegExp(`^${indent}`); |
69 | | - return lines.map(line => line.replace(match, '')).join('\n'); |
70 | | -}; |
71 | | - |
72 | | -const examples = { |
73 | | - break: ssml` |
74 | | - <speak> |
75 | | - Step 1, take a deep breath. <break time="200ms" strength="weak"/> |
76 | | - Step 2, exhale. |
77 | | - </speak> |
78 | | - `, |
79 | | - 'say-as': ssml` |
80 | | - <speak> |
81 | | - This interprets "12345" normally. |
82 | | - This interprets "<say-as interpret-as="cardinal">12345</say-as>" as a cardinal. |
83 | | - This interprets "1" normally. |
84 | | - This interprets "<say-as interpret-as="ordinal">1</say-as>" as an ordinal. |
85 | | - This interprets "can" normally. |
86 | | - This interprets "<say-as interpret-as="characters">can</say-as>" as characters. |
87 | | - This interprets "5+1/2" normally. |
88 | | - This interprets "<say-as interpret-as="fraction">5+1/2</say-as>" as a fraction. |
89 | | - This interprets "censored" normally. |
90 | | - This interprets "<say-as interpret-as="expletive">censored</say-as>" as an expletive. |
91 | | - This interprets "10 foot" normally. |
92 | | - This interprets "<say-as interpret-as="unit">10 foot</say-as>" as a unit. |
93 | | - This interprets "abcdefg" normally. |
94 | | - This interprets "<say-as interpret-as="verbatim">abcdefg</say-as>" as verbatim. |
95 | | - This interprets "1960年09月10日" normally. |
96 | | - This interprets "<say-as interpret-as="date" format="ymd">1960年09月10日</say-as>" as a date. |
97 | | - This interprets "2:30pm" normally. |
98 | | - This interprets "<say-as interpret-as="time" format="hms12">2:30pm</say-as>" as a time. |
99 | | - This interprets "(781) 771-7777" normally. |
100 | | - This interprets "<say-as interpret-as="telephone" format="1"> |
101 | | - (781) 771-7777 |
102 | | - </say-as>" as a telephone number. |
103 | | - </speak> |
104 | | - `, |
105 | | - audio: ssml` |
106 | | - <speak> |
107 | | - <audio src="https://actions.google.com/sounds/v1/animals/cat_purr_close.ogg"> |
108 | | - <desc>Sound of a cat purring.</desc> |
109 | | - Audio resource for a cat purring failed to load. |
110 | | - </audio> |
111 | | - </speak> |
112 | | - `, |
113 | | - paragraph: ssml` |
114 | | - <speak> |
115 | | - <p> |
116 | | - <s>This is sentence one.</s> |
117 | | - <s>This is sentence two.</s> |
118 | | - </p> |
119 | | - </speak> |
120 | | - `, |
121 | | - sub: ssml` |
122 | | - <speak> |
123 | | - This is speaking "W3C" normally. |
124 | | - This is speaking "<sub alias="World Wide Web Consortium">W3C</sub>" using the sub tag. |
125 | | - </speak> |
126 | | - `, |
127 | | - prosody: ssml` |
128 | | - <speak> |
129 | | - <prosody rate="100%" pitch="-2st"> |
130 | | - My name is <prosody rate="slow">Wonder Woman</prosody>. |
131 | | - </prosody> |
132 | | - <break time="0.5s" /> |
133 | | - <prosody pitch="+20st">Hi, my name is lowly worm.</prosody> |
134 | | - <prosody pitch="-10st">Hi, my name is huckleberry cat.</prosody> |
135 | | - <break time="1.5s" />Was that fun? |
136 | | - <prosody rate="x-fast">Hi I'm speaking fast.</prosody> |
137 | | - <prosody rate="x-slow">I'm speaking slow.</prosody> |
138 | | - <prosody volume="soft">I'm speaking softly.</prosody> |
139 | | - <prosody volume="x-loud">I'm speaking loud</prosody> |
140 | | - <prosody pitch="+20st">I'm speaking high.</prosody> |
141 | | - <prosody pitch="-20st">I'm speaking deep.</prosody> |
142 | | - </speak> |
143 | | - `, |
144 | | - emphasis: ssml` |
145 | | - <speak> |
146 | | - I would like to emphasize the importance of SSML. |
147 | | - I told you to pick up those toys an hour ago. |
148 | | - <emphasis>I told you to pick up those toys an hour ago.</emphasis> |
149 | | - <emphasis level="strong">I told you to pick up those toys an hour ago.</emphasis> |
150 | | - <emphasis level="moderate">I told you to pick up those toys an hour ago.</emphasis> |
151 | | - <emphasis level="reduced">I told you to pick up those toys an hour ago.</emphasis> |
152 | | - <emphasis level="none">I told you to pick up those toys an hour ago.</emphasis> |
153 | | - </speak> |
154 | | - `, |
155 | | - speed: ssml` |
156 | | - <speak> |
157 | | - This is without prosody. |
158 | | - <prosody rate="100%">This is speaking at 100% rate.</prosody> |
159 | | - <prosody rate="150%">This is speaking at 150% rate.</prosody> |
160 | | - <prosody rate="foo">This is speaking at normal rate.</prosody> |
161 | | - <prosody rate="200%">This is speaking at 200% rate.</prosody> |
162 | | - <prosody rate="medium">This is speaking at medium rate.</prosody> |
163 | | - <prosody rate="300%">This is speaking at 300% rate.</prosody> |
164 | | - <prosody rate="default">This is speaking at default rate.</prosody> |
165 | | - <prosody rate="75%">This is speaking at 75% rate.</prosody> |
166 | | - <prosody rate="50%">This is speaking at 50% rate.</prosody> |
167 | | - <prosody rate="25%">This is speaking at 25% rate.</prosody> |
168 | | - <prosody rate="10%">This is speaking at 10% rate.</prosody> |
169 | | - </speak> |
170 | | - `, |
171 | | - volume: ssml` |
172 | | - <speak> |
173 | | - This is without prosody. |
174 | | - <prosody volume="+5dB">This is speaking at +5dB volume.</prosody> |
175 | | - <prosody volume="100%">This is speaking at 100% volume.</prosody> |
176 | | - <prosody volume="loud">This is speaking at loud volume.</prosody> |
177 | | - <prosody volume="foo">This is speaking at normal volume.</prosody> |
178 | | - <prosody volume="+10dB">This is speaking at +10dB volume.</prosody> |
179 | | - <prosody volume="medium">This is speaking at medium volume.</prosody> |
180 | | - <prosody volume="x-loud">This is speaking at x-loud volume.</prosody> |
181 | | - <prosody volume="default">This is speaking at default volume.</prosody> |
182 | | - <prosody volume="-5dB">This is speaking at -5dB volume.</prosody> |
183 | | - <prosody volume="-10dB">This is speaking at -10dB volume.</prosody> |
184 | | - <prosody volume="soft">This is speaking at soft volume.</prosody> |
185 | | - <prosody volume="x-soft">This is speaking at x-soft volume.</prosody> |
186 | | - </speak> |
187 | | - `, |
188 | | - pitch: ssml` |
189 | | - <speak> |
190 | | - This is without prosody. |
191 | | - <prosody pitch="+6st">This is speaking at +6 semitones pitch.</prosody> |
192 | | - <prosody pitch="foo">This is speaking at normal pitch.</prosody> |
193 | | - <prosody pitch="high">This is speaking at high pitch.</prosody> |
194 | | - <prosody pitch="+0st">This is speaking at +0 semitones pitch.</prosody> |
195 | | - <prosody pitch="medium">This is speaking at medium pitch.</prosody> |
196 | | - <prosody pitch="default">This is speaking at default pitch.</prosody> |
197 | | - <prosody pitch="+200%">This is speaking at +200% pitch.</prosody> |
198 | | - <prosody pitch="+12st">This is speaking at +12 semitones pitch.</prosody> |
199 | | - <prosody pitch="x-high">This is speaking at x-high pitch.</prosody> |
200 | | - <prosody pitch="-6st">This is speaking at -6 semitones pitch.</prosody> |
201 | | - <prosody pitch="low">This is speaking at low pitch.</prosody> |
202 | | - <prosody pitch="-12st">This is speaking at -12 semitones pitch.</prosody> |
203 | | - <prosody pitch="x-low">This is speaking at x-low pitch.</prosody> |
204 | | - </speak> |
205 | | - `, |
206 | | - layered: ssml` |
207 | | - <speak> |
208 | | - The key element for layered sound mixing is <sub alias="par">${'<par>'}</sub> |
209 | | - (as in "parallel") which inserts a mixed sound at the current point of the TTS. |
210 | | - It is similar to the <sub alias="paragraph">${'<p>'}</sub> |
211 | | - element with an important difference of not displaying |
212 | | - the text content in chat bubbles on surfaces with displays. |
213 | | - <par> |
214 | | - <media xml:id="first_thing" begin="2.5s"> |
215 | | - <speak> |
216 | | - This media element contains a <sub alias="speak element">${'<speak>'}</sub> for TTS. |
217 | | - It has an <say-as interpret-as="verbatim">xml:id</say-as> attribute so that other |
218 | | - <sub alias="media">${'<media>'}</sub> elements can refer to it. |
219 | | - There is also a "begin" attribute that delays the start time by 2.5 seconds. |
220 | | - Millisecond units are also supported by the |
221 | | - <say-as interpret-as="letters">ms</say-as> suffix. |
222 | | - </speak> |
223 | | - </media> |
224 | | - <media xml:id="second_thing" soundLevel="-1dB" repeatCount="3"> |
225 | | - <audio src="https://actions.google.com/sounds/v1/cartoon/cartoon_boing.ogg"> |
226 | | - The sound source for this <sub alias="audio">${'<audio>'}</sub> element is missing. |
227 | | - Find more sounds at https://developers.google.com/actions/tools/sound-library. |
228 | | - </audio> |
229 | | - </media> |
230 | | - <media xml:id="last_thing" begin="first_thing.end + 1234ms"> |
231 | | - <speak> |
232 | | - This TTS starts <say-as interpret-as="units">1234 milliseconds</say-as> |
233 | | - after the end of the media element with the |
234 | | - <say-as interpret-as="verbatim">xml:id</say-as> equal to "first_thing". |
235 | | - </speak> |
236 | | - </media> |
237 | | - </par> |
238 | | - </speak> |
239 | | - ` |
240 | | -}; |
241 | | - |
242 | | -/** Dialogflow Actions {@link https://dialogflow.com/docs/actions-and-parameters#actions} */ |
243 | | -const Actions = { |
244 | | - TELL_EXAMPLE: 'tell.example', |
245 | | - UNRECOGNIZED_DEEP_LINK: 'input.unknown', |
246 | | - WELCOME: 'input.welcome' |
247 | | -}; |
248 | | -/** Dialogflow Parameters {@link https://dialogflow.com/docs/actions-and-parameters#parameters} */ |
249 | | -const Parameters = { |
250 | | - ELEMENT: 'element' |
251 | | -}; |
252 | | - |
253 | | -const baseResponses = { |
254 | | - askExample: 'Ask me for an example of a SSML element.' |
255 | | -}; |
256 | | - |
257 | | -const elements = Object.keys(examples); |
258 | | - |
259 | | -const completeResponses = { |
260 | | - examplesList: `You can ask me about ${elements.slice(0, elements.length - 1).join(', ')}` + |
261 | | - `, and ${elements[elements.length - 1]}.`, |
262 | | - didNotUnderstand: `Sorry, I didn't understand you. ${baseResponses.askExample}.`, |
263 | | - welcome: `Welcome! ${baseResponses.askExample} ` + |
264 | | - `You can say "give me an example of the prosody element".`, |
265 | | - /** @param {string} element */ |
266 | | - leadToExample: element => `Ok, here's an SSML example of ${element}.` |
267 | | -}; |
268 | | - |
269 | | -const actionMap = new Map(); |
270 | | -actionMap.set(Actions.WELCOME, /** @param {DialogflowApp} app */ app => { |
271 | | - const richResponse = app.buildRichResponse() |
272 | | - .addSimpleResponse(completeResponses.welcome) |
273 | | - .addSimpleResponse(completeResponses.examplesList); |
274 | | - app.ask(richResponse); |
275 | | -}); |
276 | | -actionMap.set(Actions.TELL_EXAMPLE, /** @param {DialogflowApp} app */ app => { |
277 | | - const element = app.getArgument(Parameters.ELEMENT); |
278 | | - if (!element) { |
279 | | - const richResponse = app.buildRichResponse() |
280 | | - .addSimpleResponse(completeResponses.didNotUnderstand) |
281 | | - .addSimpleResponse(completeResponses.examplesList); |
282 | | - return app.ask(richResponse); |
283 | | - } |
284 | | - const richResponse = app.buildRichResponse() |
285 | | - .addSimpleResponse(completeResponses.leadToExample(element)) |
286 | | - .addSimpleResponse(examples[element]); |
287 | | - app.ask(richResponse); |
288 | | -}); |
289 | | -actionMap.set(Actions.UNRECOGNIZED_DEEP_LINK, /** @param {DialogflowApp} app */ app => { |
290 | | - const richResponse = app.buildRichResponse() |
291 | | - .addSimpleResponse(completeResponses.didNotUnderstand) |
292 | | - .addSimpleResponse(completeResponses.examplesList); |
293 | | - app.ask(richResponse); |
294 | | -}); |
295 | | - |
296 | | -/** |
297 | | - * The entry point to handle a http request |
298 | | - * @param {Request} request An Express like Request object of the HTTP request |
299 | | - * @param {Response} response An Express like Response object to send back data |
300 | | - */ |
301 | | -const ssmlExamples = functions.https.onRequest((request, response) => { |
302 | | - const app = new DialogflowApp({ request, response }); |
303 | | - console.log('Headers', JSON.stringify(request.headers, null, 2)); |
304 | | - console.log('Body', JSON.stringify(request.body, null, 2)); |
305 | | - app.handleRequest(actionMap); |
306 | | -}); |
307 | | - |
308 | | -module.exports = { |
309 | | - ssmlExamples |
310 | | -}; |
| 19 | +module.exports.ssmlExampleAction = functions.https.onRequest(app); |
0 commit comments