Skip to content

Commit

Permalink
v0.1.0 done
Browse files Browse the repository at this point in the history
  • Loading branch information
amowu committed Dec 29, 2015
1 parent d0510a0 commit 3920a47
Show file tree
Hide file tree
Showing 10 changed files with 274 additions and 0 deletions.
64 changes: 64 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
![slack-lambda-google](https://cloud.githubusercontent.com/assets/559351/12033086/4eb38aba-ae5a-11e5-8dbf-50e82f94424d.png)

# Google God

A serverless [Slack Slash Commands](https://api.slack.com/slash-commands) to integrate [Google Knowledge Graph API](https://developers.google.com/knowledge-graph/) using [AWS Lambda](https://aws.amazon.com/lambda/) and [AWS API Gateway](https://aws.amazon.com/api-gateway/).

![screenshot](https://cloud.githubusercontent.com/assets/559351/12034616/835b2ad4-ae6e-11e5-8b89-fc7b486fd47e.gif)

## Getting Started

This project was built with Ryan Ray's repo [slack-lambda-weather](https://github.com/ryanray/slack-lambda-weather) and his [post](http://www.ryanray.me/serverless-slack-integrations).

### Prerequisites

* Install [AWS CLI](https://aws.amazon.com/cli/)
* Execution Role ARN for your AWS Lambda
* Create a `config.json` based on `config.sample.json`. This file is gitignored by default because this is where you would put any API key's and other secret info that your lambda may need.
* [Google Knowledge Graph Search API](https://developers.google.com/knowledge-graph/) API key, and paste it to `config.json`.

### AWS Lambda

> Lambda is based on EC2 and allows you to deploy and execute your code (Node.js, Java, Python) without having to provision servers.
First build and create your Lambda function on AWS:

```sh
npm run create LAMBDA_FUNCTION_NAME EXECUTION_ROLE_ARN
```

> Before you can create your Lambda you need to create an execution role. If you did any of the Lambda hello world tutorials in the AWS console you should already have a role created. Either way you need to goto the [IAM Management Console - Roles](https://console.aws.amazon.com/iam/home#roles). Get the ARN of `lambda_basic_execution` or create a new role based on `role.example.json` and get the ARN from that. The full ARN looks something like `arn:aws:iam::YOUR_ACCOUNT_ID:role/lambda_basic_execution`.
Build and deploy to update your Lambda code:

```sh
npm run deploy LAMBDA_FUNCTION_NAME
```

### AWS API Gateway

> Lambda responds to events, which can come from a variety of sources. By default Lambda isn't accessable from a URL, but API Gateway allows you to map a URL and an HTTP method to trigger your Lambda code. You can setup GET, POST, PUT, etc... and map the parameters/body into a JSON payload that Lambda understands.
1. Goto [AWS API Gateway](https://aws.amazon.com/api-gateway/) and Create new API - name it whatever you want - we'll do LambdaTest for now
2. Create a new resource, name it whatever
3. Create a POST method under your resource
4. Select Integration type with Lambda Function, and select your Lambda region, and enter your Lambda function name
5. Save and give API Gateway permission to invoke your Lambda function
6. Click on Integration Request > Mapping Templates
7. Add mapping Template for `application/x-www-form-urlencoded` and click checkmark
8. Change input passthrough to mapping template and paste this template [gist](https://gist.github.com/ryanray/668022ad2432e38493df) that can help you to convert `application/x-www-form-urlencoded` POST from Slack to Lambda's `application/json` format
9. Save and click Deploy API with a new stage, then you can see a public invoke URL

### Slack

1. Goto Slack App `https://YOUR_TEAN_DOMAIN.slack.com/apps/manage`
2. Search `Slash Commands` and add a new configuration
3. Choose a command, for this example enter `/google` in the command name input, click Add Slash Command Integration button.
4. Now you should be on the settings page, scroll down and copy the token to your `config.json` (NOTE: you don't want to expose the token to the public!).
5. Copy and paste your API Gateway invoke URL to URL field.

## Contributing

Improvements are welcome! Just fork, push your changes to a new branch, and create a pull request!

![slack-lambda-google-ex](https://cloud.githubusercontent.com/assets/559351/12034120/721ea5a4-ae67-11e5-8d7a-297cbaa1a51d.png)
4 changes: 4 additions & 0 deletions config.sample.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"GOOGLE_API_KEY": "YOUR_GOOGLE_API_KEY",
"SLASH_COMMANDS_TOKEN": "YOUR_SLACK_SLASH_COMMAND"
}
35 changes: 35 additions & 0 deletions lambda.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
var config = require('./config.json');
var messages = require('./messages.json');
var google = require('./lib/google.js');

/**
* Entrypoint for AWS Lambda
* @param event is the JSON payload that API Gateway transformed from slack's application/x-www-form-urlencoded request
* @param context has methods to let Lambda know when we're done - similar to http/express modules `response.send()`
*/
exports.handler = function (event, context) {
console.log(event);

// Verify request came from slack
if (event.token !== config.SLASH_COMMANDS_TOKEN) {
return context.fail(messages.UNAUTHORIZED_TOKEN);
}

google(event.text)
.then(function (response) {
context.succeed(response);
})
.catch(function (error) {
console.error(error);
context.succeed({
'response_type': 'in_channel',
'attachments': [
{
'fallback': messages.ERROR_FALLBACK,
'text': messages.ERROR_TEXT,
'color': 'danger'
}
]
});
});
};
77 changes: 77 additions & 0 deletions lib/google.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
var _ = require('lodash');
var request = require('request-promise');

var config = require('../config.json');
var messages = require('../messages.json');

// Language priority for search result
var languages = [
'zh-TW',
'zh',
'en',
'ja'
];
// Google API params
var params = {
'limit': 1,
'languages': 'zh,en,ja'
};

module.exports = function (text) {
return request('https://kgsearch.googleapis.com/v1/entities:search?' +
'query=' + text.trim() + '&' +
'key=' + config.GOOGLE_API_KEY + '&' +
'limit=' + params.limit + '&' +
'languages=' + params.languages)
.then(function (response) {
var data = JSON.parse(response);

var result = _.get(data, ['itemListElement', 0, 'result']);
if (!result) {
return {
'response_type': 'in_channel',
'attachments': [
{
'fallback': messages.RESULT_NOT_FOUND,
'text': messages.RESULT_NOT_FOUND_TEXT,
'color': 'danger'
}
]
};
}

var name = item(result, 'name', '@language');
var title = _.get(name, '@value');

var detailedDescription = item(result, 'detailedDescription' ,'inLanguage');

return {
'response_type': 'in_channel',
'attachments': [
{
'fallback': title,
'title': title,
'title_link': _.get(detailedDescription, 'url'),
'text': _.get(detailedDescription, 'articleBody'),
'thumb_url': _.get(result, ['image', 'contentUrl'])
}
]
};
});
};

/**
* Get only one item from search result by language priority list
* @param {Object} result search result
* @param {string} children item name
* @param {string} key item language property name
* @return {Object} filter object
*/
function item (result, children, key) {
var items = _.chain(result).get(children).value();
var language = _.chain(languages).filter(function (lang) {
return _.find(items, _.matchesProperty(key, lang));
}).first().value();

return _.find(items, _.matchesProperty(key, language));
}
7 changes: 7 additions & 0 deletions messages.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"UNAUTHORIZED_TOKEN": "Unauthorized Request. Check Your Slash Commands Token.",
"ERROR_FALLBACK": "告訴你一件很恐怖的事⋯⋯",
"ERROR_TEXT": "◢▆▅▄▃崩╰(〒皿〒)╯潰▃▄▅▇◣",
"RESULT_NOT_FOUND_FALLBACK": "告訴你一件很恐怖的事⋯⋯",
"RESULT_NOT_FOUND_TEXT": "幹!我找不到⋯ಥ_ಥ"
}
21 changes: 21 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "slack-lambda-google",
"version": "0.1.0",
"description": "A serverless Slack Slash Commands to integrate Google Knowledge Graph API using AWS Lambda and AWS API Gateway",
"main": "lambda.js",
"scripts": {
"build": "./scripts/build.sh",
"create": "./scripts/create.sh",
"deploy": "./scripts/deploy.sh"
},
"author": "Amo Wu <amo260@gmail.com>",
"license": "Apache-2.0",
"dependencies": {
"lodash": "^3.10.1",
"request-promise": "^0.4.3"
},
"repository": {
"type": "git",
"url": "https://github.com/amowu/slack-lambda-google.git"
}
}
21 changes: 21 additions & 0 deletions role.example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Action": [
"lambda:InvokeFunction"
],
"Effect": "Allow",
"Resource": "arn:aws:lambda:YOUR_REGION:YOUR_ACCOUNT_ID:function:*"
}
]
}
13 changes: 13 additions & 0 deletions scripts/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/sh -e

if [ ! -f ./config.json ]; then
echo "Unable to build Lambda.zip, \"config.json\" is required!"
exit 1
fi

npm install

rm -rf ./dist
mkdir -p dist

zip -r -q dist/lambda.zip . -i "lib/*" "node_modules/*" "config.json" "messages.json" "lambda.js" "package.json" -x "*/.DS_Store"
19 changes: 19 additions & 0 deletions scripts/create.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/sh -e

[[ -z "$1" ]] && echo "Lambda function name must be provided, example: npm run create myLambdaFunction \"arn:aws:iam::YOUR_AWS_ACCOUNT_ID:role/lambda_basic_execution\"" && exit 1;
[[ -z "$2" ]] && echo "IAM Role ARN must be provided, example: npm run create myLambdaFunction \"arn:aws:iam::YOUR_AWS_ACCOUNT_ID:role/lambda_basic_execution\"" && exit 1;

echo "build..."

./scripts/build.sh

echo "create..."

aws lambda create-function \
--function-name "$1" \
--runtime nodejs \
--role "$2" \
--handler lambda.handler \
--timeout 6 \
--memory-size 512 \
--zip-file fileb://$(pwd)/dist/lambda.zip
13 changes: 13 additions & 0 deletions scripts/deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/sh -e

[[ -z "$1" ]] && echo "Lambda function name must be provided, example: npm run deploy myLambdaFunction" && exit 1;

echo "build..."

./scripts/build.sh

echo "deploy..."

aws lambda update-function-code \
--function-name "$1" \
--zip-file fileb://$(pwd)/dist/lambda.zip

0 comments on commit 3920a47

Please sign in to comment.