Google Assistant のUIや機能まとめ（サンプルソース付）#dialogflow

#Google AssistantのUIを試す
Google Assistantで使用するUIや機能をAPI.AI＆Cloud Functionsで実装していきます。今回紹介する内容でGoogle AssistantのBotをある程度作れるようになると思いますので、ぜひトライしてみてください♪

**新しく出たUIについては、その都度更新していきたいと思います。**

##[API.AI](https://api.ai)の準備
- 新しいAgentの作成
    - **言語は英語を選択してください。**
- 新しいIntentsの作成
    - Intents name
       - RequestHello
    - UserSaysに「say hello」と入れる
    - ActionsNameに「request.hello」と入れる

<img width="400" alt="Screen Shot 0029-06-18 at 19.32.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/114922/d04258a9-2b84-5aee-782c-572e61b7b01f.png">

##API.AIとCloud Functionsの接続
こちらの ```index.js``` と ```package.json``` を用意しこれらをGCSにデプロイします。
Cloud functionsの初期設定やデプロイについては[こちらの資料](http://qiita.com/flatfisher/items/c96959a0d8d2daf619d2)をご参考ください。

### ```index.js``` と ```package.json``` の準備

```js:index.js
'use strict';

process.env.DEBUG = 'actions-on-google:*';
const App = require('actions-on-google').ApiAiApp;

//API.AI actions
const REQUEST_HELLO = 'request.hello'

exports.requestassistantui = (request, response) => {
  const app = new App({request, response});
  console.log('Request headers: ' + JSON.stringify(request.headers));
  console.log('Request body: ' + JSON.stringify(request.body));

  // Fulfill action business logic
  function sayHello (app) {
    app.ask('Hello!!!');
  }

  const actionMap = new Map();
  actionMap.set(REQUEST_HELLO, sayHello);

  app.handleRequest(actionMap);
};
```

```json:package.json
{
  "name": "request-assistant-ui",
  "description": "Request Assitant UI",
  "version": "0.0.1",
  "private": true,
  "license": "Apache Version 2.0",
  "author": "Google Inc.",
  "engines": {
    "node": "~6.0"
  },
  "scripts": {
    "lint": "semistandard --fix \"**/*.js\"",
    "start": "functions deploy requestassistantui --trigger-http",
    "deploy": "gcloud beta functions deploy requestassistantui --trigger-http --stage-bucket requestassitantui"
  },
  "dependencies": {
    "actions-on-google": "^1.0.0"
  },
  "devDependencies": {
    "semistandard": "^9.1.0"
  }
}
```

###Webhookの有効化

デプロイが成功したらFunctionsのTriggerURLを控えてください。そしてAPI.AIのFulfillmentのWebhookを有効にし、URLに欄に先ほど控えたURLを入力します。

<img width="500" alt="Screen Shot 0029-06-17 at 16.16.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/114922/a55772a1-94c2-6c48-c5bb-e868aa6f1b4b.png">

Webhookを有効にしたらIntentsのRequestHelloに行き、Use webhookにチェックを入れます。
<img width="500" alt="Screen Shot 0029-06-17 at 16.16.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/114922/7daf21e0-6724-cfb5-7a9c-9f09a9d3b7c0.png">

###とりあえずテスト
今回はシンプルに「Hello」と返してくれる機能を実装しました。試しにAPI.AIの右側にあるシュミレータに「Say hello」と書いてみましょう。「Hello!!!」と返ってくれば成功です。

<img width="328" alt="Screen Shot 0029-06-18 at 19.35.49.png" src="https://qiita-image-store.s3.amazonaws.com/0/114922/a738bddb-5b21-8a96-033a-0064e5af62ef.png">

###IntegrationsのGoogle Assistantを有効化
IntegrationsのGoogle Assistantを有効化してください。そしてTESTボタンを押し、シュミレーターを起動してください。シュミレーターが起動したら「talk to my test app」と入力し、アプリを起動します。そこで「SayHello」と入力し「Hello!!!」と返ってくればOKです。

<img width="384" alt="Screen Shot 0029-06-18 at 19.38.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/114922/c95b643c-52ae-6f3d-6416-e634cf7b1c06.png">

#実装の流れ
使用用途に合わせて以下の様なUIを実装することができます。UIの種類毎に、API.AIでIntentsの作成→Index.jsに必要なコードを記述→テストの順番で行っていきたいと思います。

##BasicCard

<img width="352" alt="Screen Shot 0029-06-19 at 8.10.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/114922/f5675468-c653-1dac-aaef-114eff496cd5.png">

###新しいIntentsの作成
- Name
    - 「BasicCard」
- User says
    - 「Basic card」
- Action name
    - 「basic.card」
- Use webhookにチェック

```js:index.js
...省略...
//API.AI actions
const BASIC_CARD = 'basic.card'

const IMG_URL = 'https://sample.png'

...省略...

function basicCard (app) {
    app.ask(app.buildRichResponse()
    　.addSimpleResponse('This is the first simple response for a basic card')
      .addBasicCard(app.buildBasicCard(`Hello `) 
        .setSubtitle('This is a subtitle')
        .setTitle('Title: this is a title')
        .addButton('This is a button', 'https://assistant.google.com/')
        .setImage(IMG_URL, 'Image alternate text'))
    );
  }

...省略...
actionMap.set(BASIC_CARD, basicCard);
...省略...
```
***.addSimpleResponseは必須です。最初に付けないとエラーが起きます。***

##Carousel

<img width="364" alt="Screen Shot 0029-06-19 at 8.26.40.png" src="https://qiita-image-store.s3.amazonaws.com/0/114922/4a2c8a4b-b2df-08ae-483d-4a4a7b6f620a.png">

###新しいIntentsの作成
- Name
    - 「Carousel」
- User says
    - 「Carousel」
- Action name
    - 「carousel」
- Use webhookにチェック

```js:index.js
...省略...
const CAROUSEL = 'carousel';

...省略...
// Carousel
  function carousel (app) {
    app.askWithCarousel(app.buildRichResponse()
      .addSimpleResponse('This is a simple response for a carousel'),
      app.buildCarousel()
        // Add the first item to the carousel
        .addItems(app.buildOptionItem(SELECTION_KEY_ONE,
          ['synonym of title 1', 'synonym of title 2', 'synonym of title 3'])
          .setTitle('Title of First List Item')
          .setDescription('This is a description of a carousel item')
          .setImage(IMG_URL, 'Image alternate text'))
        // Add the second item to the carousel
        .addItems(app.buildOptionItem(SELECTION_KEY_GOOGLE_HOME,
          ['Google Home Assistant', 'Assistant on the Google Home'])
          .setTitle('Google Home')
          .setDescription('Google Home is a voice-activated speaker powered ' +
            'by the Google Assistant.')
          .setImage(IMG_URL, 'Google Home')
        )
    );
  }
...省略...
actionMap.set(CAROUSEL, carousel);
...省略...
```

##List
<img width="350" alt="Screen Shot 0029-06-19 at 8.43.54.png" src="https://qiita-image-store.s3.amazonaws.com/0/114922/fab79495-31a1-e5b4-aef7-3f9959b1eac8.png">

###新しいIntentsの作成
- Name
    - 「List」
- User says
    - 「list」
- Action name
    - 「list」
- Use webhookにチェック

```js:index.js
...省略...
const LIST = 'list';
...省略...
// List
  function list (app) {
    app.askWithList(app.buildRichResponse()
      .addSimpleResponse('This is a simple response for a list'),
      app.buildList('List Title')
        // Add the first item to the list
        .addItems(app.buildOptionItem(SELECTION_KEY_ONE,
          ['synonym of title 1', 'synonym of title 2'])
          .setTitle('Title of First List Item')
          .setDescription('This is a description of a list item')
          .setImage(IMG_URL, 'Image alternate text'))
        // Add the second item to the list
        .addItems(app.buildOptionItem(SELECTION_KEY_GOOGLE_HOME,
          ['Google Home Assistant', 'Assistant on the Google Home'])
          .setTitle('Google Home')
          .setDescription('Google Home is a voice-activated speaker powered ' +
            'by the Google Assistant.')
          .setImage(IMG_URL, 'Google Home')
        )
    );
  }
...省略...
  actionMap.set(LIST, list);
...省略...
```

##SUGGESTIONS
Suggestionsはボタンの様なもので、BasicCard,Carousel,ListなどのBuilderに追加することでもできます。このUIによりユーザーに適切な選択しを誘導することができます。

<img width="358" alt="Screen Shot 0029-06-19 at 10.10.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/114922/b444fa56-1b64-18d9-ce5c-1fe959f8a039.png">

###新しいIntentsの作成
- Name
    - 「Suggestions」
- User says
    - 「suggestions」
- Action name
    - 「suggestions」
- Use webhookにチェック

```js:index.js
...省略...
const SUGGESTIONS = 'suggestions';
...省略...
// Suggestions
  function suggestions (app) {
    app.ask(app
      .buildRichResponse()
      .addSimpleResponse('This is a simple response for suggestions')
      .addSuggestions(['Basic Card', 'List', 'Carousel'])
      .addSuggestionLink('Suggestion Link', 'https://assistant.google.com/'));
  }
...省略...
actionMap.set(SUGGESTIONS, suggestions);
...省略...
```

#その他の機能紹介
その他重要な機能を紹介します。

##Itemを選択した時の処理
ListやCarouselでItemが選択された時の処理の方法を紹介します。

<img width="358" alt="Screen Shot 0029-06-19 at 10.11.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/114922/2065ef13-a69e-4507-870c-f7cfb5805d0a.png">

###新しいIntentsの作成
- Name
    - 「item selected」
- **Event**
    - 「actions_intent_OPTION」
- Action name
    - 「item.selected」
- Use webhookにチェック

Eventsに ```actions_intent_OPTION``` を入れることによりItem選択時の処理を受け取ることが出来ます。

```js:index.js
...省略...
const ITEM_SELECTED = 'item.selected';
...省略...
// item selected
  function itemSelected (app) {
    console.log('kyohei')
    const param = app.getSelectedOption();
    if (!param) {
      app.ask('You did not select any item from the list or carousel');
    } else if (param === SELECTION_KEY_ONE) {
      app.ask('You selected the first item in the list or carousel');
    } else if (param === SELECTION_KEY_GOOGLE_HOME) {
      app.ask('You selected the Google Home!');
    } else {
      app.ask('You selected an unknown item from the list or carousel');
    }
  }
...省略...
  actionMap.set(ITEM_SELECTED, itemSelected);
...省略...
```

##会話を終了する
<img width="346" alt="Screen Shot 0029-06-19 at 10.18.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/114922/e9a54784-5be2-a1e3-ec20-e81152eecccc.png">

###新しいIntentsの作成
- Name
    - 「Bye」
- User says
    - 「bye」
- Action name
    - 「request.bye」
- Use webhookにチェック

```js:index.js
...省略...
const REQUEST_BYE = 'request.bye';
...省略...
 function bye (app) {
    app.tell('Okay see you later!');
  }
...省略...
  actionMap.set(REQUEST_BYE, bye);
...省略...
```

#参考資料
[Learn how to build apps for the Assistant
](https://developers.google.com/actions/components/?hl=ja)