Skip to content

Instantly share code, notes, and snippets.

@SethBuilder
Last active November 24, 2020 09:48
Show Gist options
  • Save SethBuilder/0c3e26a2aa4f7c2765ec1167b7a44bcd to your computer and use it in GitHub Desktop.
Save SethBuilder/0c3e26a2aa4f7c2765ec1167b7a44bcd to your computer and use it in GitHub Desktop.
Node webhook for the Google Assistant.
'use strict';
const {dialogflow, Image, Carousel, BasicCard, Button, LinkOutSuggestion} = require('actions-on-google');
const functions = require('firebase-functions');
// Different "Intents" or "purposes" to be fullfilled
const WELCOME_INTENT = 'Default Welcome Intent';
const TRIPS_INTENT = 'find_trips';
const TRIP_INTENT = 'trip_details';
// Params passed to backend by the assistant
const LOCATION_PARAMETER_1 = 'geo-city';
const LOCATION_PARAMETER_2 = 'geo-state';
const LOCATION_PARAMETER_3 = 'geo-country';
const LOCATION_PARAMETER_4 = 'place-attraction';
const app = dialogflow({debug: false});
app.fallback((conv, input, option) => {
const intent = conv.intent;
const axios = require('axios');
switch (intent) {
// Welcome Intent triggered right after user invokes Action
case WELCOME_INTENT:
conv.ask(`Hello there! Welcome to Adventure Meetups! Where would you like to go? `);
const countries_url = process.env.COUNTRIES_API_URL;
return axios.get(countries_url)
.then(response => {
this.data = response.data.data;
// list of countries to show on carousel
let items={};
// String of countries to read out to user
let countries = "";
this.data.forEach((item, index, array) => {
let obj =
{
synonyms: [
item.code,
],
title: item.name,
description: item.activities + "Trips",
image: new Image({
url: item.photo,
alt: item.name,
}),
};
items[item.name.toUpperCase()] = obj
if (index == 0) {
countries+=item.name;
}
else if (index === array.length - 1) {
countries+=" and "+item.name+".";
}
else {
countries+=", "+item.name;
}
});
// Return string
conv.ask(' We have trips and tours in '+countries);
// and carousel
conv.ask(new Carousel({
title: 'Trips',
items: items
}));
})
break;
// Trip details Intent
case TRIP_INTENT:
const trip_url = process.env.TRIP_DETAILS_API_URL+option;
return axios.get(trip_url)
.then(response => {
const data = response.data.data;
const isCountry = response.data.isCountry;
if(!isCountry) {
// Return trip details
conv.ask(`Alright, here are the deets for this trip.`);
conv.ask(new BasicCard({
text: data.deets,
subtitle: data.category.name,
title: data.name,
buttons: new Button({
title: 'Book | $'+data.budgetUsd+" per person",
url: data.complete_url,
}),
image: new Image({
url: data.hasVideoCover ? data.inspiration.desktopThumb : data.inspiration.desktopInspiration,
alt: data.name,
}),
display: 'CROPPED',
}));
conv.close(new LinkOutSuggestion({
name: 'Search',
url: 'https://www.getyourguide.com/s?partner_id=0CVLYT9',
}));
}
else {
// Return all trips in certain country (after being selected from carousel)
var items={};
data.forEach((item) => {
let obj =
{
synonyms: [
item.name,
],
title: item.name,
description: item.deets,
image: new Image({
url: item.hasVideoCover ? item.inspiration.desktopThumb : item.inspiration.desktopInspiration,
alt: item.name,
}),
};
items[item.name.toUpperCase()] = obj
});
conv.ask('I found these awesome trips for you!');
conv.ask(new Carousel({
title: 'Trips',
items: items
}));
conv.ask(` I hope you like them!`);
}
})
.catch(err => {
conv.ask(`Albeit I'm super awesome, I'm unable to respond right now due to computer error.`);
})
break;
// When user mentions random location names instaed of choosing country
case TRIPS_INTENT:
// User might mention location name(s) instead of choosing country from carousel.
// Backend returns all trips if it found none matching user's preference.
const location = conv.parameters[LOCATION_PARAMETER_1] + " " +conv.parameters[LOCATION_PARAMETER_2] + conv.parameters[LOCATION_PARAMETER_3]
+ " " +conv.parameters[LOCATION_PARAMETER_4];
const url_call = process.env.LOCATION_SEARCH_API_URL+location;
return axios.get(url_call)
.then(response => {
this.data = response.data.data;
var items={};
this.data.forEach((item) => {
let obj =
{
synonyms: [
item.name,
],
title: item.name,
description: item.deets,
image: new Image({
url: item.hasVideoCover ? item.inspiration.desktopThumb : item.inspiration.desktopInspiration,
alt: item.name,
}),
};
items[item.name.toUpperCase()] = obj
});
return items;
})
.then(items => {
conv.ask('I found these awesome trips for you!');
conv.ask(new Carousel({
title: 'Trips',
items: items
}));
conv.ask(` I hope you like them!`);
})
break;
}
});
exports.tripFinder = functions.https.onRequest(app);
{
"name": "google-assistant-trip-finder",
"description": "Node.js webhook as backend for the google assistant as frontend",
"version": "0.0.1",
"author": "Seif Elmughrabi",
"engines": {
"node": "8"
},
"dependencies": {
"actions-on-google": "^2.0.0",
"firebase-admin": "^4.2.1",
"firebase-functions": "1.0.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment