Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Goodbye graffiti agent #475

Closed
58 changes: 58 additions & 0 deletions goodbye-graffiti-agent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# goodbye-graffiti-agent

## Overview

“Goodbye Graffiti” is a demo built on Dialogflow CX inspired by a number of online forms created by city councils and local governments to report graffiti vandalism.

Check failure on line 5 in goodbye-graffiti-agent/README.md

View workflow job for this annotation

GitHub Actions / markdown-lint

Trailing spaces

goodbye-graffiti-agent/README.md:5:166 MD009/no-trailing-spaces Trailing spaces [Expected: 0 or 2; Actual: 1] https://github.com/DavidAnson/markdownlint/blob/v0.29.0/doc/md009.md

Check failure on line 5 in goodbye-graffiti-agent/README.md

View workflow job for this annotation

GitHub Actions / markdown-lint

Line length

goodbye-graffiti-agent/README.md:5:81 MD013/line-length Line length [Expected: 80; Actual: 166] https://github.com/DavidAnson/markdownlint/blob/v0.29.0/doc/md013.md

The demo features:
1. [Generative Fallback](https://cloud.google.com/dialogflow/cx/docs/concept/generative-fallback) to generate virtual agent responses when end-user input does not match an intent or parameter for form filling.

Check failure on line 8 in goodbye-graffiti-agent/README.md

View workflow job for this annotation

GitHub Actions / markdown-lint

Trailing spaces

goodbye-graffiti-agent/README.md:8:210 MD009/no-trailing-spaces Trailing spaces [Expected: 0 or 2; Actual: 1] https://github.com/DavidAnson/markdownlint/blob/v0.29.0/doc/md009.md

Check failure on line 8 in goodbye-graffiti-agent/README.md

View workflow job for this annotation

GitHub Actions / markdown-lint

Line length

goodbye-graffiti-agent/README.md:8:81 MD013/line-length Line length [Expected: 80; Actual: 210] https://github.com/DavidAnson/markdownlint/blob/v0.29.0/doc/md013.md

Check failure on line 8 in goodbye-graffiti-agent/README.md

View workflow job for this annotation

GitHub Actions / markdown-lint

Lists should be surrounded by blank lines

goodbye-graffiti-agent/README.md:8 MD032/blanks-around-lists Lists should be surrounded by blank lines [Context: "1. [Generative Fallback](https..."] https://github.com/DavidAnson/markdownlint/blob/v0.29.0/doc/md032.md
1. [Generators](https://cloud.google.com/dialogflow/cx/docs/concept/generators) to greet the user and provide a summary of the report.

Check failure on line 9 in goodbye-graffiti-agent/README.md

View workflow job for this annotation

GitHub Actions / markdown-lint

Trailing spaces

goodbye-graffiti-agent/README.md:9:135 MD009/no-trailing-spaces Trailing spaces [Expected: 0 or 2; Actual: 1] https://github.com/DavidAnson/markdownlint/blob/v0.29.0/doc/md009.md

Check failure on line 9 in goodbye-graffiti-agent/README.md

View workflow job for this annotation

GitHub Actions / markdown-lint

Line length

goodbye-graffiti-agent/README.md:9:81 MD013/line-length Line length [Expected: 80; Actual: 135] https://github.com/DavidAnson/markdownlint/blob/v0.29.0/doc/md013.md
1. [Google Maps Geocoding API](https://developers.google.com/maps/documentation/geocoding/requests-geocoding) to lookup latitude/longitude and formatted address of the graffiti location

Check failure on line 10 in goodbye-graffiti-agent/README.md

View workflow job for this annotation

GitHub Actions / markdown-lint

Trailing spaces

goodbye-graffiti-agent/README.md:10:186 MD009/no-trailing-spaces Trailing spaces [Expected: 0 or 2; Actual: 1] https://github.com/DavidAnson/markdownlint/blob/v0.29.0/doc/md009.md

Check failure on line 10 in goodbye-graffiti-agent/README.md

View workflow job for this annotation

GitHub Actions / markdown-lint

Line length

goodbye-graffiti-agent/README.md:10:81 MD013/line-length Line length [Expected: 80; Actual: 186] https://github.com/DavidAnson/markdownlint/blob/v0.29.0/doc/md013.md
1. The [address collection prebuilt component](https://cloud.google.com/dialogflow/cx/docs/concept/prebuilt-component/address-collection) to assist the user with a step-by-step address collection process in case the request to the Geocode APIs returns zero results or the returned address is incorrect.

Check failure on line 11 in goodbye-graffiti-agent/README.md

View workflow job for this annotation

GitHub Actions / markdown-lint

Line length

goodbye-graffiti-agent/README.md:11:81 MD013/line-length Line length [Expected: 80; Actual: 304] https://github.com/DavidAnson/markdownlint/blob/v0.29.0/doc/md013.md
1. [Maps Static API](https://developers.google.com/maps/documentation/maps-static/overview) to visualize the location of the graffiti on the map
1. [Dialogflow CX Phone Gateway and Call Companion](https://cloud.google.com/dialogflow/cx/docs/concept/integration/phone-gateway) to provide a telephone interface to the agent and a a multi-modal (voice + visual) customer experience
1. [Cloud Functions](https://cloud.google.com/functions/docs/configuring) to run the webhook service required to integrate the agent with Google Maps APIs.

## Setup your Google Cloud Project
1. Setup your [Google Cloud Project](https://cloud.google.com/dialogflow/cx/docs/quick/setup)
1. To use Google Maps Platform you must enable the [Geocoding API](https://console.cloud.google.com/apis/library/geocoding-backend.googleapis.com?utm_source=Docs_EnableAPIs&utm_content=Docs_geocoding-backend&_gl=1*1syfwbs*_ga*MTMxNzQwMTEyNS4xNjkyMDE1OTQ1*_ga_NRWSTWS78N*MTY5MjAxNTk0Ni4xLjEuMTY5MjAxNjk4Ni4wLjAuMA..) and [Maps Static API](https://console.cloud.google.com/apis/library/static-maps-backend.googleapis.com?utm_source=Docs_EnableAPIs&utm_content=Docs_static-maps-backend&_gl=1*13fpvaq*_ga*MTMxNzQwMTEyNS4xNjkyMDE1OTQ1*_ga_NRWSTWS78N*MTY5MjAxNTk0Ni4xLjEuMTY5MjAxNjk4Ni4wLjAuMA..).
1. You must have at least one API key associated with your project. Go to the Google Maps Platform > Credentials [page](https://console.cloud.google.com/project/_/google/maps-apis/credentials?utm_source=Docs_CreateAPIKey&utm_content=Docs_static-maps-backend&_gl=1*8gesr0*_ga*MTMxNzQwMTEyNS4xNjkyMDE1OTQ1*_ga_NRWSTWS78N*MTY5MjAxNTk0Ni4xLjEuMTY5MjAxNzI2MS4wLjAuMA..) and create an [API key](https://developers.google.com/maps/documentation/maps-static/get-api-key#creating-api-keys).
1. Open file `index.js` located under `maps-function` folder. You must include your API key with both the Geocoding API and Maps Static API requests. Replace `YOUR_API_KEY` with your API key.
1. Enable the Cloud Functions, Cloud Build, Artifact Registry, Cloud Run, Dialogflow API and Cloud Logging [APIs](https://console.cloud.google.com/flows/enableapi?apiid=cloudfunctions.googleapis.com,%20%20%20%20%20cloudbuild.googleapis.com,artifactregistry.googleapis.com,%20%20%20%20%20run.googleapis.com,logging.googleapis.com&redirect=https://cloud.google.com/functions/docs/create-deploy-http-nodejs&_ga=2.176777133.982063149.1692018523-870547608.1691743190)
1. Install and initialize the [gcloud CLI](https://cloud.google.com/sdk/docs/install).

## Deploy the Cloud Function
To deploy the function, run the `gcloud functions deploy` command in the `maps-function` directory:
```
gcloud functions deploy lookupPlace --runtime=nodejs20 --region=REGION --source=. --entry-point=lookupPlace --trigger-http --allow-unauthenticated
```

Replace `REGION` with the name of the Google Cloud region where you want to deploy your function (for example, `us-west1`).
The optional `--allow-unauthenticated` flag lets you reach your function without authentication.

After the function deploys, note the url property from the output of the gcloud functions deploy command, or retrieve it with the command `gcloud functions describe lookupPlace --region REGION`. Replace `REGION` with the name of the Google Cloud region where you deployed your function. You will need the url when configuring the webhook in Dialogflow CX.

## Create the agent and configure the webhook
1. In your browser, navigate to the [Dialogflow CX console](https://dialogflow.cloud.google.com/cx/projects)
1. Create a new agent (select the option **Build your own agent**). You must create the agent in the **global** region as this is the only region where the Phone Gateway integration is currently supported. You cannot change region once the agent is deployed.
1. [Restore](https://cloud.google.com/dialogflow/cx/docs/concept/agent#export) the Goodbye Graffiti agent exported in the JSON package file format (agent.zip)
1. Once you have successfully restored the agent, under the **Manage** tab open the definition of the **lookup-place** webhook
1. Enter your function URL as the webhook URL and click Save.

![Dialogflow CX Webhook](images/webhook.png)
alessiasacchi marked this conversation as resolved.
Show resolved Hide resolved

## Configure the phone gateway

1. Navigate to **Integrations** and click **Manage** on the CX Phone Gateway panel.
1. Setup the [phone gateway](https://cloud.google.com/dialogflow/cx/docs/concept/integration/phone-gateway#setup) integration.
1. Select a phone number, then click **Show more settings** to enable the call companion feature .
1. Copy the phone number.

## Test the agent

In your browser using Google Voice or directly from your phone, call the number. You will receive a message with the Cloud Companion URL. Click the link to open the UI and test the agent.

![Demo](images/demo.png)

Congratulations, you've successfully deployed the Goodbye Graffiti demo!

Binary file added goodbye-graffiti-agent/agent.zip
Binary file not shown.
Binary file added goodbye-graffiti-agent/images/demo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added goodbye-graffiti-agent/images/webhook.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
129 changes: 129 additions & 0 deletions goodbye-graffiti-agent/maps-function/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/**
* Copyright 2023 Google LLC
*
* 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.
*/

/**
* Responds to any HTTP request.
*
* @param {!express:Request} req HTTP request context.
* @param {!express:Response} res HTTP response context.
*/

var axios = require('axios');

exports.lookupPlace = async (req, res) => {

let tag = req.body.fulfillmentInfo.tag;
var payload = {};
var caller_id = '<no-number>';

if (!!tag) {
switch (tag) {
//BEGIN findPlace

case 'geocode':
console.log(tag + ' was triggered.');
var results = [];

// Check if required params have been populated
if (!(req.body.sessionInfo && req.body.sessionInfo.parameters)) {
return res.status(404).send({ error: 'Not enough information.' });
}

// Retrieve caller_id if it's a phone call
if ( typeof req.body.payload !== 'undefined' && req.body.payload !== 'undefined' ) {
caller_id = req.body.payload.telephony.caller_id;
}

// Set location to the location param value collected from the user.
// Location must be a place name or an address. Reserved characters (for example the plus sign "+") must be URL-encoded.
location = encodeURI(req.body.sessionInfo.parameters['location'].original);
console.log('caller_id: ' + caller_id);

// invokes Geocode APIs and looks for a match
try {
var config = {
method: 'get',
url: 'https://maps.googleapis.com/maps/api/geocode/json?address=' + location + '&key=YOUR_API_KEY',
headers: { }
};

axios(config)
.then(function (response) {
//at least one result
if(response.data.results.length > 0) {

for(var i in response.data.results){
// geocoder returned several results
var result = response.data.results[i];
results.push(result);
}

// single match scenario. Build map. Either ways transition to the same target page. Disambiguation in Dialogflow
if (results.length == 1){
var lat = results[0].geometry.location.lat;
var lng = results[0].geometry.location.lng;
var formatted_address = results[0].formatted_address;

// config static map
var map_img = 'https://maps.googleapis.com/maps/api/staticmap?center=' + formatted_address + '&zoom=14&size=600x300&markers=color:red|' + lat + ',' + lng +'&key=YOUR_API_KEY'
payload = {
"richContent": [
{
"type": "image",
"imageUrl": map_img
}
]
};

}

} else {
//handle ZERO_RESULTS
formatted_address = "";
}
// send fullfilment back to agent
res.status(200).send({
sessionInfo: {
parameters: {
formatted_address: formatted_address,
caller_id: caller_id
}
},
fulfillmentResponse: {
messages: [{
payload: payload
}]
}
});
});

} catch (error) {
res.status(500).send(error);
console.log(error);
}

break;



default:
console.log('default case called');
res.status(200).end();
break;
}
}
};

16 changes: 16 additions & 0 deletions goodbye-graffiti-agent/maps-function/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
alessiasacchi marked this conversation as resolved.
Show resolved Hide resolved
"name": "maps-function",
"version": "1.0.0",
"description": "Webhook service required to integrate the goodbye-graffiti agent with Google Maps Geocode and Static API",
"main": "index.js",
"dependencies": {
"@google-cloud/functions-framework": "^2.0.0",
"axios": "^0.24.0"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "alessiasacchi",
"license": "ISC"
}
Loading