actions-on-google
Version:
Actions on Google Client Library for Node.js
447 lines (439 loc) • 13.3 kB
text/typescript
/**
* Copyright 2018 Google Inc. All Rights Reserved.d
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as Api from './api/v2';
import * as ApiV1 from './api/v1';
import {
Image,
Suggestions,
BasicCard,
SimpleResponse,
LinkOutSuggestion,
List,
Carousel,
Button,
} from '../actionssdk';
import {JsonObject} from '../../common';
export type IncomingMessage =
| string
| Image
| Suggestions
| BasicCard
| SimpleResponse
| LinkOutSuggestion
| List
| Carousel
| JsonObject;
const toImage = (imageUri: string | undefined) => {
if (imageUri) {
return new Image({
url: imageUri,
alt: '',
});
}
return undefined;
};
export class Incoming {
/** @public */
parsed: IncomingMessage[] = [];
/** @hidden */
constructor(
fulfillment:
| Api.GoogleCloudDialogflowV2IntentMessage[]
| ApiV1.DialogflowV1Fulfillment
| undefined
) {
if (typeof fulfillment === 'undefined') {
return;
}
if (Array.isArray(fulfillment)) {
// Dialogflow v2
for (const message of fulfillment) {
const {
text,
image,
quickReplies,
card,
simpleResponses,
basicCard,
suggestions,
linkOutSuggestion,
listSelect,
carouselSelect,
platform,
payload,
} = message;
if (
platform &&
platform !== 'ACTIONS_ON_GOOGLE' &&
platform !== 'PLATFORM_UNSPECIFIED'
) {
continue;
}
if (text) {
this.parsed.push(...text.text!);
continue;
}
if (image) {
this.parsed.push(
new Image({
url: image.imageUri!,
alt: image.accessibilityText!,
})
);
continue;
}
if (quickReplies) {
this.parsed.push(new Suggestions(quickReplies.quickReplies!));
continue;
}
if (card) {
const {buttons} = card;
this.parsed.push(
new BasicCard({
title: card.title,
subtitle: card.subtitle,
image: toImage(card.imageUri),
buttons: buttons
? buttons.map(
b =>
new Button({
title: b.text!,
url: b.postback,
})
)
: undefined,
})
);
continue;
}
if (simpleResponses) {
this.parsed.push(
...simpleResponses.simpleResponses!.map(
s =>
new SimpleResponse({
speech: s.textToSpeech || s.ssml!,
text: s.displayText,
})
)
);
continue;
}
if (basicCard) {
const {image, buttons} = basicCard;
this.parsed.push(
new BasicCard({
title: basicCard.title,
subtitle: basicCard.subtitle,
text: basicCard.formattedText,
image: image
? new Image({
url: image.imageUri!,
alt: image.accessibilityText!,
})
: undefined,
buttons: buttons
? buttons.map(
b =>
new Button({
title: b.title!,
url: b.openUriAction!.uri,
})
)
: undefined,
})
);
continue;
}
if (suggestions) {
this.parsed.push(
new Suggestions(suggestions.suggestions!.map(s => s.title!))
);
continue;
}
if (linkOutSuggestion) {
this.parsed.push(
new LinkOutSuggestion({
name: linkOutSuggestion.destinationName!,
openUrlAction: {
url: linkOutSuggestion.uri,
},
})
);
continue;
}
if (listSelect) {
this.parsed.push(
new List({
title: listSelect.title,
items: listSelect.items!,
})
);
continue;
}
if (carouselSelect) {
this.parsed.push(
new Carousel({
items: carouselSelect.items!,
})
);
continue;
}
if (payload) {
this.parsed.push(payload);
continue;
}
}
} else {
// Dialogflow v1
const {speech, messages} = fulfillment;
if (speech) {
this.parsed.push(speech);
}
if (typeof messages !== 'undefined') {
for (const message of messages) {
const {platform, type} = message;
if (platform && platform !== 'google') {
continue;
}
if (type === 0) {
const assumed = message as ApiV1.DialogflowV1MessageText;
this.parsed.push(assumed.speech!);
continue;
}
if (type === 3) {
const assumed = message as ApiV1.DialogflowV1MessageImage;
this.parsed.push(toImage(assumed.imageUrl)!);
continue;
}
if (type === 1) {
const assumed = message as ApiV1.DialogflowV1MessageCard;
const {buttons} = assumed;
this.parsed.push(
new BasicCard({
title: assumed.title,
subtitle: assumed.subtitle,
image: toImage(assumed.imageUrl),
buttons: buttons
? buttons.map(
b =>
new Button({
title: b.text!,
url: b.postback,
})
)
: undefined,
})
);
continue;
}
if (type === 2) {
const assumed = message as ApiV1.DialogflowV1MessageQuickReplies;
this.parsed.push(new Suggestions(assumed.replies!));
continue;
}
if (type === 4) {
const assumed = message as ApiV1.DialogflowV1MessageCustomPayload;
this.parsed.push(assumed.payload!);
continue;
}
if (type === 'simple_response') {
const assumed = message as ApiV1.DialogflowV1MessageSimpleResponse;
this.parsed.push(
new SimpleResponse({
text: assumed.displayText,
speech: assumed.textToSpeech!,
})
);
continue;
}
if (type === 'basic_card') {
const assumed = message as ApiV1.DialogflowV1MessageBasicCard;
const {image = {}, buttons} = assumed;
this.parsed.push(
new BasicCard({
title: assumed.title,
subtitle: assumed.subtitle,
text: assumed.formattedText,
image: toImage(image.url),
buttons: buttons
? buttons.map(
b =>
new Button({
title: b.title!,
url: b.openUrlAction!.url,
})
)
: undefined,
})
);
continue;
}
if (type === 'list_card') {
const assumed = message as ApiV1.DialogflowV1MessageList;
this.parsed.push(
new List({
title: assumed.title,
items: assumed.items!,
})
);
continue;
}
if (type === 'suggestion_chips') {
const assumed = message as ApiV1.DialogflowV1MessageSuggestions;
this.parsed.push(
new Suggestions(assumed.suggestions!.map(s => s.title!))
);
continue;
}
if (type === 'carousel_card') {
const assumed = message as ApiV1.DialogflowV1MessageCarousel;
this.parsed.push(
new Carousel({
items: assumed.items!,
})
);
continue;
}
if (type === 'link_out_chip') {
const assumed = message as ApiV1.DialogflowV1MessageLinkOut;
this.parsed.push(
new LinkOutSuggestion({
name: assumed.destinationName!,
openUrlAction: {
url: assumed.url!,
},
})
);
continue;
}
if (type === 'custom_payload') {
const assumed = message as ApiV1.DialogflowV1MessageGooglePayload;
this.parsed.push(assumed.payload!);
continue;
}
}
}
}
}
/**
* Gets the first Dialogflow incoming message with the given type.
* Messages are converted into client library class instances or a string.
*
* Only messages with the platform field unlabeled (for generic use)
* or labeled `ACTIONS_ON_GOOGLE` (`google` in v1) will be converted and read.
*
* The conversation is detailed below for a specific message oneof:
* * Generic Platform Response
* * `text` -> `typeof string`
* * `image` -> `Image`
* * `quickReplies` -> `Suggestions`
* * `card` -> `BasicCard`
* * Actions on Google Response
* * `simpleResponses` -> `SimpleResponse[]`
* * `basicCard` -> `BasicCard`
* * `suggestions` -> `Suggestions`
* * `linkOutSuggestion` -> `LinkOutSuggestion`
* * `listSelect` -> `List`
* * `carouselSelect` -> `Carousel`
* * `payload` -> `typeof object`
*
* Dialogflow v1:
* * Generic Platform Response
* * `0` (text) -> `typeof string`
* * `3` (image) -> `Image`
* * `1` (card) -> `BasicCard`
* * `2` (quick replies) -> `Suggestions`
* * `4` (custom payload) -> `typeof object`
* * Actions on Google Response
* * `simple_response` -> `SimpleResponse`
* * `basic_card` -> `BasicCard`
* * `list_card` -> `List`
* * `suggestion_chips` -> `Suggestions`
* * `carousel_card` -> `Carousel`
* * `link_out_chip` -> `LinkOutSuggestion`
* * `custom_payload` -> `typeof object`
*
* @example
* ```javascript
*
* // Dialogflow
* const { dialogflow, BasicCard } = require('actions-on-google')
*
* const app = dialogflow()
*
* // Create an Actions on Google Basic Card in the Dialogflow Console Intent Responses section
* app.intent('Default Welcome Intent', conv => {
* const str = conv.incoming.get('string') // get the first text response
* const card = conv.incoming.get(BasicCard) // gets the instance of BasicCard
* // Do something with the Basic Card
* })
* ```
*
* @param type A string checking for the typeof message or a class checking for instanceof message
* @public
*/
// tslint:disable-next-line:no-any allow constructors with any type of arguments
get<TMessage extends IncomingMessage>(
type: new (...args: any[]) => TMessage
): TMessage;
/** @public */
get(type: 'string'): string;
// tslint:disable-next-line:no-any allow constructors with any type of arguments
get<TMessage extends IncomingMessage>(
type: 'string' | (new (...args: any[]) => TMessage)
) {
for (const message of this) {
if (typeof type === 'string') {
if (typeof message === type) {
return message;
}
continue;
}
if (message instanceof type) {
return message;
}
}
return null;
}
/**
* Gets the Dialogflow incoming messages as an iterator.
* Messages are converted into client library class instances or a string.
* See {@link Incoming#get|conv.incoming.get} for details on how the conversion works.
*
* @example
* ```javascript
*
* // Dialogflow
* const app = dialogflow()
*
* // Create messages in the Dialogflow Console Intent Responses section
* app.intent('Default Welcome Intent', conv => {
* const messages = [...conv.incoming]
* // do something with the messages
* // or just spread them out back to the user
* conv.ask(`Here's what was set in the Dialogflow console`)
* conv.ask(...conv.incoming)
* }
* ```
*
* @public
*/
[Symbol.iterator]() {
return this.parsed[Symbol.iterator]();
// suppose to be Array.prototype.values(), but can't use because of bug:
// https://bugs.chromium.org/p/chromium/issues/detail?id=615873
}
}