# @cxco/google-assistant

> DigitalCX's ready-to-go integration between DigitalCX and the Google Assistant

Latest version **2.0.1** (published 2020-06-09) · ISC license · 0 weekly downloads

## Install

```sh
npm install @cxco/google-assistant
pnpm add @cxco/google-assistant
yarn add @cxco/google-assistant
bun add @cxco/google-assistant
```

## Health

**Score 15/100 (F)** — status: abandoned.

Positive: no vulnerabilities.

Warnings: low downloads; no types; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 2.0.1 |
| Published | 2020-06-09 |
| First published | 2019-03-11 |
| Weekly downloads | 0 |
| License | ISC |
| TypeScript types | none |
| Module format | CommonJS |
| Dependencies | 4 |
| Unpacked size | 42.3 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | remco.de.vries@cxcompany.com |
| Maintainers | cx-company, dcustodiocx, kaspersimons, remcodevries |
| Keywords | DigitalCX, cxcompany |

## Links

- npm: https://www.npmjs.com/package/@cxco/google-assistant
- npm.io page: https://npm.io/package/@cxco/google-assistant

## Dependencies (4)

- [ajv](https://npm.io/package/ajv.md) ^6.10.0
- [ssml-builder](https://npm.io/package/ssml-builder.md) ^0.4.3
- [actions-on-google](https://npm.io/package/actions-on-google.md) ^2.12.0
- [@cxco/dcx-connector](https://npm.io/package/@cxco/dcx-connector.md) ^1.3.1

## Recent versions

- 2.0.1 (latest) — 2020-06-09
- 2.0.0 — 2020-01-22
- 1.1.1 — 2019-03-25
- 1.1.0 — 2019-03-22
- 1.0.1 — 2019-03-11
- 1.0.0 — 2019-03-11

## README

# Introduction
The **Google Assistant Integration (GAI)** between DigitalCX and Google Assistant. This package:

* adds basic intent handling
* offers hooks to apply custom logic
* allows you to add custom intents

The basic intents that are supported are:

|Description|Google Actions Intent|DCX endpoint|
|:-|:-|:-|
|Initialize the conversation|actions.intent.MAIN|DCX event (onLoad)|
|Quit the conversation|actions.intent.CANCEL|DCX event (onCancel)|
|Any follow up interactions|actions.intent.TEXT|DCX ask|

## Features
* Hooks into any [Express.js](https://expressjs.com/) server as [Express middleware](https://expressjs.com/en/guide/using-middleware.html).
* Offers full support for Q&A's, events, dialogs and transactional dialogs.
* Each instance can be configured to integrate with a single customer-project-culture combination.
* Multiple instances can easily be created.
* Custom intents can easily be added.
* Default intents can be overwritten.
* Offers hooks to apply custom logic.

# Pre-requisites
In order to get the GAI package up and running you need to:

* Have [Node.js](https://nodejs.org/en/) installed. This Node.js module was developed using Node.js v8.12.0.
* Create an [Express.js](https://expressjs.com/en/starter/installing.html) server to set up the GAI as middleware.
* Setup an Google Actions project through the [Google Action SDK](https://developers.google.com/actions/sdk/) to communicate to Google Assistant.
* Have a project in [Actions on Google](https://console.actions.google.com) of the type conversational.

### First steps

In order to configure it properly with the google assistant you need to have the `gactions` SDK in your system so you can hook up the actions to the project that is running in google.

For this:
- Install the `gactions` sdk
- Make the `gactions` sdk executable by running `chmod ugo+x gactions`
- Run `./gactions init`, this will create an `action.json` file that you will use to set up the calls to your project
- Change the `action.json` to be similar to the one underneath
- After having a proper `action.json` run the command `./gactions update --action_package action.json --project <GOOGLE-ACTION-PROJECT-ID>` to configure which are the intents that exist and where do they map
    - The `<GOOGLE-ACTION-PROJECT-ID>` can be seen in the **Actions on Google project settings**

`action.json`
```json
{
  "actions": [
    {
      "description": "Default Welcome Intent",
      "name": "MAIN",
      "fulfillment": {
        "conversationName": "localhost" // You can define here the conversations that fulfill this intent
      },
      "intent": {
        "name": "actions.intent.MAIN",
        "trigger": {
          "queryPatterns": [
            "talk to digitalcx support portal" // Define a pattern that you use to talk to the actions on google project
          ]
        }
      }
    }
  ],
  "conversations": {
    "localhost": { // Define the conversation "server"
      "name": "localhost", // Give it a name
      "url": "https://example.com/googleassistant" // Your favorite URL
    }
  },
  "locale": "en"
}
```

# Installation

```bash
npm i @cxco/google-assistant
```

# Configuration
The Google Assistant Integration offers a number of project configuration options, some of them required and some of them optional. All configuration options need to be assigned to a single 'actionsProject' object.

|Parameter Name|Type|Description|Required|Example|
|:-|:-|:-|:-|:-|
|project|object|An object containing the information required to connect and authenticate your application with and to the DigitalCX engine.|yes|{<br>&nbsp;&nbsp;&nbsp;"customerKey":"ckey", <br>&nbsp;&nbsp;&nbsp;"projectKey":"pkey", <br>&nbsp;&nbsp;&nbsp;"culture":"en",<br>&nbsp;&nbsp;&nbsp;"apiKey":"api key",<br>&nbsp;&nbsp;&nbsp;"env":"production"<br>}|
|onLoadEvent|string|A string representing the DigitalCX event you'd like the Google Assistant to trigger and welcome the end user with.|yes|dcx-google-assistant-onload|
|onCancelEvent|string|A string representing the DigitalCX event you'd like the Google Assistant to trigger and end the conversation with.|no|dcx-google-assistant-oncancel|

# Basic Usage

```JavaScript
const express = require('express')
const { actionssdk } = require('actions-on-google')
const googleAssistant = require('@cxco/google-assistant')
const bodyParser = require('body-parser')
const app = express()

const actionsApp = actionssdk()

const actionsProject = {
    project: {
        customerKey: 'customerA',
        projectKey: 'projectB',
        culture: 'en',
        apiKey: 'xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx',
        env: 'cms'
    },  
    onLoadEvent: 'on_load',
    onCancelEvent: 'on_end'
}

const googleAssistantPayload = { 
    actionsProject, 
    actionsApp 
}

app.use(bodyParser.json())

app.use('/googleassistant', (req, res, next) => {
    googleAssistant(googleAssistantPayload)(req, res)
})

app.listen(3000)
```

# Hooks
With hooks you can add logic between DigitalCX and Google Assistant.
There are three hooks for every Google Action Intent. You can add custom logic and manipulate the input data inside every hook. 
All specified hooks will trigger for every basic intent ('actions.intent.MAIN', 'actions.intent.TEXT', 'actions.intent.CANCEL'). 

The defined hooks ordered on callstack:

|Hooks|Description|
|:-|:-|
|onAsk|Executes before request to DCX|
|onAnswer|Executes after response from DCX|
|onSSML|Executes after SSML formatting and before request to Google Assistant|



## Hook signature
The hooks are methods of the Hooks class derived from the GAI package. To define a hook you initiate a new hooks instance. Then assign a callback function to the method. This curried function uses two arguments, the intentName and the input. The intentName is a string that can be used inside the hook to add logic for a specific intent. The input argument can be modified inside the hook and must be returned! Assign the class to the Google Assistant payload.

```JavaScript
    hooks.onAsk = (intentName) => (input) => {
      return input
    }
```
Example of multiple hooks:

```JavaScript
const express = require('express')
const { actionssdk } = require('actions-on-google')
const googleAssistant = require('@cxco/google-assistant')
const bodyParser = require('body-parser')
const app = express()
const actionsApp = actionssdk()

// Require hooks class
const Hooks = require('@cxco/google-assistant/hooks')

const actionsProject = {
    project: {
        customerKey: 'customerA',
        projectKey: 'projectB',
        culture: 'en',
        apiKey: 'xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx',
        env: 'cms'
    },  
    onLoadEvent: 'on_load',
    onCancelEvent: 'on_end'
}


// Create new hooks instance

hooks = new Hooks()

/**
* Hook that can modify the DigitalCX request payload per Intent
* @param {String} intentName 'actions.intent.MAIN' || 'actions.intent.TEXT' || 'actions.intent.CANCEL'
* @param {Object} dcxPayload payload for DigitalCX request(@cxco/dcx-connector)
* @return {Object} dcxPayload
*/
hooks.onAsk = intentName => dcxPayload => (
    // Return the DigitalCX payload adding custom context
    { 
        metadata: {
            dimensions: [{ Integration: 'Test' }]
        }
    })

/**
 * Hook that can modify the response from DigitalCX per Intent
 * @param {String} intentName 'actions.intent.MAIN' || 'actions.intent.TEXT' || 'actions.intent.CANCEL'
 * @param {Object} dcxResponse normalised response from DigitalCX (@cxco/dcx-connector)
 * @return {Object} dcxResponse
 */
hooks.onAnswer = intentName => dcxResponse => {

    // Modify the DigitalCX response before parsing it to SSML
    dcxResponse.data.answer = `${dcxResponse.data.answer} (DigitalCX answer is modified by the onAnswer hook)`

    return dcxResponse
}

/**
 * Hook that can modify the Google Actions request payload per Intent
 * https://www.npmjs.com/package/ssml-builder Formatted SSML response for Google Actions
 * @param {String} intentName 'actions.intent.MAIN' || 'actions.intent.TEXT' || 'actions.intent.CANCEL'
 * @param {Object} handlerResponse Result from dcxResponseHandler which contains : data, metadata and speech object
 * @return {Object} handlerResponse
 */
hooks.onSSML = intentName => handlerResponse => {

    // Modify the SSML object on the CANCEL intent / DCX onEnd event.
    if (intentName === 'actions.intent.CANCEL') {
        handlerResponse.speech.sentence('This added sentence to the SSML object is modified by the onSSML hook')
    }

    return handlerResponse
}

app.use(bodyParser.json())

app.use('/googleassistant', (req, res, next) => {
    googleAssistant({ actionsProject, actionsApp, hooks })(req, res)
})

app.listen(3000)
```

# Custom Intents
You can add custom intents and / or overwrite default intents stated in the Google Assistant Integration. This can be done by mapping your custom intents and adding it to the Google Assistant Integration payload.

```JavaScript
const express = require('express')
const { actionssdk } = require('actions-on-google')
const googleAssistant = require('@cxco/google-assistant')
const bodyParser = require('body-parser')
const app = express()
const actionsApp = actionssdk()
const actionsProject = {
    project: {
        customerKey: 'customerA',
        projectKey: 'projectB',
        culture: 'en',
        apiKey: 'xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx',
        env: 'cms'
    },  
    onLoadEvent: 'on_load',
    onCancelEvent: 'on_end'
}

const customIntents = new Map()

// Overwriting a custom intent
customIntents.set('customIntentName', (conv, input) => {
    conv.ask('Hello world!')
})

// Overwriting a default intent
customIntents.set('actions.intent.MAIN', (conv, input) => {
    conv.ask('Hello world!')
})

app.use(bodyParser.json())

app.use('/googleassistant', (req, res, next) => {
    googleAssistant({ actionsProject, actionsApp, customIntents })(req, res)
})

app.listen(3000)
```

# Custom Intents using the dcx-connector
The **[dcx-connector](https://www.npmjs.com/package/@cxco/dcx-connector/)** package can be used to connect to the DCX endpoints and retrieve normalized answers. 
The **dcxResponseHandler** function (included in the GAI) returns data and metadata from the connector and formatted SSML.

Installing the dcx-connector:

```bash
npm i @cxco/dcx-connector
```

Usage:

```Javascript
const express = require('express')
const { actionssdk } = require('actions-on-google')
const googleAssistant = require('@cxco/google-assistant')
const { dcxResponseHandler } = require('@cxco/google-assistant/formatter')
const { doGetRequest } = require('@cxco/dcx-connector')
const bodyParser = require('body-parser')
const app = express()

const actionsApp = actionssdk()
const actionsProject = {
    project: {
        customerKey: 'customerA',
        projectKey: 'projectB',
        culture: 'en',
        apiKey: 'xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx',
        env: 'cms'
    },  
    onLoadEvent: 'on_load',
    onCancelEvent: 'on_end'
}

const customIntents = new Map()

customIntents.set('customIntentName', async (conv, input) => {
    
    const dcxPayload = {
        type: 'ask',
        data: {
            userInput: input
        },
        metadata: {
            sessionId: conv.user.storage.id,
            tDialogState: conv.user.storage.tDialogState,
        },
        config: {
            baseUrl: 'https://your-digitalcx-endpoint',
            apiKey: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx',
            culture: 'en'
        }
    }

    const dcxResponse = await doGetRequest(dcxPayload);
    const handlerResponse = await dcxResponseHandler({
        dcxResponse,
        conv,
        config
    })

    const { speech, data, metadata } = handlerResponse 

    conv.ask(speech.ssml())
})

app.use(bodyParser.json())

app.use('/googleassistant', (req, res, next) => {
    googleAssistant({ actionsProject, actionsApp, customIntents })(req, res)
}
```



# Build and Test
To run the test suite, first install the dependencies, then run npm test:
```bash
npm i
npm test
```

---
_Source: https://npm.io/package/@cxco/google-assistant · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
