-
Notifications
You must be signed in to change notification settings - Fork 252
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add an "Account Owner Lookup" Bot-- GCF + Sheets Chatbot sample (#65)
* Initial set up of an account look up bot * Checks account names from data sheet * added response cards * lint * Add screenshots * First draft of README * cleaned up README * removes extra stringifys and misc clean up
- Loading branch information
Showing
6 changed files
with
1,536 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
# Accounts Bot | ||
|
||
This demo bot serves account owner information for a fictional sales team. This code sample | ||
shows how create a bot that looks up information from a [Google Sheet][data_sheet] and served by a | ||
[Google Cloud Function][gcf]. With this bot, a user who requests an account owner's information will | ||
either receive a card with contact info or an option ensure the data is up-to-date if the requested | ||
account could not be found. | ||
|
||
![Acme-Lookup](https://cdn.jsdelivr.net/gh/gsuitedevs/hangouts-chat-samples@master/node/accounts-bot/assests/AcmeLookup.png) | ||
|
||
[gcf]: https://cloud.google.com/functions | ||
[data_sheet]: https://docs.google.com/spreadsheets/d/1kxW15ZI48mh4KkvgsMpg7gInmEQmyKYRnZdbUOSMRnU/copy | ||
|
||
## Prerequisites | ||
|
||
1. The [Google Cloud SDK][cloud_sdk] and `gcloud` set up on your machine. | ||
1. Please be sure to have completed or understood the concepts in the Hangouts Chat | ||
Google Cloud Functions [quickstart guide][gcf_bot] first. | ||
|
||
[cloud_sdk]: https://cloud.google.com/deployment-manager/docs/step-by-step-guide/installation-and-setup | ||
[gcf_bot]: https://developers.google.com/hangouts/chat/quickstart/gcf-bot | ||
|
||
## Set up instructions | ||
|
||
1. Make a copy of the [data sheet][data_sheet] in your Google Drive. | ||
1. Create a new Node.js project in your working directory with: | ||
|
||
`npm init` | ||
|
||
1. Copy the code in index.js to your working directory. | ||
1. Install the googleapis library to access the Sheets API: | ||
|
||
`npm install googleapis --save` | ||
|
||
### Set up a Google Cloud Project (if needed) | ||
|
||
1. [Create a new project][new_project] in the Google Cloud Developer Console. | ||
Name it "AccountsBot", select a **Billing Account** if prompted, and | ||
click **CREATE**. More information on how to setup billing [here][billing]. | ||
1. When the project creation is complete a notification appears in the | ||
upper-right of the page. Click on the **Create Project: AccountsBot** entry | ||
to open the project. | ||
1. Open the [**OAuth consent screen**][consent_screen] settings page for the | ||
project. | ||
1. In the field **Application name** enter "AccountsBot" and click the | ||
**Save** button at the bottom. | ||
1. Open the [**Sheets API**][library_sheets] page in the API library and click | ||
the **ENABLE** button. | ||
1. Open the [**Project settings**][project_settings] page for the project. | ||
1. Copy the value listed under **Project number**. | ||
|
||
[new_project]: https://console.cloud.google.com/projectcreate | ||
[billing]: https://cloud.google.com/free/docs/gcp-free-tier | ||
[consent_screen]: https://console.cloud.google.com/apis/credentials/consent | ||
[library_sheets]: https://console.cloud.google.com/apis/library/sheets | ||
[project_settings]: https://console.cloud.google.com/iam-admin/settings | ||
|
||
### Deploying the Cloud Function | ||
|
||
1. In your working directory, deploy the Cloud function with the following command: | ||
`gcloud functions deploy accountsBot --runtime nodejs8 --trigger-http` | ||
|
||
### Set the permissions on the data | ||
|
||
1. The Cloud Function at runtime will run as your GCP project's AppEngine Default | ||
Service Account. Read more [here][functions_iam]. In the Cloud Console, navigate | ||
to your project's [service accounts][service_accounts] and copy your AppEngine | ||
Service Account address which should be in this format: | ||
|
||
`PROJECT_ID@appspot.gserviceaccount.com` | ||
|
||
1. If one is not already present, trigger your Cloud Function from the Functions Console. | ||
1. Navigate back to your service accounts and it should be present. | ||
1. Open your data sheet and give this address view-only permissions. | ||
|
||
[cloud_console]: https://console.cloud.google.com/ | ||
[service_accounts]: https://console.cloud.google.com/iam-admin/serviceaccounts | ||
[functions_iam]: https://cloud.google.com/functions/docs/concepts/iam | ||
|
||
### Publish the bot to Hangouts Chat | ||
|
||
1. Back in the Cloud Console, open the | ||
[**Hangouts Chat API**][library_chat] page in the API library and click the | ||
**ENABLE** button. | ||
1. Once the API is enabled, on click the **Configuration** tab. | ||
1. In the Configuration tab, do the following: | ||
1. In the **Bot name** box, enter "AccountsBot". | ||
1. In the **Avatar URL box**, enter `https://www.gstatic.com/images/icons/material/system_gm/1x/badge_black_18dp.png`. | ||
1. In the **Description box**, enter "Easy account owners look up". | ||
1. Under **Functionality**, select all options. | ||
1. Under **Connection settings**, select **Bot Url** and paste | ||
your the URL for the Cloud Function trigger into the box. | ||
1. Under **Permissions**, select **Specific people and group in your | ||
domain**. In the text box under the drop-down menu, enter your email | ||
address. | ||
1. Click Save changes. | ||
1. After you save your changes, verify that the status on the Hangouts Chat API | ||
page shows the Bot Status to be **LIVE – available to users**. | ||
|
||
[library_chat]: https://console.cloud.google.com/apis/library/chat.googleapis.com | ||
|
||
## Test the bot | ||
|
||
1. Open [Hangouts Chat][hangouts_chat]. | ||
1. Click **Find people, rooms, bots > Message a bot**. | ||
1. From the list, select the **AccountsBot** that you created. | ||
1. Send the message "Acme" to the bot, you should receive a card shown above. | ||
1. Send the message "foo", you should receive the default card: | ||
![Acme-Lookup](https://cdn.jsdelivr.net/gh/gsuitedevs/hangouts-chat-samples@master/node/accounts-bot/assests/FooLookup.png) | ||
|
||
[hangouts_chat]: https://chat.google.com |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
const {google} = require('googleapis'); | ||
const SHEETS_SCOPE = 'https://www.googleapis.com/auth/spreadsheets.readonly'; | ||
const ACCOUNTS_SHEET_ID = '1kxW15ZI48mh4KkvgsMpg7gInmEQmyKYRnZdbUOSMRnU'; | ||
const ACCOUNTS_SHEET_URL = 'https://docs.google.com/spreadsheets/d/1kxW15ZI48mh4KkvgsMpg7gInmEQmyKYRnZdbUOSMRnU/edit#gid=0'; | ||
const ACCOUNT_IMAGE_URL = 'https://www.gstatic.com/images/icons/material/system_gm/1x/account_circle_black_18dp.png'; | ||
|
||
/** | ||
* Looks up the account owner for the company requested. | ||
* | ||
* @param {!express:Request} req HTTP request context. | ||
* @param {!express:Response} res HTTP response context. | ||
*/ | ||
exports.accountsBot = async (req, res) => { | ||
const message = req.body.message; | ||
if (!message || !message.text) { | ||
console.log('invalid request'); | ||
} | ||
|
||
const messageParts = message.text.split(/[ ,]+/); | ||
if (messageParts.length != 2) { | ||
res.send(createTextResponse('Please enter a single account to look up.')); | ||
} | ||
|
||
const accountName = messageParts[1]; | ||
console.log(`accountName: ${accountName}`); | ||
const sheetsClient = await getSheetsClient(); | ||
try { | ||
const ownerData = await accountLookup(sheetsClient, accountName); | ||
res.send(createOwnerCard(ownerData)); | ||
} catch (error) { | ||
res.send(createErrorCard(accountName)); | ||
} | ||
}; | ||
|
||
/** | ||
* Looks up ownership information for the account. | ||
* | ||
* @param {Object} client initialized Sheets API client. | ||
* @param {String} accountName inbound message | ||
* @return {String} owner data | ||
*/ | ||
async function accountLookup(client, accountName) { | ||
const accountsSheet = await client.spreadsheets.values.get({ | ||
spreadsheetId: ACCOUNTS_SHEET_ID, | ||
range: 'Sheet1!A:D', | ||
}); | ||
const accountsTable = accountsSheet.data.values; | ||
|
||
return accountsTable.find((entry) => entry[0] === accountName); | ||
} | ||
|
||
/** | ||
* Authenticates the Sheets API client for read-only access. | ||
* | ||
* @return {Object} sheets client | ||
*/ | ||
async function getSheetsClient() { | ||
// Should change this to file.only probably | ||
const auth = await google.auth.getClient({ | ||
scopes: [SHEETS_SCOPE], | ||
}); | ||
return google.sheets({version: 'v4', auth}); | ||
} | ||
|
||
/** | ||
* Creates JSON response for a simple text message for Hangouts. | ||
* | ||
* @param {String} message the text of message response | ||
* @return {JSON} the reponse data | ||
*/ | ||
function createTextResponse(message) { | ||
return {text: message}; | ||
} | ||
|
||
/** | ||
* Creates a JSON with the fields specified. | ||
* | ||
* @param {Array} data owner info | ||
* @return {Object} a card with the owner info | ||
*/ | ||
function createOwnerCard(data) { | ||
const company = data[0]; | ||
const name = data[1]; | ||
const location = data[2]; | ||
const email = data[3]; | ||
|
||
const cardHeader = { | ||
title: company + ' Account Owner', | ||
subtitle: name, | ||
imageUrl: ACCOUNT_IMAGE_URL, | ||
imageStyle: 'IMAGE', | ||
}; | ||
|
||
const emailWidget = { | ||
keyValue: { | ||
content: 'Email', | ||
bottomLabel: email, | ||
}, | ||
}; | ||
|
||
const locationWidget = { | ||
keyValue: { | ||
content: 'Location', | ||
bottomLabel: location, | ||
}, | ||
}; | ||
|
||
const infoSection = {widgets: [emailWidget, locationWidget]}; | ||
|
||
const cards = [{ | ||
name: 'Status Card', | ||
header: cardHeader, | ||
sections: [infoSection], | ||
}]; | ||
|
||
return {cards: cards}; | ||
} | ||
|
||
/** | ||
* Creates a card to respond if no account is found. | ||
* | ||
* @param {String} accountName the company that could not be found. | ||
* @return {Object} a card with default info. | ||
*/ | ||
function createErrorCard(accountName) { | ||
const cardHeader = { | ||
title: 'Account Information Not Found', | ||
subtitle: 'Account requested: ' + accountName, | ||
imageUrl: ACCOUNT_IMAGE_URL, | ||
imageStyle: 'IMAGE', | ||
}; | ||
|
||
const textWidget = { | ||
textParagraph: { | ||
text: 'Please check the account data is up to date', | ||
}, | ||
}; | ||
|
||
const buttonWidget = { | ||
buttons: [ | ||
{ | ||
textButton: { | ||
text: 'Account Data', | ||
onClick: { | ||
openLink: { | ||
url: ACCOUNTS_SHEET_URL, | ||
}, | ||
}, | ||
}, | ||
}, | ||
], | ||
}; | ||
|
||
const infoSection = {widgets: [textWidget, buttonWidget]}; | ||
|
||
const cards = [{ | ||
name: 'No Owner Found', | ||
header: cardHeader, | ||
sections: [infoSection], | ||
}]; | ||
return {cards: cards}; | ||
} |
Oops, something went wrong.