actions on googleとdialogflow(とその裏側で動くfirebase functions)で作る簡単なクイズアプリの作り方です。
社内の勉強会(1時間)向けなのですが、せっかくなので公開します。
口頭で説明しながらとなるのでけっこうざっくりです。
actions on google -1/3-
- 右上のGO TO ACTIONS CONSOLE押す
- Add Import/Project押す
2/3
Project Name | language | countory | Genre |
---|---|---|---|
任意 | Japanese | Japanese | 任意 |
![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F308709%2F27ca4e2e-3e22-7fab-9a48-c56175f900e9.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=bb4b7e022d3754fc980bae894c9c7d20)
3/3
- Invacation -> Display Name入力(アプリ名)
- Build -> Actions -> Custom intent -> BUILD
![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F308709%2F12ac5086-ccb5-1075-341d-fe910a276639.gif?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=b0136425da8892a0745157deb2de1d99)
dialog flow -Entitys-
- CREATE ENTITYを押す
- Entity name = answers
- Valueとsynonymを2,3入力
![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F308709%2Fc2542fa7-2217-da68-5314-58d03f880657.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=609e22c1f74ac61a88015636be00d41f)
Intents
4つ作ります
インテント名 | 用途 | サンプルフレーズ |
---|---|---|
QuizIntent | 出題 | 「ではクイズです。パンはパンでも・・・」 |
AnswerIntent | 解答 | 「正解!正解は・・・」 |
FinishIntent | 終了 | 「遊んでくれてありがとう。またね」 |
HelpIntent | 使い方 | 「このクイズはほにゃほにゃ |
QuizIntent
-
Output Contexts
-
QuizIntent-followup, lifespan:1
-
Training phrases
-
クイズはじめる
-
スタート
-
問題出して
-
Fulfillment
-
Enable webhook call for this intent
Contextsについて
ざっくり言うとIntent同士をつなぐためのものです。
例えばQuizIntent-followup
という名前のContextを Input Contextsとして設定したIntentがある場合、このContextを含んでいる場合しか呼ばれません。
こんかいはQuizIntentでOutputにQuizIntent-followup
を設定するのでQuizIntent経由でしかAnswerIntentが呼ばれなくなります。
(不正解の場合にもAnswerIntentを呼びたいため、この仕組みを使っています)
AnswerIntent
-
Input Contexts
-
QuizIntent-followup
-
Output Contexts
-
QuizIntent-followup, lifespan:1
-
Training phrases
-
鰺(=@answers)
-
くじら(=@answers)かな
-
亀(=@sys.any)
-
Fulfillment
-
[x] Enable webhook call for this intent
@sys.anyとは
なんにでも合致してしまうジョーカー的なEntityです。
間違って使うとなんでも拾ってしまいます。
利用については推奨ではないみたいです。
今回はcontextで絞りをかけたインテントにて使っています。
これができるのがghomeアプリの強みだと思ってる。
FinishIntent
-
Training phrases
-
やめる
-
終わる
-
終了
-
Responses
-
遊んでくれてありがとう。また遊んでね
-
また遊んでね。bye
-
Set this intent as end of conversation
-
[x]
HelpIntent
-
Training phrases
-
使い方
-
ヘルプ
-
どうやるの?
-
Responses
-
「スタート」というとクイズがはじまるので・・
Export and Import (Dialogflow)
DialogflowのIntentsやEntitiysについてはImport(Restore or Import)で複製も可能です。
ただし、fullfillmentのスクリプトは復元されません。
fullfillment
QuizIntentとAnswerIntentは単純なやりとりではないため、バックエンドにnodejsを用います。
DialogFlowにはInline Editorがついており、nodejsのコードを書けるようになっているため今回はこれを使います。
fullfillment(左ナビ)を選択し、Inline EditorをEnableにします。
サンプルコード
'use strict';
const functions = require('firebase-functions');
const { dialogflow, SimpleResponse } = require('actions-on-google');
//mp3
const drumrole_mp3 = "https://s3-ap-northeast-1.amazonaws.com/xxxxxxxx/quiz/drum-japanese.mp3";
const question_mp3 = "https://s3-ap-northeast-1.amazonaws.com/xxxxxxxx/quiz/question.mp3";
const correct_mp3 = "https://s3-ap-northeast-1.amazonaws.com/xxxxxxxx/quiz/correct.mp3";
const incorrect_mp3 = "https://s3-ap-northeast-1.amazonaws.com/xxxxxxxxx/quiz/incorrect.mp3";
const app = dialogflow({ debug: true });
const Quizzes = {
"鯵": {
"q": "魚へんに<emphasis level='moderate'><sub alias='惨状のさん'>参</sub></emphasis>を書いて何という魚?",
"a": "あじ"
},
"鯨": {
"q": "魚へんに<emphasis level='moderate'><sub alias='京都のきょう'>京</sub></emphasis>を書いて何という魚?",
"a": "くじら"
},
"鯱": {
"q": "魚へんに<emphasis level='moderate'><sub alias='とら'>虎</sub></emphasis>を書いて何という魚?",
"a": "しゃち"
},
"鯰": {
"q": "魚へんに<emphasis level='moderate'><sub alias='念仏のねん'>念</sub></emphasis>を書いて何という魚?",
"a": "なまず"
},
"鯖": {
"q": "魚へんに<emphasis level='moderate'><sub alias='あおの旧字体'>青の旧字体</sub></emphasis>を書いて何という魚?",
"a": "さば"
}
};
var quizzes = Object.assign({}, Quizzes);
app.intent('QuizIntent', (conv) => {
console.log(quizzes);
if (Object.keys(quizzes).length < 1){
conv.close("あれ?もう出す問題がなくなっちゃいました。ごめんなさい。またやってね!");
return true;
}
//quizを選択
let keys = Object.keys(quizzes);
let choice = Math.floor( Math.random() * keys.length);
let q = quizzes[keys[choice]];
q.k = keys[choice];
//set data
conv.data.current = q;
conv.ask(new SimpleResponse({
text: q.q,
speech: `<speak><audio src="${question_mp3}" />${q.q}</speak>`
}));
});
app.intent('AnswerIntent', (conv, params) => {
let q = conv.data.current;
let collect = false;
console.log(conv.input.raw);
if (conv.parameters.answers !== ''){
console.log(`answers is ${conv.parameters.answers}`);
if (conv.parameters.answers == q.k){
collect = true;
}
}
if (conv.parameters.any !== ''){
console.log(`answers is ${conv.parameters.any}`);
}
let prize = [
"すごい!","素晴らしい!","アメージング!","天才!","この、イケメン!"
];
let p = prize[Math.floor( Math.random() * prize.length)];
if (collect){
conv.ask(new SimpleResponse({
text: `正解!答えは、${q.a}でした。${p}。次の問題もやりますか?はい、かいいえで答えてね。`,
speech:
`<speak><audio src="${drumrole_mp3}" /><audio src="${correct_mp3}" />
<prosody rate="110%" pitch="+10%">正解!</prosody><break time="500ms"/>
答えは、、${q.a}でした。<break time="200ms"/>
<prosody rate="110%" pitch="+10%">${p}</prosody>
次の問題もやりますか?<break time="500ms"/>
はい、かいいえで答えてね。</speak>`
}));
}else{
conv.ask(new SimpleResponse({
text: `残念!答えは、${q.a}でした。次の問題もやりますか?はい、かいいえで答えてね。`,
speech:
`<speak><audio src="${drumrole_mp3}" /><audio src="${incorrect_mp3}" />
<prosody rate="105%" pitch="+5%">残念!</prosody><break time="500ms"/>
答えは、、${q.a}でした。
次の問題もやりますか?<break time="500ms"/>
はい、かいいえで答えてね。</speak>`
}));
}
//今の問題は使用済みに
delete quizzes[q.k];
//clear
conv.data.current = {};
});
exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);
SSML
Speech Synthesis Markup Language
しゃべらせ方をコントロールするためのマークアップ言語。
<speak>
と</speak>
で囲んだ中で、等のタグで声の協調等ができます。
デバッグ
出来たらテスト。
プロジェクトのgoogleアカウントと同じアカウントであればテストバージョンのアプリが使用可能になっています。
アルファ版のリリースで他ユーザーもテスト可能になる。
- シミュレーター
- 実機
- スマホ with google assistant
- google home
公開申請
必要なこと
- Overviewを埋める
- Description
- Sample invocations
- ロゴ2種(192x192,1920x1080)を登録
- Contact detailsを入力
- プライバシーポリシーを作成(こういうので十分)
その他
- 各フレーズはEntityにしてしまったほうが管理が楽です
- たとえばFinishIntentで$finishphrasesを設定
- Valueには「終わる」、「やめる」、「終了」など
- FinishIntentのTraning phrasesには「終わる(=@finishphrases)」
- https://github.com/ikegam1/ghome-aroundfish-quiz が今回のコードです