|
|
@@ -0,0 +1,432 @@ |
|
|
// See https://github.com/dialogflow/dialogflow-fulfillment-nodejs |
|
|
// for Dialogflow fulfillment library docs, samples, and to report issues |
|
|
'use strict'; |
|
|
|
|
|
const functions = require('firebase-functions'); |
|
|
const {WebhookClient} = require('dialogflow-fulfillment'); |
|
|
const {Card, Suggestion} = require('dialogflow-fulfillment'); |
|
|
|
|
|
|
|
|
// Copyright 2017-2018, Google, Inc. |
|
|
// 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. |
|
|
'use strict'; |
|
|
const { |
|
|
dialogflow, |
|
|
BasicCard, |
|
|
BrowseCarousel, |
|
|
FinalResponse, |
|
|
BrowseCarouselItem, |
|
|
Button, |
|
|
Carousel, |
|
|
LinkOutSuggestion, |
|
|
List, |
|
|
MediaObject, |
|
|
Suggestions, |
|
|
SimpleResponse, |
|
|
} = require('actions-on-google'); |
|
|
|
|
|
|
|
|
// Constants for list and carousel selection |
|
|
const SELECTION_KEY_GOOGLE_ALLO = 'googleAllo'; |
|
|
const SELECTION_KEY_GOOGLE_HOME = 'googleHome'; |
|
|
const SELECTION_KEY_GOOGLE_PIXEL = 'googlePixel'; |
|
|
const SELECTION_KEY_ONE = 'title'; |
|
|
|
|
|
// Constant for image URLs |
|
|
const IMG_URL_AOG = 'https://developers.google.com/actions/images/badges' + |
|
|
'/XPM_BADGING_GoogleAssistant_VER.png'; |
|
|
const IMG_URL_GOOGLE_ALLO = 'https://allo.google.com/images/allo-logo.png'; |
|
|
const IMG_URL_GOOGLE_HOME = 'https://lh3.googleusercontent.com' + |
|
|
'/Nu3a6F80WfixUqf_ec_vgXy_c0-0r4VLJRXjVFF_X_CIilEu8B9fT35qyTEj_PEsKw'; |
|
|
const IMG_URL_GOOGLE_PIXEL = 'https://storage.googleapis.com/madebygoog/v1' + |
|
|
'/Pixel/Pixel_ColorPicker/Pixel_Device_Angled_Black-720w.png'; |
|
|
const IMG_URL_MEDIA = 'https://storage.googleapis.com/automotive-media/album_art.jpg'; |
|
|
const MEDIA_SOURCE = 'https://storage.googleapis.com/automotive-media/Jazz_In_Paris.mp3'; |
|
|
|
|
|
// Constants for selected item responses |
|
|
const SELECTED_ITEM_RESPONSES = { |
|
|
[SELECTION_KEY_ONE]: 'You selected the first item in the list or carousel', |
|
|
[SELECTION_KEY_GOOGLE_HOME]: 'You selected the Google Home!', |
|
|
[SELECTION_KEY_GOOGLE_PIXEL]: 'You selected the Google Home!', |
|
|
[SELECTION_KEY_GOOGLE_PIXEL]: 'You selected the Google Pixel!', |
|
|
[SELECTION_KEY_GOOGLE_ALLO]: 'You selected Google Allo!', |
|
|
}; |
|
|
|
|
|
const intentSuggestions = [ |
|
|
'Basic Card', |
|
|
'Browse Carousel', |
|
|
'Carousel', |
|
|
'List', |
|
|
'Media', |
|
|
'Suggestions', |
|
|
]; |
|
|
|
|
|
const app = dialogflow({ |
|
|
debug: true |
|
|
}); |
|
|
|
|
|
app.middleware((conv) => { |
|
|
conv.hasScreen = |
|
|
conv.surface.capabilities.has('actions.capability.SCREEN_OUTPUT'); |
|
|
conv.hasAudioPlayback = |
|
|
conv.surface.capabilities.has('actions.capability.AUDIO_OUTPUT'); |
|
|
}); |
|
|
|
|
|
// Welcome |
|
|
app.intent('Default Welcome Intent', (conv) => { |
|
|
conv.ask(new SimpleResponse({ |
|
|
speech: 'Hi there!', |
|
|
text: 'Hello there!', |
|
|
})); |
|
|
conv.ask(new SimpleResponse({ |
|
|
speech: 'I can show you basic cards, lists and carousels ' + |
|
|
'as well as suggestions on your phone.', |
|
|
text: 'I can show you basic cards, lists and carousels as ' + |
|
|
'well as suggestions.', |
|
|
})); |
|
|
conv.ask(new Suggestions(intentSuggestions)); |
|
|
}); |
|
|
|
|
|
app.intent('normal ask', (conv) => { |
|
|
conv.ask('Ask me to show you a list, carousel, or basic card.'); |
|
|
}); |
|
|
|
|
|
// Suggestions |
|
|
app.intent('suggestions', (conv) => { |
|
|
if (!conv.hasScreen) { |
|
|
conv.ask('Sorry, try this on a screen device or select the ' + |
|
|
'phone surface in the simulator.'); |
|
|
return; |
|
|
} |
|
|
conv.ask('This is a simple response for suggestions.'); |
|
|
conv.ask(new Suggestions('Suggestion Chips')); |
|
|
conv.ask(new Suggestions(intentSuggestions)); |
|
|
conv.ask(new LinkOutSuggestion({ |
|
|
name: 'Suggestion Link', |
|
|
url: 'https://assistant.google.com/', |
|
|
})); |
|
|
}); |
|
|
|
|
|
// Basic card |
|
|
app.intent('basic card', (conv) => { |
|
|
conv.ask('This is the first simple response for a basic card.'); |
|
|
conv.ask(new Suggestions('Suggestion Chips')); |
|
|
conv.ask(new Suggestions(intentSuggestions)); |
|
|
// Create a basic card |
|
|
conv.ask(new BasicCard({ |
|
|
text: `This is a basic card. Text in a basic card can include "quotes" and |
|
|
most other unicode characters including emoji 📱. Basic cards also support |
|
|
some markdown formatting like *emphasis* or _italics_, **strong** or |
|
|
__bold__, and ***bold itallic*** or ___strong emphasis___ as well as other |
|
|
things like line \nbreaks`, // Note the two spaces before '\n' required for |
|
|
// a line break to be rendered in the card. |
|
|
subtitle: 'This is a subtitle', |
|
|
title: 'Title: this is a title', |
|
|
buttons: new Button({ |
|
|
title: 'This is a button', |
|
|
url: 'https://assistant.google.com/', |
|
|
}), |
|
|
image: new Image({ |
|
|
url: IMG_URL_AOG, |
|
|
alt: 'Image alternate text', |
|
|
}), |
|
|
platform: 'ACTIONS_ON_GOOGLE' |
|
|
})); |
|
|
|
|
|
conv.ask(new LinkOutSuggestion({ |
|
|
name: 'Suggestion Link', |
|
|
url: 'https://assistant.google.com/', |
|
|
})); |
|
|
}); |
|
|
|
|
|
// List |
|
|
app.intent('list', (conv) => { |
|
|
if (!conv.hasScreen) { |
|
|
conv.ask('Sorry, try this on a screen device or select the ' + |
|
|
'phone surface in the simulator.'); |
|
|
return; |
|
|
} |
|
|
conv.ask('This is a simple response for a list.'); |
|
|
conv.ask(new Suggestions(intentSuggestions)); |
|
|
// Create a list |
|
|
conv.ask(new List({ |
|
|
title: 'List Title', |
|
|
items: { |
|
|
// Add the first item to the list |
|
|
[SELECTION_KEY_ONE]: { |
|
|
synonyms: [ |
|
|
'synonym of title 1', |
|
|
'synonym of title 2', |
|
|
'synonym of title 3', |
|
|
], |
|
|
title: 'Title of First List Item', |
|
|
description: 'This is a description of a list item.', |
|
|
image: new Image({ |
|
|
url: IMG_URL_AOG, |
|
|
alt: 'Image alternate text', |
|
|
}), |
|
|
}, |
|
|
// Add the second item to the list |
|
|
[SELECTION_KEY_GOOGLE_HOME]: { |
|
|
synonyms: [ |
|
|
'Google Home Assistant', |
|
|
'Assistant on the Google Home', |
|
|
], |
|
|
title: 'Google Home', |
|
|
description: 'Google Home is a voice-activated speaker powered by ' + |
|
|
'the Google Assistant.', |
|
|
image: new Image({ |
|
|
url: IMG_URL_GOOGLE_HOME, |
|
|
alt: 'Google Home', |
|
|
}), |
|
|
}, |
|
|
// Add the third item to the list |
|
|
[SELECTION_KEY_GOOGLE_PIXEL]: { |
|
|
synonyms: [ |
|
|
'Google Pixel XL', |
|
|
'Pixel', |
|
|
'Pixel XL', |
|
|
], |
|
|
title: 'Google Pixel', |
|
|
description: 'Pixel. Phone by Google.', |
|
|
image: new Image({ |
|
|
url: IMG_URL_GOOGLE_PIXEL, |
|
|
alt: 'Google Pixel', |
|
|
}), |
|
|
}, |
|
|
// Add the last item to the list |
|
|
[SELECTION_KEY_GOOGLE_ALLO]: { |
|
|
title: 'Google Allo', |
|
|
synonyms: [ |
|
|
'Allo', |
|
|
], |
|
|
description: 'Introducing Google Allo, a smart messaging app that ' + |
|
|
'helps you say more and do more.', |
|
|
image: new Image({ |
|
|
url: IMG_URL_GOOGLE_ALLO, |
|
|
alt: 'Google Allo Logo', |
|
|
}), |
|
|
}, |
|
|
}, |
|
|
})); |
|
|
}); |
|
|
|
|
|
// Carousel |
|
|
app.intent('carousel', (conv) => { |
|
|
if (!conv.hasScreen) { |
|
|
conv.ask('Sorry, try this on a screen device or select the ' + |
|
|
'phone surface in the simulator.'); |
|
|
return; |
|
|
} |
|
|
conv.ask('This is a simple response for a carousel.'); |
|
|
conv.ask(new Suggestions(intentSuggestions)); |
|
|
// Create a carousel |
|
|
conv.ask(new Carousel({ |
|
|
items: { |
|
|
// Add the first item to the carousel |
|
|
[SELECTION_KEY_ONE]: { |
|
|
synonyms: [ |
|
|
'synonym of title 1', |
|
|
'synonym of title 2', |
|
|
'synonym of title 3', |
|
|
], |
|
|
title: 'Title of First Carousel Item', |
|
|
description: 'This is a description of a carousel item.', |
|
|
image: new Image({ |
|
|
url: IMG_URL_AOG, |
|
|
alt: 'Image alternate text', |
|
|
}), |
|
|
}, |
|
|
// Add the second item to the carousel |
|
|
[SELECTION_KEY_GOOGLE_HOME]: { |
|
|
synonyms: [ |
|
|
'Google Home Assistant', |
|
|
'Assistant on the Google Home', |
|
|
], |
|
|
title: 'Google Home', |
|
|
description: 'Google Home is a voice-activated speaker powered by ' + |
|
|
'the Google Assistant.', |
|
|
image: new Image({ |
|
|
url: IMG_URL_GOOGLE_HOME, |
|
|
alt: 'Google Home', |
|
|
}), |
|
|
}, |
|
|
// Add third item to the carousel |
|
|
[SELECTION_KEY_GOOGLE_PIXEL]: { |
|
|
synonyms: [ |
|
|
'Google Pixel XL', |
|
|
'Pixel', |
|
|
'Pixel XL', |
|
|
], |
|
|
title: 'Google Pixel', |
|
|
description: 'Pixel. Phone by Google.', |
|
|
image: new Image({ |
|
|
url: IMG_URL_GOOGLE_PIXEL, |
|
|
alt: 'Google Pixel', |
|
|
}), |
|
|
}, |
|
|
// Add last item of the carousel |
|
|
[SELECTION_KEY_GOOGLE_ALLO]: { |
|
|
title: 'Google Allo', |
|
|
synonyms: [ |
|
|
'Allo', |
|
|
], |
|
|
description: 'Introducing Google Allo, a smart messaging app that ' + |
|
|
'helps you say more and do more.', |
|
|
image: new Image({ |
|
|
url: IMG_URL_GOOGLE_ALLO, |
|
|
alt: 'Google Allo Logo', |
|
|
}), |
|
|
}, |
|
|
}, |
|
|
})); |
|
|
}); |
|
|
|
|
|
// Browse Carousel |
|
|
app.intent('browse carousel', (conv) => { |
|
|
const a11yText = 'Google Assistant Bubbles'; |
|
|
const googleUrl = 'https://google.com'; |
|
|
if (!conv.hasScreen) { |
|
|
conv.ask('Sorry, try this on a screen device or select the ' + |
|
|
'phone surface in the simulator.'); |
|
|
return; |
|
|
} |
|
|
conv.ask('This is an example of a "Browse Carousel"'); |
|
|
// Create a browse carousel |
|
|
conv.ask(new BrowseCarousel({ |
|
|
items: [ |
|
|
new BrowseCarouselItem({ |
|
|
title: 'Title of item 1', |
|
|
url: googleUrl, |
|
|
description: 'Description of item 1', |
|
|
image: new Image({ |
|
|
url: IMG_URL_AOG, |
|
|
alt: a11yText, |
|
|
}), |
|
|
footer: 'Item 1 footer', |
|
|
}), |
|
|
new BrowseCarouselItem({ |
|
|
title: 'Title of item 2', |
|
|
url: googleUrl, |
|
|
description: 'Description of item 2', |
|
|
image: new Image({ |
|
|
url: IMG_URL_AOG, |
|
|
alt: a11yText, |
|
|
}), |
|
|
footer: 'Item 2 footer', |
|
|
}), |
|
|
], |
|
|
})); |
|
|
}); |
|
|
|
|
|
// Media response |
|
|
app.intent('media response', (conv) => { |
|
|
if (!conv.hasAudioPlayback) { |
|
|
conv.ask('Sorry, this device does not support audio playback.'); |
|
|
return; |
|
|
} |
|
|
conv.ask('This is the first simple response for a media response'); |
|
|
conv.ask(new MediaObject({ |
|
|
name: 'Jazz in Paris', |
|
|
url: MEDIA_SOURCE, |
|
|
description: 'A funky Jazz tune', |
|
|
icon: new Image({ |
|
|
url: IMG_URL_MEDIA, |
|
|
alt: 'Media icon', |
|
|
}), |
|
|
|
|
|
})); |
|
|
conv.ask(new Suggestions(intentSuggestions)); |
|
|
}); |
|
|
|
|
|
// Handle a media status event |
|
|
app.intent('media status', (conv) => { |
|
|
const mediaStatus = conv.arguments.get('MEDIA_STATUS'); |
|
|
let response = 'Unknown media status received.'; |
|
|
if (mediaStatus && mediaStatus.status === 'FINISHED') { |
|
|
response = 'Hope you enjoyed the tunes!'; |
|
|
} |
|
|
conv.ask(response); |
|
|
conv.ask(new Suggestions(intentSuggestions)); |
|
|
}); |
|
|
|
|
|
// React to list or carousel selection |
|
|
app.intent('item selected', (conv, params, option) => { |
|
|
let response = 'You did not select any item from the list or carousel'; |
|
|
if (option && SELECTED_ITEM_RESPONSES.hasOwnProperty(option)) { |
|
|
response = SELECTED_ITEM_RESPONSES[option]; |
|
|
} else { |
|
|
response = 'You selected an unknown item from the list or carousel'; |
|
|
} |
|
|
conv.ask(response); |
|
|
}); |
|
|
|
|
|
app.intent('card builder', (conv) => { |
|
|
if (!conv.hasScreen) { |
|
|
conv.ask('Sorry, try this on a screen device or select the ' + |
|
|
'phone surface in the simulator.'); |
|
|
return; |
|
|
} |
|
|
conv.ask(...conv.incoming); |
|
|
conv.ask(new BasicCard({ |
|
|
text: `Actions on Google let you build for |
|
|
the Google Assistant. Reach users right when they need you. Users don’t |
|
|
need to pre-enable skills or install new apps. \n \nThis was written |
|
|
in the fulfillment webhook!`, |
|
|
subtitle: 'Engage users through the Google Assistant', |
|
|
title: 'Actions on Google', |
|
|
buttons: new Button({ |
|
|
title: 'Developer Site', |
|
|
url: 'https://developers.google.com/actions/', |
|
|
}), |
|
|
image: new Image({ |
|
|
url: IMG_URL_AOG, |
|
|
alt: 'Actions on Google', |
|
|
}), |
|
|
})); |
|
|
}); |
|
|
|
|
|
// Leave conversation with card |
|
|
app.intent('bye card', (conv) => { |
|
|
|
|
|
conv.ask('Goodbye, World!'); |
|
|
conv.close(new BasicCard({ |
|
|
text: 'This is a goodbye card.', |
|
|
})); |
|
|
}); |
|
|
|
|
|
// Leave conversation with SimpleResponse |
|
|
app.intent('bye response', (conv) => { |
|
|
conv.close(new SimpleResponse({ |
|
|
speech: 'Okay see you later', |
|
|
text: 'OK see you later!', |
|
|
})); |
|
|
}); |
|
|
app.intent('actions.intent.MEDIA_STATUS',(conv)=>{ |
|
|
const mediaStatus = conv.arguments.get('MEDIA_STATUS'); |
|
|
let response = 'Unknown media status received.'; |
|
|
if (mediaStatus && mediaStatus.status === 'FINISHED') { |
|
|
response = 'Hope you enjoyed the tunes!'; |
|
|
} |
|
|
conv.ask(response); |
|
|
|
|
|
}); |
|
|
// Leave conversation |
|
|
app.intent('normal bye', (conv) => { |
|
|
conv.close('Okay see you later!'); |
|
|
}); |
|
|
|
|
|
exports.conversationComponent = functions.https.onRequest(app); |
|
|
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements |
|
|
|
|
|
exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app); |