#前置き
※長くなってしまったので、さっさとタイトルの内容について知りたい方は飛ばしてください🙇
Google Homeが日本に上陸して早2ヶ月…。
みなさん、バリバリ活用してますか!?!?!?
私はGoolgePlayMusicで音楽を流したり、Chromecastと連携してYouTubeで動画を観たりしています。
やはり声だけで操作できるというのは面白いし便利ですよね。
よくわかりませんでした、を連発されてもどかしい時もありますが…。
私も一応エンジニアなので、Google Homeを使って何かアプリを作りたいな〜と漠然と考えていました。
そんななか、Todoistのタスクを整理していた時に思ったのです。
声だけでタスクを追加できれば捗るのでは?、と。
タスクの追加はとにかく早さが大事(あとでやろうと思っても忘れる)なので、
たとえ両手が塞がっていてもタスクの追加ができるのは大きなメリットだと考えました。
ちなみに、単純なタスクの追加であれば、IFTTTと連携させることで実現が可能ですが、以下のようなデメリットがあります。
- 細かいカスタマイズができない。
- 特に日付を設定できないのが辛い。
- Google Home(Google Assistant)が認識した単語ごとに半角スペースが入ってしまう。
- Google HomeとIFTTTを連携させるのは、他の方が書かれていますので割愛します。
#作ってみる
そんなわけで、TodoistのAPIを叩いてタスクを追加するアプリを作っていきたいと思います。
##プロジェクトを作成する
Actions on Googleにアクセスして、Add/import project
をクリックすると以下のダイアログが表示されます。
プロジェクト名と国を入力して、CREATE PROJECT
をクリックして、プロジェクトを作成します。
今回は、プロジェクト名を「Verbalist」、国を「Japan」としました。
プロジェクトの作成に成功すると、作成したプロジェクトの画面に遷移します。
##Dialogflowと連携させる
プロジェクトの画面のDialogflowのカードのBUILD
をクリックすると、以下のダイアログが表示されます。
このダイアログに記載されている手順に従って進めていきます。
###Dialogflowのエージェントを作成する
先程のダイアログのCREATE ACTIONS ON DIALOGFLOW
をクリックすると、以下のようなDialogflowの画面に遷移します。
利用規約を確認して、問題なければACCEPT
をクリックします。
ダイアログが消えますので、言語が「Japanese」、タイムゾーンが「Asia/Tokyo」であることを確認してCREATE
をクリックします。
これにてエージェントの作成は完了です。
###Fulfillment
の有効化
左にあるサイドメニューのFulfillment
をクリックして、Fulfillment
の設定を行います。
スイッチをクリックしてENABLED
にして、テキトーなURLを入力します。(後ほど変更します。)
設定が完了したらSAVE
ボタンをクリックして、Fulfillment
を有効にします。
##会話を定義する
左にあるサイドメニューのIntents
から、Intentを追加して会話を定義していきます。
###起動時のIntent(Default Welcome Intent
)
必須ではありませんが、デバッグする時のために、User says
に起動のトリガーとなる言葉を入力しておきます。
今回は、「起動」と入力することでアプリが立ち上がるようにしました。
次にResponse
セクションにあるText response
カードを、ゴミ箱アイコンをクリックして削除します。
その後、ADD MESSAGE CONTENT
>Text response
とクリックして、新しくText response
カードを追加します。
テキストボックスに喋らせたい内容を入力して、SAVE
をクリックしたら編集完了です。
###タスクについてやりとりするIntent
+
をクリックして新しいIntentを追加します。
Intent name
は「add_task」にしました。
まず、ユーザーが「○○(タスク名)を追加」と言ったら、○○にあたる内容をタスクと見做すようにします。
先程定義したtask
パラメータについて以下のように定義します。
- Required - チェックをつける
- チェックをつけると、ユーザーが、このアクションを満たす適切なフレーズを言うまで、トリガーがかかりません。
- Parameter name - task
- Entity - @sys.any
- Dialogflowのドキュメントが詳しいです。
- Value - $task
- Prompts - タスクを教えてください
- Dialogflowが発話を検知するまで、このフレーズがユーザーに対して繰り返し発話されます。
また、日付のパラメータをdate
として以下のように定義します。
- Required - チェックをつける
- Parameter name - date
- Entity - @sys.date-time
- Value - $date
- Prompts - 予定日時を教えてください
このように追加の入力(date
)をRequired
として定義することで、新しくIntentを作らなくても、続けて質問をすることができます。
最後に、Fulfillment
セクションのUse webhook
と、Actions on Google
セクションのEnd conversation
にチェックを入れて完了です。
##Fulfillmentを構築する
今回はNode.jsで作ったアプリをHerokuでデプロイしました。
それぞれの使い方はこちらの記事に丁寧に書かれていますので、詳しくはそちらをご覧ください。
こちらのサンプルも参考にしながら以下のように書きました。
// モジュールのインポート
var express = require('express');
var bodyParser = require('body-parser');
var request = require('request');
// 開発で使うポート番号
const LOCAL_PORT_NUMBER = 5000;
// ----初期化と設定----
var app = express();
app.set('port', (process.env.PORT || LOCAL_PORT_NUMBER));
app.set('x-powered-by', false);
app.set('case sensitive routing', true);
app.set('strict routing', true);
app.use(bodyParser.json());
// VerbalistのFulfillment
app.post('/google_home/verbalist', function (req, res, next) {
console.log('=====[REQUEST]====');
console.log(req.body);
// リクエストに必要なパラメータが含まれていない場合は、以降の処理を中止する
if (!req.body || !req.body.result || !req.body.result.parameters) {
return res.status(400).send('No parameters.');
}
// タスクの内容を取得する
var task = req.body.result.parameters.task;
console.log('=====[TASK]====');
console.log(task);
// 単語が半角スペースで区切られているのでそれを削除
var trimmedTask = task.replace(/ /g, '');
// 予定日時を取得する(JSTをUTCに変換する)
var jstDateTime = Date.parse(req.body.result.parameters.date);
var utcDate = new Date(jstDateTime - 1000 * 60 * 60 * 9);
console.log(utcDate);
var options = {
// MEMO: TOKENは適当な文字列を入れてください
uri: 'https://beta.todoist.com/API/v8/tasks?token=' + TOKEN,
headers: {
'Content-type': 'application/json',
},
json: {
'content': trimmedTask,
'due_datetime': utcDate
}
};
// POSTする
request.post(options, function (error, response, body) {
// 返答内容
var speech;
// (ディスプレイがあれば)ディスプレイに表示する内容
var displayText;
if (error) {
console.log('=====[ERROR]====');
console.log(error);
speech = 'タスクの追加に失敗しました。';
displayText = 'タスクの追加に失敗しました。';
} else {
console.log('=====[BODY]====');
console.log(body);
speech = 'タスク、' + body.content + '、を追加しました。';
displayText = 'タスク名:' + body.content + '\n' + '予定日時:' + body.due.datetime;
}
// レスポンスを送る
res.json({
source: 'add_task',
speech: speech,
displayText: displayText
});
});
});
// 第一引数にポート番号、第二引数にコールバック関数を指定して、サーバを起動
var server = app.listen(app.get('port'), function () {
console.log('http server is running...');
// すでに終了しているかどうかのフラグ
var isFinished = false;
process.on('SIGTERM', () => {
// すでに終了している場合は何もしない
if (isFinished) {
return;
}
isFinished = true;
console.log('http server is closing...');
// サーバを停止する
server.close(function () {
console.log('http server closed.');
// 正常終了
process.exit(0);
});
});
});
// サーバのエラーを監視する
server.on('error', function (err) {
console.error(err);
// 異常終了
process.exit(1);
});
ローカルで動作を確認したいところですが、localhostはFulfillmentには指定できないので、ngrokを使います。
表示されたURLをFulfillmentに設定して、右側のエリアでテストします。
無事レスポンスが返ってきました!🎉
##Integrations
の設定
左にあるサイドメニューのIntegrations
をクリックして、Google Assistantをクリックします。
以下のダイアログが表示されるので、TEST
をクリックします。
このような表示になれば、Google Homeから呼び出すことが可能です!
Google Homeに「テスト用アプリにつないで」と話すと、アプリが立ち上がります。
#最後に
今回はホントにシンプルなタスク追加でしたが、まだまだカスタマイズする余地はあると思っています。
タスク追加をさらに掘り下げることもできますし、追加以外の機能、例えばタスクの確認などもできます。
Todoistに限らず、音声操作を最大限活かせるようなシチュエーションで使えるアプリを作れたらいいですね!
#参考リンク
Actions on Googleでapi.aiを使ってGoogle Homeに何か言わせてみる
Build Your First App with Dialogflow
Actions on Google と AWS Lambda で Google Home から Slack にポストする
Getting Started on Heroku with Node.js
supermamon/apiai-nodejs-webhook-sample