UNPKG

12.5 kBJavaScriptView Raw
1"use strict";
2/**
3 * Copyright 2018 Google Inc. All Rights Reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17Object.defineProperty(exports, "__esModule", { value: true });
18exports.Conversation = exports.UnauthorizedError = void 0;
19const surface_1 = require("./surface");
20const user_1 = require("./user");
21const response_1 = require("./response");
22const helper_1 = require("./helper");
23const argument_1 = require("./argument");
24const device_1 = require("./device");
25const input_1 = require("./input");
26const canvas_1 = require("./canvas");
27/**
28 * Throw an UnauthorizedError in an intent handler to make the library
29 * respond with a HTTP 401 Status Code.
30 *
31 * @example
32 * ```javascript
33 * const app = dialogflow()
34 *
35 * // If using Actions SDK:
36 * // const app = actionssdk()
37 *
38 * app.intent('intent', conv => {
39 * // ...
40 *
41 * // given a function to check if a user auth is still valid
42 * const valid = checkUserAuthValid(conv)
43 * if (!valid) {
44 * throw new UnauthorizedError()
45 * }
46 *
47 * // ...
48 * })
49 *
50 * ```
51 *
52 * @public
53 */
54class UnauthorizedError extends Error {
55}
56exports.UnauthorizedError = UnauthorizedError;
57/** @public */
58class Conversation {
59 /** @hidden */
60 constructor(options = {}) {
61 /** @public */
62 this.responses = [];
63 /** @public */
64 this.expectUserResponse = true;
65 /** @public */
66 this.digested = false;
67 /**
68 * Set reprompts when users don't provide input to this action (no-input errors).
69 * Each reprompt represents as the {@link SimpleResponse}, but raw strings also can be specified
70 * for convenience (they're passed to the constructor of {@link SimpleResponse}).
71 * Notice that this value is not kept over conversations. Thus, it is necessary to set
72 * the reprompts per each conversation response.
73 *
74 * @example
75 * ```javascript
76 *
77 * app.intent('actions.intent.MAIN', conv => {
78 * conv.noInputs = [
79 * 'Are you still there?',
80 * 'Hello?',
81 * new SimpleResponse({
82 * text: 'Talk to you later. Bye!',
83 * speech: '<speak>Talk to you later. Bye!</speak>'
84 * })
85 * ]
86 * conv.ask('What's your favorite color?')
87 * })
88 * ```
89 *
90 * @public
91 */
92 this.noInputs = [];
93 /**
94 * Sets speech biasing options.
95 *
96 * @example
97 * ``` javascript
98 *
99 * app.intent('actions.intent.MAIN', conv => {
100 * conv.speechBiasing = ['red', 'blue', 'green']
101 * conv.ask('What is your favorite color out of red, blue, and green?')
102 * })
103 * ```
104 *
105 * @public
106 */
107 this.speechBiasing = [];
108 /** @hidden */
109 this._responded = false;
110 /** @hidden */
111 this._ordersv3 = false;
112 const { request = {}, headers = {}, init = {}, ordersv3 = false } = options;
113 this.request = request;
114 this.headers = headers;
115 this._init = init;
116 this._ordersv3 = ordersv3;
117 this.sandbox = !!this.request.isInSandbox;
118 const { inputs = [], conversation = {} } = this.request;
119 const [input = {}] = inputs;
120 const { rawInputs = [] } = input;
121 this.input = new input_1.Input(rawInputs[0]);
122 this.surface = new surface_1.Surface(this.request.surface);
123 this.available = new surface_1.Available(this.request.availableSurfaces);
124 this.user = new user_1.User(this.request.user, this._init.storage);
125 this.arguments = new argument_1.Arguments(input.arguments);
126 this.device = new device_1.Device(this.request.device);
127 this.canvas = new canvas_1.Canvas(input);
128 this.id = conversation.conversationId;
129 this.type = conversation.type;
130 this.screen = this.surface.capabilities.has('actions.capability.SCREEN_OUTPUT');
131 }
132 /** @public */
133 json(json) {
134 this._raw = json;
135 this._responded = true;
136 return this;
137 }
138 /** @public */
139 add(...responses) {
140 if (this.digested) {
141 throw new Error('Response has already been sent. ' +
142 'Is this being used in an async call that was not ' +
143 'returned as a promise to the intent handler?');
144 }
145 this.responses.push(...responses);
146 this._responded = true;
147 return this;
148 }
149 /**
150 * Asks to collect user's input. All user's queries need to be sent to the app.
151 * {@link https://developers.google.com/actions/policies/general-policies#user_experience|
152 * The guidelines when prompting the user for a response must be followed at all times}.
153 *
154 * @example
155 * ```javascript
156 *
157 * // Actions SDK
158 * const app = actionssdk()
159 *
160 * app.intent('actions.intent.MAIN', conv => {
161 * const ssml = '<speak>Hi! <break time="1"/> ' +
162 * 'I can read out an ordinal like <say-as interpret-as="ordinal">123</say-as>. ' +
163 * 'Say a number.</speak>'
164 * conv.ask(ssml)
165 * })
166 *
167 * app.intent('actions.intent.TEXT', (conv, input) => {
168 * if (input === 'bye') {
169 * return conv.close('Goodbye!')
170 * }
171 * const ssml = `<speak>You said, <say-as interpret-as="ordinal">${input}</say-as></speak>`
172 * conv.ask(ssml)
173 * })
174 *
175 * // Dialogflow
176 * const app = dialogflow()
177 *
178 * app.intent('Default Welcome Intent', conv => {
179 * conv.ask('Welcome to action snippets! Say a number.')
180 * })
181 *
182 * app.intent('Number Input', (conv, {num}) => {
183 * conv.close(`You said ${num}`)
184 * })
185 * ```
186 *
187 * @param responses A response fragment for the library to construct a single complete response
188 * @public
189 */
190 ask(...responses) {
191 this.expectUserResponse = true;
192 return this.add(...responses);
193 }
194 /**
195 * Have Assistant render the speech response and close the mic.
196 *
197 * @example
198 * ```javascript
199 *
200 * // Actions SDK
201 * const app = actionssdk()
202 *
203 * app.intent('actions.intent.MAIN', conv => {
204 * const ssml = '<speak>Hi! <break time="1"/> ' +
205 * 'I can read out an ordinal like <say-as interpret-as="ordinal">123</say-as>. ' +
206 * 'Say a number.</speak>'
207 * conv.ask(ssml)
208 * })
209 *
210 * app.intent('actions.intent.TEXT', (conv, input) => {
211 * if (input === 'bye') {
212 * return conv.close('Goodbye!')
213 * }
214 * const ssml = `<speak>You said, <say-as interpret-as="ordinal">${input}</say-as></speak>`
215 * conv.ask(ssml)
216 * })
217 *
218 * // Dialogflow
219 * const app = dialogflow()
220 *
221 * app.intent('Default Welcome Intent', conv => {
222 * conv.ask('Welcome to action snippets! Say a number.')
223 * })
224 *
225 * app.intent('Number Input', (conv, {num}) => {
226 * conv.close(`You said ${num}`)
227 * })
228 * ```
229 *
230 * @param responses A response fragment for the library to construct a single complete response
231 * @public
232 */
233 close(...responses) {
234 this.expectUserResponse = false;
235 return this.add(...responses);
236 }
237 /** @public */
238 response() {
239 if (!this._responded) {
240 throw new Error('No response has been set. ' +
241 'Is this being used in an async call that was not ' +
242 'returned as a promise to the intent handler?');
243 }
244 if (this.digested) {
245 throw new Error('Response has already been digested');
246 }
247 this.digested = true;
248 const { expectUserResponse } = this;
249 let richResponse = new response_1.RichResponse();
250 let expectedIntent;
251 let requireSimpleResponse = false;
252 for (const response of this.responses) {
253 if (typeof response === 'string') {
254 richResponse.add(response);
255 continue;
256 }
257 if (response instanceof helper_1.Helper) {
258 if (!(response instanceof helper_1.SoloHelper)) {
259 requireSimpleResponse = true;
260 }
261 if (this._ordersv3) {
262 let type = null;
263 if (response instanceof helper_1.TransactionDecision) {
264 type =
265 'type.googleapis.com/google.actions.transactions.v3.TransactionDecisionValueSpec';
266 }
267 else if (response instanceof helper_1.TransactionRequirements) {
268 type =
269 'type.googleapis.com/google.actions.transactions.v3.TransactionRequirementsCheckSpec';
270 }
271 if (type !== null) {
272 response.inputValueData['@type'] = type;
273 }
274 }
275 expectedIntent = response;
276 continue;
277 }
278 if (response instanceof response_1.RichResponse) {
279 richResponse = response;
280 continue;
281 }
282 if (response instanceof response_1.Suggestions) {
283 requireSimpleResponse = true;
284 richResponse.addSuggestion(response);
285 continue;
286 }
287 if (response instanceof response_1.Image) {
288 requireSimpleResponse = true;
289 richResponse.add(new response_1.BasicCard({ image: response }));
290 continue;
291 }
292 if (response instanceof response_1.MediaObject) {
293 requireSimpleResponse = true;
294 richResponse.add(new response_1.MediaResponse(response));
295 continue;
296 }
297 if (response instanceof response_1.BasicCard ||
298 response instanceof response_1.Table ||
299 response instanceof response_1.BrowseCarousel ||
300 response instanceof response_1.MediaResponse ||
301 response instanceof response_1.OrderUpdate ||
302 response instanceof response_1.LinkOutSuggestion) {
303 requireSimpleResponse = true;
304 richResponse.add(response);
305 continue;
306 }
307 richResponse.add(response);
308 }
309 if (this._ordersv3) {
310 for (const response of richResponse.items) {
311 const { structuredResponse } = response;
312 if (structuredResponse && structuredResponse.orderUpdate) {
313 response.structuredResponse = {
314 orderUpdateV3: structuredResponse.orderUpdate,
315 };
316 }
317 }
318 }
319 let hasSimpleResponse = false;
320 for (const response of richResponse.items) {
321 if (response.simpleResponse) {
322 hasSimpleResponse = true;
323 break;
324 }
325 }
326 if (requireSimpleResponse && !hasSimpleResponse) {
327 throw new Error('A simple response is required in addition to this type of response');
328 }
329 const userStorageIn = new user_1.User(this.user.raw, this._init.storage)._serialize();
330 const userStorageOut = this.user._serialize();
331 const userStorage = userStorageOut === userStorageIn ? '' : userStorageOut;
332 const response = {
333 expectUserResponse,
334 richResponse,
335 userStorage,
336 expectedIntent,
337 };
338 if (this.noInputs.length > 0) {
339 response.noInputPrompts = this.noInputs.map(prompt => {
340 return typeof prompt === 'string' ? new response_1.SimpleResponse(prompt) : prompt;
341 });
342 }
343 if (this.speechBiasing.length > 0) {
344 response.speechBiasingHints = this.speechBiasing;
345 }
346 return response;
347 }
348}
349exports.Conversation = Conversation;
350//# sourceMappingURL=conversation.js.map
\No newline at end of file