Google Home連携でタスク登録 & 確認をしてみよう!

著者名:三宅 智子(サイボウズ)

目次

caution
警告

記事内で利用しているライブラリ「 request (External link) 」は、非推奨(deprecated)になりました。
HTTPリクエストができる他のライブラリ( axios (External link) など)や、 https.requst (External link) に書き換えることをおすすめします。

概要

皆さんこんにちは!
価格もお手軽なスマートスピーカーということで人気の Google Home (External link)
今回はkintoneと連携してタスク登録と今日締め切りのタスク確認をします。
工夫するといろんなパターンの会話を作ることができますが、ここでは基本的な作り方を解説するためにシンプルな例で紹介したいと思います。
このサンプルコードは、Node.jsでのコーディングを含みます。

シナリオ

大まかなシナリオは、以下の2つです。

  • タスク登録
  • 今日締切のタスク確認

Actions on googleとは

Google Assistant用のアプリケーションを作成するためのプラットフォームです。
Google Assistantに独自のサービスを追加したり、Google Homeと会話するためのアプリケーションを作成したりできます。
また、細かい会話ロジックの作成は、Dialogflow/Smart home/Actions SDK/Converse.AIのいずれかを選択して行います。
Converse AIは日本語での対話に対応していないので、ご注意ください。
詳細は公式サイト Google Assistant (External link) を確認してください。

Dialogflowとは

自然言語の対話プラットフォームです。
Google Assistant上で、シンプルな会話ロジックを作成するためのアプリケーションです。
ちなみにGoogle Assistant以外でのロジック作成にも使うことが可能です。
また、Webhookも利用できます。
詳細は Dialogflowドキュメント (External link) を確認してください。

Firebaseとは

MBaas(モバイルBackend as a Service)と呼ばれるクラウドデータベースサービスです。
今回はDialogflow内でのスクリプト置き場 & 実行環境として利用しています。
まだβ版な点に注意して利用してください。

  • 公式サイト (External link)
  • 料金体系 (External link)
    今回はFirebaseのCloud Functionsを利用します。
    Google関連以外のAPIリクエストをする場合は、無料のSparkプランでは対応していないので、FlameやBlazeプランにアップグレードする必要があります。
    FlameやBlazeプランでも、無料で使える枠はあります。

完成イメージ

諸々の設定が完了してGoogle Homeに話しかけてみると、以下のようなやりとりができます。
Actions on GoogleのSimulatorで実行しています。(正式なアプリとしては公開していません)

会話の流れ

会話の詳細な流れは以下になります。
タスク追加や今日締切のタスク取得のところで、kintoneへHTTP Requestをしています。

手順概要

Google Home連携には各サービスでいくつか細かい設定が必要になります。
今回は以下の順番で設定します。

  1. kintoneでのアプリ作成
  2. Google Homeアプリのインストールと設定
  3. Actions on Googleの設定
  4. Dialogflowの設定

下準備

  • kintone環境
    kintoneアカウントを持っていない方は、1年間無料の「 開発者ライセンス 」を利用してください。
  • Googleアカウント
  • Firebase FlameやBlazeプラン
caution
警告

FreeプランだとkintoneへのHTTP Requestができません。
Firebase FlameやBlazeプランは、クレジットカードの登録が必要、かつ利用量によって料金が発生するので、ご利用の際はご注意ください。

kintoneのアプリ作成

タスクを登録 & 確認するためのkintoneアプリを作成します。

  1. kintoneにログイン後、アプリストアから「To Do」アプリを追加します。
    アプリの追加方法はヘルプサイトの サンプルアプリを追加する (External link) を参照してください。

  2. 追加したアプリ内で以下のフィールド名とフィールドコードが存在することを確認してください。

    フィールドタイプ フィールド名 フィールドコード
    日付 締切日 Duedate
    文字列(1行) ToDo名 To_Do
  3. アプリIDをメモしておいてください。
    のちほどコード内で指定します。
    例)https://{subdomain}.cybozu.com/k/xxx/ ←アプリIDはxxxの部分の数字です。

  4. APIトークンを生成してください。
    アクセス権は、「レコード閲覧」と「レコード追加」にチェックを入れます。
    こちらものちほどコード内で指定するので、メモしておいてください。
    APIトークンの生成方法は、 APIトークンを生成する (External link) を参照してください。

これにてkintoneの設定は完了です。

Google Homeアプリのインストールと設定

次は、Google Homeを使うための初期設定です。

  1. 電源ケーブルをGoogle Homeに差し込むます。
  2. 電源アダプターをコンセントに差し込みます。
  3. モバイルやタブレットでGoogle Homeアプリをダウンロードして実行します。

Google公式サイト (External link) やレビューサイトにも手順が載っているので、参考にしながら進めてください。
アプリで 言語設定を日本語へ変更 (External link) するのを忘れないようにしてください。

Actions on Googleの設定

続いて、Google Assistant用のアプリケーションを作成します。
基本英語ドキュメントしかないのですが、日本語訳されている方がいるので、 Actions on Googleの開発者向けドキュメントの日本語訳リスト (External link) を私は活用させていただきました!

  1. Actions on Googleの設定の前に、利用するGoogleアカウントでGoogle Homeを利用するための設定を1つ行います。
    Googleアカウントのアクティビティ管理サイト (External link) にアクセスして、以下の項目を有効にしてください。

    • Webとアプリのアクティビティ(その下のチェックボックスにもチェックを入れる)
    • 音声アクティビティ
  2. Actions on Googleのサイト (External link) にアクセスします。

  3. Googleアカウントにログインしていない場合は、Google Homeと紐付けたGoogleアカウントでログインします。

  4. 「ACTIONS CONSOLE」ボタンをクリックします。

  5. 新規にプロジェクトを作成するので「Add/ import project」をクリックします。
    右上の赤枠のアイコンを見て、別のGoogleアカウントにログインされている場合は、アイコンをクリックしてアカウントを切り替えてください。

  6. 「Project name」と「Country/region」をそれぞれ以下に設定して、「CREATE PROJECT」ボタンをクリックします。

  7. Overviewの画面に遷移するので、会話ロジック作成に利用する「Dialogflow」のボックス右下の「BUILD」をクリックします。

  8. 「CREATE ACTIONS ON DIALOGFLOW」ボタンをクリックして、Dialogflowの画面に移動します。

Dialogflowの設定

Actions on GoogleからDialogflowアカウントが自動生成されて、以下の画面に移動するかと思います。
こちらで会話の詳細なロジックを組み立ていきます。

まず、Dialogflowを設定していく上でよく出てくる2つの言葉の意味を紹介します。

  • Intents:会話のブロックを作成する項目です。
  • Fulfillment:WebhookやFirebaseのInline Editorを使える項目です。
    ここでNode.jsを記述して、kintoneにHTTP Requestをします。

これらを頭に置いた上で、設定していきましょう。

  1. プロジェクトの初期設定をします。
    「DEFAULT LANGUAGE」欄で、「Japanese - ja」を選択して、「CREATE」ボタンをクリックしてください。

  2. Intentsの作成画面に移動します。
    デフォルトで、「Default Fallback Intent」(エラーなどが起きた時の処理)と「Default Welcome Intent」(会話のきっかけ処理)が設置されているので、これはこのまま利用します。
    この後はここに必要なIntentを増やしていく流れになります。

  3. まずはGoogle Homeとの会話のきっかけとなる「Default Welcome Intent」を編集するので、クリックしてください。

    以下の図に沿ってユーザーが何といったいった時に、どう答えるのかを指定します。
    入力できたら上の「SAVE」ボタンをクリックして設定を保存してください。

    User says Text Response
    こんにちは こんにちは!kintoneに登録したいタスクを話しかけてください。
    また「タスクを確認」と話しかけると、今日締切のタスクをお伝えします。
  4. お気付きの方もいらっしゃると思いますが、Intentを設定している右側に「Test Console」があります。
    いったん会話のきっかけがちゃんとできているかどうか確かめてみましょう!
    ダイアログボックスに「こんにちは」と入力してみてください。
    レスポンスに指定した値が入ってくれば成功です。
    このまま続けて設定していきましょう。

  5. 次に、左メニューの「Fulfillment」をクリックしてください。
    「Webhook」と「Inline Editor」の2つの選択肢が出てきますが、今回はFirebaseにコードを置くのでInline Editorの方を「ENABLED」にします。
    Editor下の「DEPLOY」ボタンを押して「View execution logs in the Firebase console」という文言が表示されるまで待ちます。(デプロイ自体に数分かかる場合があります)
    これでIntentをコード内で指定して操作できます。
    今はコードの編集は必要ありません。
    そのままDEPLOYボタンをクリックしてください。
    AWSのLambda & API Gateway/Zapier/Microsoft FlowなどでWebhookを生成した場合は、Webhook URLに設定することで開発可能です。

  6. 左メニューより「Intents」に戻ります。

  7. 「CREATE INTENT」ボタンをクリックして、新規にタスク登録用のIntentを追加します。

  8. 下図に沿ってIntentの詳細設定をします。
    設定できたら「SAVE」ボタンをクリックしてください。
    「〇〇を追加」という合言葉で新規にkintoneにタスクを登録できます。
    さらに質問に続けてタスクの締切日を答えると、それも併せてレコードに登録されます。

    Intent name User says Action Fulfillment
    AddTask タスクを追加
    (タスクをクリックして、@sys.anyを選択)
    input.task
    (パラメーターもtaskとdateを上図のとおり設定)
    Use webhookにチェック
    (コード内で操作を制御するため)
  9. 次に、今日締切のタスクを確認するためのIntentを作成します。
    先ほどと同様に「CREATE INTENT」ボタンをクリックして、下図のとおり設定して、「SAVE」ボタンをクリックしてください。

    Intent name User says Action Fulfillment
    CheckTask
    • タスクを確認
    • タスクの確認
    • 今日のタスクは?
    • 本日のタスク
    • 今日締切のタスクは?
    • 締切のタスクは?
    check.task Use webhookにチェック
    (コード内で操作を制御するため)
  10. あとは会話の終わりを定義してIntentの設定は完了です。
    「CREATE INTENT」より以下のとおり設定して、「SAVE」ボタンをクリックします。

    Intent name User says Action Text response Google Assistant
    EndConversation
    • 終わり
    • またね
    • バイバイ
    • ありがとう
    end.conversation ありがとうございました!またね End conversationにチェック
    (会話の最後のため)
  11. これでIntentの設定がすべて完了したので、左メニューよりFulfillmentにアクセスし、Inline Editorの「index.js」にkintoneへHTTPリクエストをするためのコードを記述します。
    コード部分はDialogflowのFulfillmentにあらかじめ記載されているサンプルコードを一部そのまま利用しています。
    コード内の最初の方にkintoneアプリの次の情報を記述している箇所があるので、それぞれの環境に合わせた内容に変更してください。

    • appId
    • token
    • domain

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    
    /*
    * Google Home sample program
    * Copyright (c) 2018 Cybozu
    *
    * Licensed under the MIT License
    */
    
    (() => {
      'use strict';
    
      // Cloud Functions for Firebaseライブラリ
      const functions = require('firebase-functions');
    
      // Google Assistant helperライブラリ
      const DialogflowApp = require('actions-on-google').DialogflowApp;
      const googleAssistantRequest = 'google';
    
      // HTTP Request用モジュール
      const req = require('request');
    
      // kintoneアプリ情報(要編集)
      const subdomain = '<サブドメイン名(〇〇.cybozu.comの〇〇の部分)>';
      const appId = '<アプリID>';
      const token = '<APIトークン>';
    
      // kintoneへのタスク登録 & 確認関数
      const addTask = (request, response) => {
    
        let action = request.body.result.action;
        const parameters = request.body.result.parameters;
        const requestSource = (request.body.originalRequest) ? request.body.originalRequest.source : undefined;
        const app = new DialogflowApp({request: request, response: response});
    
        // Dialogflow送信用のGoogle Assistant Response作成関数
        const sendGoogleResponse = (responseToUser) => {
          if (typeof responseToUser === 'string') {
            app.ask(responseToUser);
          } else {
            let googleResponse = app.buildRichResponse().addSimpleResponse({
              speech: responseToUser.speech || responseToUser.displayText,
              displayText: responseToUser.displayText || responseToUser.speech
            });
    
            if (responseToUser.googleRichResponse) {
              googleResponse = responseToUser.googleRichResponse;
            }
    
            if (responseToUser.googleOutputContexts) {
              app.setContext(...responseToUser.googleOutputContexts);
            }
    
            // DialogflowとGoogle Assistantへのレスポンス送信
            app.ask(googleResponse);
          }
        };
    
        // Dialogflow送信用のResponse作成関数
        const sendResponse = (responseToUser) => {
          // もしレスポンスが文字列の場合、そのままユーザーへのレスポンスとして送信
          if (typeof responseToUser === 'string') {
            const responseJson = {};
            responseJson.speech = responseToUser;
            responseJson.displayText = responseToUser;
            // Dialogflowにレスポンス送信
            response.json(responseJson);
          } else {
            // もしレスポンスにリッチレスポンスや文章が含まれていた場合、それをDialogflowに送信
            const responseJson = {};
    
            responseJson.speech = responseToUser.speech || responseToUser.displayText;
            responseJson.displayText = responseToUser.displayText || responseToUser.speech;
            responseJson.data = responseToUser.richResponses;
            responseJson.contextOut = responseToUser.outputContexts;
    
            // Dialogflowへのレスポンス送信
            response.json(responseJson);
          }
        };
    
        // Intentごとの実行処理
        const actionHandlers = {
          // AddTask Intent発生時の処理
          'input.task': () => {
            // HTTPリクエストヘッダー
            const headers = {
              'Content-Type': 'application/json',
              'X-Cybozu-API-Token': token
            };
    
            // HTTPリクエストボディ
            const form = {
              app: appId,
              record: {
                To_Do: {
                  value: parameters.task
                },
                Duedate: {
                  value: parameters.date
                }
              }
            };
    
            const options = {
              url: 'https://' + subdomain + '.cybozu.com/k/v1/record.json',
              method: 'POST',
              headers: headers,
              json: form
            };
    
            // HTTPのPOSTリクエスト
            req(options, (error, resp, body) => {
              if (body) {
                const responseToUser = {
                  speech: '締切日' + parameters.date + 'でkintoneに' + parameters.task + 'を登録しました',
                  text: '締切日' + parameters.date + 'でkintoneに' + parameters.task + 'を登録しました'
                };
                sendResponse(responseToUser);
              } else if (error) {
                const responseToUser = {
                  speech: 'エラーが発生しました。コンソールをご確認ください',
                  text: 'エラーが発生しました。コンソールをご確認ください'
                };
                sendResponse(responseToUser);
              }
            });
          },
    
          // CheckTask Intent発生時の処理
          'check.task': () => {
            // HTTPリクエストヘッダー
            const headers = {
              'Content-Type': 'application/json',
              'X-Cybozu-API-Token': token
            };
    
            // HTTPリクエストボディ
            const form = {
              app: appId,
              query: 'Duedate = TODAY()',
              fields: ['To_Do']
            };
    
            const options = {
              url: 'https://' + subdomain + '.cybozu.com/k/v1/records.json',
              method: 'GET',
              headers: headers,
              json: form
            };
    
            // HTTPのGETリクエスト
            req(options, (error, resp, body) => {
              if (body) {
                const tasks = body.records.map((record) => {
                  return record.To_Do.value;
                });
                let responseToUser;
                if (tasks.length === 0) {
                  responseToUser = {
                    speech: '今日締切のタスクはありません',
                    text: '今日締切のタスクはありません'
                  };
                } else if (tasks.length > 0) {
                  responseToUser = {
                    speech: '今日締切のタスクは' + tasks + 'です',
                    text: '今日締切のタスクは' + tasks + 'です'
                  };
                }
                sendResponse(responseToUser);
              } else if (error) {
                const responseToUser = {
                  speech: 'エラーが発生しました。コンソールをご確認ください',
                  text: 'エラーが発生しました。コンソールをご確認ください'
                };
                sendResponse(responseToUser);
              }
            });
          },
    
          // 上記以外のIntent発生時の処理
          default: () => {
            // Googleリクエスト応答用にActions on Googleライブラリを利用
            if (requestSource === googleAssistantRequest) {
              const responseToUser = {
                speech: 'これはDialogflowからのメッセージです',
                text: 'これはDialogflowからのメッセージです'
              };
              sendGoogleResponse(responseToUser);
            } else {
              const responseToUser = {
                speech: 'これはDialogflowからのメッセージです',
                text: 'これはDialogflowからのメッセージです'
              };
              sendResponse(responseToUser);
            }
          }
        };
    
        if (!actionHandlers[action]) {
          action = 'default';
        }
        actionHandlers[action]();
      };
    
      // 実行関数
      exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
        // デバッグ用コード
        console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
        console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
    
        if (request.body.result) {
          addTask(request, response);
        } else {
          return response.status(400).end('不正なWebhookのリクエストです。v1かv2のWebhookリクエストを利用してください。');
        }
      });
    })();
  12. Inline Editorの「package.json」に以下を記述します。
    デフォルトのpackage.jsonにHTTP Requestで利用するrequestモジュールを追記しています。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    {
      "name": "dialogflowFirebaseFulfillment",
      "description": "This is the default fulfillment for a Dialogflow agents using Cloud Functions for Firebase",
      "version": "0.0.1",
      "private": true,
      "license": "Apache Version 2.0",
      "author": "Google Inc.",
      "engines": {
        "node": "~6.0"
      },
      "scripts": {
        "start": "firebase serve --only functions:dialogflowFirebaseFulfillment",
        "deploy": "firebase deploy --only functions:dialogflowFirebaseFulfillment"
      },
      "dependencies": {
        "actions-on-google": "^1.5.x",
        "firebase-admin": "^4.2.1",
        "firebase-functions": "^0.5.7",
        "apiai": "^4.0.3",
        "request": "2.83.0"
      }
    }
  13. 「DEPLOY」ボタンをクリックします。
    下図のとおり、「View execution logs in the Firebase console」とログが出力されれば成功です。

  14. 実際にHTTP Requestが成功するか「Test Console」で会話をして試してみてください。
    kintoneアプリ側にレコードが登録されたらOKです。

  15. また「Inline Editor」下の「View execution logs in the Firebase console」のリンクをクリックしてください。
    Firebaseの画面に遷移してログを見ることができるようになっているので、そちらも確認しておきましょう。
    デバッグする時に活用できます。

  16. ひとまずこれにて設定完了です。

動作確認

あとはActions on Googleの画面に戻って音声でのテストをしてみましょう!
実際にGoogle Homeを使ってもOKです。

  1. 作成したプロジェクトのOverview画面から「TEST DRAFT」をクリックして、Simulatorを立ち上げます。

  2. Surfaceを「Speaker」に、Languageを「Japanese - ja」に設定して、左側の入力ダイアログにてテストを行います。
    テスト用のSimulatorなので、会話のきっかけとして設定した「こんにちは」の代わりに、合言葉として必ず「テスト用アプリにつないで」という呼びかけが必要になります。
    上記画像の「Overview > App Information > Edit > Detail > Sample invocations」箇所を設定することで「テスト用アプリにつないで」以外のきっかけで動作させることもできますが、ここでは割愛します。
    Google Homeの実機でテストする場合は、「OK, googleテスト用アプリにつないで」と呼びかけてください。

  3. 会話をすると以下のようになります。
    Google Homeでも試してみてください。

  4. 念のためkintoneにも無事レコードが登録されていることを確認します。

実際に運用するアプリケーションとして公開する場合は、Actions on Google画面( 本Tipsの動作確認 > 手順1の箇所 )で追加情報を記載して、審査を通す必要があります。
詳細については Prepare to release your Action (External link) を参照してください。

サンプルコードの解説

途中で出てきたNode.jsの実装ポイントを少し解説します。
今回はDialogflow V1 APIを利用しています。

大まかな流れだけ抜き出すと以下のようになります。

  1. ライブラリやモジュールのインストール
  2. addTask関数を定義して、その中でレスポンスの形式を定義 & kintoneへのHTTP Requestを送信
  3. 実行関数を定義
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
(() => {
  'use strict';

  // ライブラリやモジュールのインストール
  const functions = require('firebase-functions');
  const DialogflowApp = require('actions-on-google').DialogflowApp;
  const googleAssistantRequest = 'google';
  const req = require('request');

  // /// 中略 /////

  // kintoneへのタスク登録 & 確認関数
  const addTask = (request, response) => {

    let action = request.body.result.action;
    const parameters = request.body.result.parameters;
    const requestSource = (request.body.originalRequest) ? request.body.originalRequest.source : undefined;
    const app = new DialogflowApp({request: request, response: response});

    // Dialogflow送信用のGoogle Assistant Response作成関数
    const sendGoogleResponse = (responseToUser) => {
      // /// 中略 /////
    };

    // Dialogflow送信用のResponse作成関数
    const sendResponse = (responseToUser) => {
      // /// 中略 /////
    };

    // Intentごとの実行処理
    const actionHandlers = {
      // AddTask Intent発生時の処理
      'input.task': () => {
        // /// 中略 /////

        // HTTPのPOSTリクエスト
        req(options, (error, resp, body) => {
          if (body) {
            const responseToUser = {
              speech: '締切日' + parameters.date + 'でkintoneに' + parameters.task + 'を登録しました',
              text: '締切日' + parameters.date + 'でkintoneに' + parameters.task + 'を登録しました'
            };
            sendResponse(responseToUser);
          } else if (error) {
            const responseToUser = {
              speech: 'エラーが発生しました。コンソールをご確認ください',
              text: 'エラーが発生しました。コンソールをご確認ください'
            };
            sendResponse(responseToUser);
          }
        });
      },

      // CheckTask Intent発生時の処理
      'check.task': () => {
        // /// 中略 /////

        // HTTPのGETリクエスト
        req(options, (error, resp, body) => {
          if (body) {
            const tasks = body.records.map((record) => {
              return record.To_Do.value;
            });
            let responseToUser;
            if (tasks.length === 0) {
              responseToUser = {
                speech: '今日締切のタスクはありません',
                text: '今日締切のタスクはありません'
              };
            } else if (tasks.length > 0) {
              responseToUser = {
                speech: '今日締切のタスクは' + tasks + 'です',
                text: '今日締切のタスクは' + tasks + 'です'
              };
            }
            sendResponse(responseToUser);
          } else if (error) {
            const responseToUser = {
              speech: 'エラーが発生しました。コンソールをご確認ください',
              text: 'エラーが発生しました。コンソールをご確認ください'
            };
            sendResponse(responseToUser);
          }
        });
      },

      // 上記以外のIntent発生時の処理
      default: () => {
        // /// 中略 /////
      }
    };

    if (!actionHandlers[action]) {
      action = 'default';
    }
    actionHandlers[action]();
  };

  // 実行関数
  exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
    // /// 中略 /////
    if (request.body.result) {
      addTask(request, response);
    } else {
      return response.status(400).end('不正なWebhookのリクエストです。v1かv2のWebhookリクエストを利用してください。');
    }
  });
})();

その他注意ポイントは以下です。

  • 実行関数名は、必ず「dialogflowFirebaseFulfillment」にする。
  • Google Assistantからのレスポンスにはさまざまな形式があるので、それをうまく変換する関数を用意する。
  • actionHandlers内で、作成したIntentごとの処理を記載する。
  • 指定したIntent以外が発生した場合は、default Intentに誘導する。

注意事項

  • Dialogflow V2 APIも利用可能ですが、本TipsではV1 APIを利用しているので、API VERSIONを変更すると動作しない場合があります。
  • Firebaseはβ版なので、ご利用の際にはご注意ください。
  • 今回のGoogle Home連携はDialogflowの「Test console」とActions on Googleの「Simulator」でのみ利用できます。
    アプリを公開する際は、別途手続きが必要ですので、ドキュメントを確認してください。
  • 英語のみの対応でよい場合は、Dialogflowではなく、Converse AIを選択すると、GUIのみで簡単に設定できます。
    HTTP Request部分もほとんどコードを書かずに済みます
  • kintoneのBasic認証も設定している場合は、認証情報を追記してください。

おわりに

無事Google Homeと会話をしてタスクの登録 & 確認ができましたでしょうか?
今回はシンプルな構成でしたが、工夫の幅は無限大です。
ぜひスマートスピーカーのメリットを活かした使い方を考えて、開発してみてください。
設定項目も多いので、最初は扱うのがたいへんですが、慣れれば楽しく連携できます!

information

このTipsは、2018年1月版kintoneで動作を確認しています。