1 | ;
|
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 | */
|
17 | Object.defineProperty(exports, "__esModule", { value: true });
|
18 | exports.Conversation = exports.UnauthorizedError = void 0;
|
19 | const surface_1 = require("./surface");
|
20 | const user_1 = require("./user");
|
21 | const response_1 = require("./response");
|
22 | const helper_1 = require("./helper");
|
23 | const argument_1 = require("./argument");
|
24 | const device_1 = require("./device");
|
25 | const input_1 = require("./input");
|
26 | const 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 | */
|
54 | class UnauthorizedError extends Error {
|
55 | }
|
56 | exports.UnauthorizedError = UnauthorizedError;
|
57 | /** @public */
|
58 | class 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 | }
|
349 | exports.Conversation = Conversation;
|
350 | //# sourceMappingURL=conversation.js.map |
\ | No newline at end of file |