Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Gnzlt committed Mar 21, 2017
1 parent aa2272f commit 28f039a
Show file tree
Hide file tree
Showing 6 changed files with 392 additions and 0 deletions.
150 changes: 150 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@

# Created by https://www.gitignore.io/api/webstorm,osx,windows,node

### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Typescript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env


### OSX ###
*.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon


# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

### WebStorm ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

# User-specific stuff:
.idea/
*.zip

## File-based project format:
*.iws

## Plugin-specific files:

# IntelliJ
/out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties

### WebStorm Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721

# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr

### Windows ###
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db

# Folder config file
Desktop.ini

# Recycle Bin used on file shares
$RECYCLE.BIN/

# Windows Installer files
*.cab
*.msi
*.msm
*.msp

# Windows shortcuts
*.lnk

# End of https://www.gitignore.io/api/webstorm,osx,windows,node
122 changes: 122 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# AlexaApiAiBridge
Bridge to connect Amazon Alexa to Api.ai using an AWS Lambda Function.

## Steps
### 1. Create a new Alexa Skill
#### Account
* Navigate to the [Amazon Developer Portal](https://developer.amazon.com/edw/home.html). Sign in or create a free account.
* Select **"Getting Started"** under Alexa Skills Kit.
* Select **"Add a New Skill"**.

#### Skill Information
* Select **English (U.S.)** as the Skill language *(it's the only way to use [LITERAL slot types](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/alexa-skills-kit-interaction-model-reference#literal-slot-type-reference) to recognize words without converting them).*
* Select the **Custom Interaction Model** *Skill type*.
* Add the **Name** and the **Invocation Name** of the skill.

#### Interaction Model
* Use the next JSON as **Intent Schema**:

```json
{
"intents": [
{
"intent": "ApiIntent",
"slots": [
{
"name": "Text",
"type": "AMAZON.LITERAL"
}
]
},
{
"intent": "AMAZON.CancelIntent"
},
{
"intent": "AMAZON.StopIntent"
},
{
"intent": "AMAZON.HelpIntent"
}
]
}
```
* Create a **Custom Slot Type**:
* Type: `list_test`
* Values: `test`
* Use the next **Sample Utterances**:

```
ApiIntent {test|Text}
ApiIntent {hello test|Text}
```
* Copy the **Alexa App Id** (upper-left corner) to use it later in the [Final Configuration section](#final-configuration).

#### Skill Configuration
* Select **AWS Lambda ARN (Amazon Resource Name)** as *Service Endpoint Type*.
* Select *Region* depending on your Lambda region and paste your Lambda **ANR** into the *Endpoint* field when you have it.

### 2. Create a new Api.ai Agent
#### Account
* Log in to the [Api.ai console](https://console.api.ai/api-client/).
* [Create a new agent](https://console.api.ai/api-client/#/newAgent) and fill all necessary information. Then click **Save** to continue.

#### Intents
* Select **Default Welcome Intent**:
* Add or modify any text responses which will be triggered as the first welcome response.

* Select **Default Fallback Intent**:
* Add **`FALLBACK`** as a trigger Event.
* Add or modify any text responses which will be triggered if a user's input is not matched by any of the regular intents or enabled domains.

* Create a new Intent called **Default Bye Event**:
* Add **`BYE`** as a trigger Event.
* Add or modify any text responses which will be triggered as a goodbye response.

#### Agent Settings
* Select the the gear icon (upper-left corner) and go to **Settings**.
* Copy your **Client access token** to use it later in the [Final Configuration section](#final-configuration).


### 3. Create an AWS Lambda Function
#### AWS Account
* If you do not already have an account on AWS, go to [Amazon Web Services](http://aws.amazon.com/) and create an account.
* Log in to the [AWS Management Console](https://console.aws.amazon.com/) and navigate to AWS Lambda.

#### Create a Lambda Function
* Click the region drop-down in the upper-right corner of the console and select either **US East (N. Virginia)** or **EU (Ireland)** *(These are the only two regions currently supported for Alexa skill development on AWS Lambda, and choosing the right region will guarantee lower latency).*
* If you have no Lambda functions yet, click **Get Started Now**. Otherwise, click **Create a Lambda Function**.

#### Select blueprint and Configure triggers
* Select **Blank Function** as blueprint.
* When prompted to *Configure Triggers*, click the box and select **Alexa Skills Kit**, then click **Next**.

#### Configure function
* Enter a **Name** and choose **Node.js 4.x** as the *Runtime*.

##### Lambda function code
* [**Download the zip file**](https://github.com/Gnzlt/AlexaApiAiBridge/releases/latest) from the latest release of this repo.
* Drop down the *Code entry type* menu and select **Upload a .ZIP file**.
* Click on the **Function package** upload button and choose the file you just downloaded.

##### Lambda function handler and role
* Set your handler and role as follows:
* Keep Handler as ‘index.handler’.
* Drop down the *Role* menu and select **“Create a new custom role”**. This will launch a new tab in the IAM Management Console. (Note: if you have already used Lambda you may already have a `lambda_basic_execution` role created that you can use.)
* You will be asked to set up your Identity and Access Management or “IAM” role if you have not done so. AWS Identity and Access Management (IAM) enables you to securely control access to AWS services and resources for your users. Using IAM, you can create and manage AWS users and groups, and use permissions to allow and deny their access to AWS resources. We need to create a role that allows our skill to invoke this Lambda function. In the Role Summary section, select "Create a new IAM Role" from the IAM Role dropdown menu. The Role Name and policy document will automatically populate.
* Select **“Allow”** in the lower right corner and you will be returned to your Lambda function.
* Keep the Advanced settings as default and select **‘Next’**.

#### Review
* Review the lambda details and select **‘Create Function’**.

### Final Configuration
* Copy the Lambda **ARN** (upper-right corner) and use in the [Alexa Skill Configuration section](#skill-configuration).
* Go to your Lambda **Code** tab.
* Replace `ALEXA_APP_ID` with your **Alexa App Id** and `APIAI_CLIENT_ACCESS_TOKEN` with your **Api.ai Client Access Token**:

```
const ALEXA_APP_ID = 'amzn1.ask.skill.app.your-skill-id';
const APIAI_CLIENT_ACCESS_TOKEN = 'your-apiai-client-access-token';
```


84 changes: 84 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
'use strict';

var Alexa = require('alexa-sdk');
var ApiAi = require('apiai');

const ALEXA_APP_ID = '';
const APIAI_CLIENT_ACCESS_TOKEN = '';

var apiAi = ApiAi(APIAI_CLIENT_ACCESS_TOKEN);
var alexaSessionId;

exports.handler = function (event, context) {
var alexa = Alexa.handler(event, context);
alexa.appId = ALEXA_APP_ID;
alexa.registerHandlers(handlers);
alexa.execute();
};

var handlers = {
'LaunchRequest': function () {
var self = this;
alexaSessionId = getTruncatedSessionIdFromAlexa(self.event.session.sessionId);
apiAi.eventRequest({name: 'WELCOME'}, {sessionId: alexaSessionId})
.on('response', function (response) {
var speech = response.result.fulfillment.speech;
self.emit(':ask', speech, speech);
})
.on('error', function (error) {
self.emit(':tell', error);
})
.end();
},
'ApiIntent': function () {
var self = this;
var text = self.event.request.intent.slots.Text.value;
if (text) {
apiAi.textRequest(text, {sessionId: alexaSessionId})
.on('response', function (response) {
var speech = response.result.fulfillment.speech;
if (response.result.actionIncomplete) {
self.emit(':ask', speech, speech);
} else {
self.emit(':tell', speech);
}
})
.on('error', function (error) {
self.emit(':tell', error.message);
})
.end();
} else {
self.emit('Unhandled');
}
},
'AMAZON.CancelIntent': function () {
this.emit('AMAZON.StopIntent');
},
'AMAZON.StopIntent': function () {
var self = this;
apiAi.eventRequest({name: 'BYE'}, {sessionId: alexaSessionId})
.on('response', function (response) {
self.emit(':tell', response.result.fulfillment.speech);
})
.on('error', function (error) {
self.emit(':tell', error.message);
})
.end();
},
'Unhandled': function () {
var self = this;
apiAi.eventRequest({name: 'FALLBACK'}, {sessionId: alexaSessionId})
.on('response', function (response) {
var speech = response.result.fulfillment.speech;
self.emit(':ask', speech, speech);
})
.on('error', function (error) {
self.emit(':tell', error.message);
})
.end();
}
};

function getTruncatedSessionIdFromAlexa(alexaSessionId) {
return alexaSessionId.split('amzn1.echo-api.session.').pop();
}
15 changes: 15 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "alexa-apiai-bridge",
"version": "1.0.0",
"description": "Bridge to connect Amazon Alexa to Api.ai using an AWS Lambda Function",
"author": "Gnzlt",
"main": "index.js",
"repository": {
"type": "git",
"url": "https://github.com/Gnzlt/AlexaApiAiBridge.git"
},
"dependencies": {
"alexa-sdk": "^1.0.7",
"apiai": "^4.0.1"
}
}
19 changes: 19 additions & 0 deletions speechAssets/IntentSchema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"intents": [
{
"intent": "ApiIntent",
"slots": [
{
"name": "Text",
"type": "AMAZON.LITERAL"
}
]
},
{
"intent": "AMAZON.CancelIntent"
},
{
"intent": "AMAZON.StopIntent"
}
]
}
2 changes: 2 additions & 0 deletions speechAssets/Utterances.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ApiIntent {test|Text}
ApiIntent {hello test|Text}

0 comments on commit 28f039a

Please sign in to comment.