Google Home向けアプリ開発チュートリアルーFulfillmentを使った実装(Inline Editor編)

Mika
12 min readDec 19, 2018

Google Home向けアプリ開発チュートリアル(1)〜(4)ではおやつをオススメするアクションを作ってきました。

Google Home向けアプリ開発チュートリアル(1)ープロジェクトの作成
Google Home向けアプリ開発チュートリアル(2)ーDialogFlowを使った会話部分の実装(カスタムインテントの作成)・シュミレーターによるテスト
Google Home向けアプリ開発チュートリアル(3)ーDialogFlowを使った会話部分の実装(会話を続ける・アクションを終了する)
Google Home向けアプリ開発チュートリアル(4)ーDialogFlowを使った会話部分の実装(ヘルプコマンド・終了コマンドの実装)

(4)の完成時点ではGetSnacksインテントのレスポンスにオススメのおやつを数個入れて、そこからランダムで返していますよね。

今回の記事ではFulfillmentを使いアクションをもう少し複雑にして、おやつを時間帯に応じてオススメするようにしていきます。

時間帯に応じてオススメのおやつを返すには、今の時間を取得しないといけないですよね。

このような少し複雑なレスポンスをしたい時に便利なのがInline Editorです。Inline Editorには簡易的なNode.jsの処理を記載できます。

早速実装していきましょう!

Fulfillmentを作成

左メニュー→IntentsGetSnacksとクリックし、GetSnacksインテントを開きます。

Fulfillmentの”Enable webhook call for this intent” をオンにしSAVEボタンをクリックして保存します。

これでGetSnacksインテントが呼ばれた時は、Responsesの中からランダムに返すのではなく、Fulfillmentが呼ばれるようになりました

FulfillmentセクションのInline Editorに処理を書いていきます。

左メニューのFulfillmentをクリックしFulfillmentを表示します。Inline Editor編集をしますので、Inline EditorをENABLEDの状態にします。

Fulfillment

Inline Editorにあるコードを一旦全て消し、以下のコードをコピー&ペーストしてください。

以下のコードではオススメのおやつの配列を朝・昼・夕方の時間ごとに作り、ユーザーがアプリを立ち上げた時間に応じて対応する配列の中からランダムにおやつを取り出しています。

'use strict';

const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');

process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
//MorningSnacks(朝にオススメのおやつのデータの配列を作成)
const morningSnacks = [
'バナナです。バナナはビタミンやミネラル、食物繊維など、栄養満点。糖質もバランスよく含まれているので、朝のエネルギーチャージに最適ですよ。',
'ミックスナッツです。良質な脂肪分を含み、健康や美容に良いです。ランチの前に食べると、満腹中枢が刺激されて食べ過ぎを防げますよ。少量をよく噛んで食べてくださいね。',
'ヨーグルトです。ヨーグルトにはカルシウムやビタミンが豊富に含まれています。乳酸菌の働きで便秘の改善や美肌への効果も期待できますよ。',
'グラノーラです。不足しがちなビタミンやミネラルが豊富に含まれており、手軽に栄養補給ができます。そのまま食べても、牛乳をかけたり、ヨーグルトにかけて食べても美味しいですよ。',
'カットフルーツです。フルーツには、ビタミンCが豊富に含まれていて体にとてもいいです。カットフルーツなら忙しい朝でも取り入れやすいですよね。',
];
//NoonSnacks(昼にオススメのおやつのデータの配列を作成)
const noonSnacks = [
'プリンです。プリンは牛乳や卵で作られているので、意外と栄養バランスがいいんですよ。ランチ後のちょっとしたデザートにもオススメです。',
'スイートポテトです。和風のスイーツが食べたい時にオススメです。カロリーは高めですが、食物繊維が豊富に含まれており、腹持ちが抜群です。',
'ドライフルーツです。ドライフルーツは生のフルーツより栄養素が高くなっています。よく噛んで食べましょう。',
'フェットチーネグミです。独特の噛み心地。一度食べたらハマってしまうかもしれません。',
'高カカオチョコレートです。カカオ含有率70%以上のダークチョコレートがオススメ。ダークチョコレートに多く含まれるカカオポリフェノールにはアンチエイジング効果があります。',
];
//EveningSnacks(夕方にオススメのおやつのデータの配列を作成)
const eveningSnacks = [
'シュークリームです。卵や牛乳を使っているので、腹持ちがよく夕方に食べるおやつにオススメです。',
'アイスクリームです。夕方はおやつを食べても太りにくい時間帯。アイスクリームは夕方のおやつタイムに食べましょう!',
'肉まんです。小腹がすいた時に。片手で手軽に食べられますよ。帰りが遅くなる日のエネルギー補充にもオススメです。',
'ブラウニーです。結構お腹にたまりますよ。カロリーが気になる方は糖質オフのものを。',
'カントリーマアムです。電子レンジで20秒くらい、トーストでは1、2分焼いて食べても美味しいですよ。',
];
//NightSnacks(夜にオススメのおやつのデータの配列を作成)
const nightSnacks = [
'野菜チップスです。いつもポテトチップスを食べている方もたまには野菜にしてみるのはいかがですか?野菜チップスには食物繊維がたくさん含まれていますよ。',
'ゆで卵です。ダイエット中のおやつに。ゆで卵は、糖質がほとんどなく、満足感を得やすいです。',
'おしゃぶり昆布です。噛み応えがあるので、満腹中枢が刺激され満腹感を得やすいです。ダイエット中のおやつにぜひ。',
'カップスープです。夜中にどうしてもお腹がすいた時は、汁物で空腹感を紛らわすのがオススメです。',
'サラダチキンです。高タンパク、低カロリー、低糖質です。ダイエット中の夜食に。',
];

exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
console.log('Dialogflow Request body: ' + JSON.stringify(request.body));

function welcome(agent) {
agent.add(`Welcome to my agent!`);
}

function fallback(agent) {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
function getSnacks(agent) {
//現在時刻を取得
var currentTime = new Date();
//時間だけ取得
var currentHour = currentTime.getHours();
//UTCで返ってくるので日本時間になるように+9時間する
currentHour = currentHour + 9;
//おやつを定義しておく
var snack;
if (currentHour >= 5 && currentHour < 11) {
//朝(現在の時間が5時〜10時59分の間の場合)
//Morning Snacksの配列からランダムにおやつを一つ取得する
snack = morningSnacks[Math.floor(Math.random()*morningSnacks.length)];

} else if (currentHour >= 11 && currentHour < 15) {
//昼(現在の時間が11時〜14時59分の間の場合)
//Noon Snacksの配列からランダムにおやつを一つ取得する
snack = noonSnacks[Math.floor(Math.random()*noonSnacks.length)];

} else if (currentHour >= 15 && currentHour < 20) {
//夕方(現在の時間が15時〜20時59分の間の場合)
//Evening Snacksの配列からランダムにおやつを一つ取得する
snack = eveningSnacks[Math.floor(Math.random()*eveningSnacks.length)];

} else{
//夜(上記の時間帯に当てはまっていない)
greetings = 'こんばんは。';
//Night Snacksの配列からランダムにおやつを一つ取得する
snack = nightSnacks[Math.floor(Math.random()*nightSnacks.length)];
}
//挨拶とおやつを組み合わせた文を返す
agent.add('オススメのおやつは' + snack);
}

// Run the proper function handler based on the matched Dialogflow intent name
let intentMap = new Map();
intentMap.set('Default Welcome Intent', welcome);
intentMap.set('Default Fallback Intent', fallback);
intentMap.set('GetSnacks', getSnacks);
// intentMap.set('your intent name here', yourFunctionHandler);
// intentMap.set('your intent name here', googleAssistantHandler);
agent.handleRequest(intentMap);
});

ディプロイボタンをクリックしディプロイします。

Deploy fulfillment

ディプロイには数十秒程かかりますが、ディプロイが終了すると先ほどのディプロイボタンの表示が「DEPLOYING…」から「DEPLOYED」に変わります。

ディプロイが完了したら、Actions on Googleのシミュレーターを使ってテストしてみましょう!

画面右側の「See how it works in Google Assistant」のリンクをクリックします。

シミュレーターでテスト

Actions on Googleのシミュレーターでアプリをテストしましょう。

Simulator

テスト用アプリにつないで」と入力するとアプリのテストをスタートすることができます。「オススメのおやつを教えて」と入力し、GetSnacksインテントが呼ばれるかテストしてみましょう。

Test GetSnacks

ちゃんと動きましたね!

このようにFulfillmentのInline Editorを使うと、少し複雑な処理が可能になります。

インテントの中にも、Responsesだけで処理を完結できるようなものもあれば、Inline Editorを使った方がアクションの幅が広がるものもあります。

例えばアクションの使い方を案内するだけのようなインテントであれば、Responsesに返答を入れておけば十分なので、わざわざInline Editorを使う必要はないでしょう
このGetSnacksインテントのように少し複雑な処置をしたければInline Editorを使うのが便利ですよね。

以上、Inline Editorの実装方法でした。

--

--

Mika

Software engineer at Goldrush Computing Inc. 🍦