Skip to content

Commit

Permalink
Add Cloud Functions Slack (Slash Command) sample. (#151)
Browse files Browse the repository at this point in the history
* Add Cloud Functions Slack Slash Command sample.

* Couple of fixes.

* Update Readme.

* Small fixes.

* Add tests for Slack sample.

* Make lint happy.

* Make lint happy.
  • Loading branch information
jmdobry authored Jul 16, 2016
1 parent 7cfbb9d commit dc54846
Show file tree
Hide file tree
Showing 9 changed files with 456 additions and 28 deletions.
54 changes: 27 additions & 27 deletions datastore/concepts.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,33 +165,33 @@ Entity.prototype.testEntityWithParent = function (callback) {
Entity.prototype.testProperties = function (callback) {
// jshint camelcase:false
// [START properties]
var task = [
{
name: 'type',
value: 'Personal'
},
{
name: 'created',
value: new Date()
},
{
name: 'done',
value: false
},
{
name: 'priority',
value: 4
},
{
name: 'percent_complete',
value: 10.0
},
{
name: 'description',
value: 'Learn Cloud Datastore',
excludeFromIndexes: true
}
];
var task = [
{
name: 'type',
value: 'Personal'
},
{
name: 'created',
value: new Date()
},
{
name: 'done',
value: false
},
{
name: 'priority',
value: 4
},
{
name: 'percent_complete',
value: 10.0
},
{
name: 'description',
value: 'Learn Cloud Datastore',
excludeFromIndexes: true
}
];
// [END properties]

this.datastore.save({
Expand Down
2 changes: 1 addition & 1 deletion datastore/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ function addTask (description, callback) {

datastore.save({
key: taskKey,
data: [
data: [
{
name: 'created',
value: new Date().toJSON()
Expand Down
1 change: 1 addition & 0 deletions functions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ environment.
* [Modules](module/)
* [OCR (Optical Character Recognition)](ocr/)
* [SendGrid](sendgrid/)
* [Slack](slack/)
4 changes: 4 additions & 0 deletions functions/slack/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
config.json
test.js
test/
14 changes: 14 additions & 0 deletions functions/slack/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<img src="https://avatars2.githubusercontent.com/u/2810941?v=3&s=96" alt="Google Cloud Platform logo" title="Google Cloud Platform" align="right" height="96" width="96"/>

# Google Cloud Functions Slack Slash Command sample

This tutorial demonstrates using Cloud Functions to implement a Slack Slash
Command that searches the Google Knowledge Graph API.

View the [source code][code].

[code]: index.js

## Deploy and Test

Read the tutorial at https://cloud.google.com/functions/docs/tutorials/slack
4 changes: 4 additions & 0 deletions functions/slack/config.default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"SLACK_TOKEN": "[YOUR_SLACK_TOKEN]",
"KG_API_KEY": "[YOUR_KG_API_KEY]"
}
162 changes: 162 additions & 0 deletions functions/slack/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Copyright 2016, 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';

// [START setup]
var config = require('./config.json');
var googleapis = require('googleapis');

// Get a reference to the Knowledge Graph Search component
var kgsearch = googleapis.kgsearch('v1');
// [END setup]

// [START formatSlackMessage]
/**
* Format the Knowledge Graph API response into a richly formatted Slack message.
*
* @param {string} query The user's search query.
* @param {Object} response The response from the Knowledge Graph API.
* @returns {Object} The formatted message.
*/
function formatSlackMessage (query, response) {
var entity;

// Extract the first entity from the result list, if any
if (response && response.itemListElement &&
response.itemListElement.length) {
entity = response.itemListElement[0].result;
}

// Prepare a rich Slack message
// See https://api.slack.com/docs/message-formatting
var slackMessage = {
response_type: 'in_channel',
text: 'Query: ' + query,
attachments: []
};

if (entity) {
var attachment = {
color: '#3367d6'
};
if (entity.name) {
attachment.title = entity.name;
if (entity.description) {
attachment.title = attachment.title + ': ' + entity.description;
}
}
if (entity.detailedDescription) {
if (entity.detailedDescription.url) {
attachment.title_link = entity.detailedDescription.url;
}
if (entity.detailedDescription.articleBody) {
attachment.text = entity.detailedDescription.articleBody;
}
}
if (entity.image && entity.image.contentUrl) {
attachment.image_url = entity.image.contentUrl;
}
slackMessage.attachments.push(attachment);
} else {
slackMessage.attachments.push({
text: 'No results match your query...'
});
}

return slackMessage;
}
// [END formatSlackMessage]

// [START verifyWebhook]
/**
* Verify that the webhook request came from Slack.
*
* @param {Object} body The body of the request.
* @param {string} body.token The Slack token to be verified.
*/
function verifyWebhook (body) {
if (!body || body.token !== config.SLACK_TOKEN) {
var error = new Error('Invalid credentials');
error.code = 401;
throw error;
}
}
// [END verifyWebhook]

// [START makeSearchRequest]
/**
* Send the user's search query to the Knowledge Graph API.
*
* @param {string} query The user's search query.
* @param {Function} callback Callback function.
*/
function makeSearchRequest (query, callback) {
kgsearch.entities.search({
auth: config.KG_API_KEY,
query: query,
limit: 1
}, function (err, response) {
if (err) {
return callback(err);
}

// Return a formatted message
return callback(null, formatSlackMessage(query, response));
});
}
// [END makeSearchRequest]

// [START kgSearch]
/**
* Receive a Slash Command request from Slack.
*
* Trigger this function by making a POST request with a payload to:
* https://[YOUR_REGION].[YOUR_PROJECT_ID].cloudfunctions.net/kgsearch
*
* @example
* curl -X POST "https://us-central1.your-project-id.cloudfunctions.net/kgSearch" --data '{"token":"[YOUR_SLACK_TOKEN]","text":"giraffe"}'
*
* @param {Object} req Cloud Function request object.
* @param {Object} req.body The request payload.
* @param {string} req.body.token Slack's verification token.
* @param {string} req.body.text The user's search query.
* @param {Object} res Cloud Function response object.
*/
exports.kgSearch = function kgSearch (req, res) {
try {
if (req.method !== 'POST') {
var error = new Error('Only POST requests are accepted');
error.code = 405;
throw error;
}

// Verify that this request came from Slack
verifyWebhook(req.body);

// Make the request to the Knowledge Graph Search API
makeSearchRequest(req.body.text, function (err, response) {
if (err) {
console.error(err);
return res.status(500);
}

// Send the formatted message back to Slack
return res.json(response);
});
} catch (err) {
console.error(err);
return res.status(err.code || 500).send(err.message);
}
};
// [END kgSearch]
15 changes: 15 additions & 0 deletions functions/slack/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "nodejs-docs-samples-functions-slack",
"description": "Node.js samples found on https://cloud.google.com",
"version": "0.0.1",
"private": true,
"license": "Apache Version 2.0",
"author": "Google Inc.",
"repository": {
"type": "git",
"url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git"
},
"dependencies": {
"googleapis": "^11.0.0"
}
}
Loading

0 comments on commit dc54846

Please sign in to comment.