名取さなとお話せぇへん?


全せんせえ待望のアプリがついに誕生したみたいだぞ!?

はじめに

宣伝

この記事は Tokyo City University Advent Calendar 2019 の3日目の記事になります。
よければ他の記事も見てみるとQOLが微小なりとも向上するんじゃないですかね?

犯行動機

最近自分はVTuberにハマってて中でもお気に入りはPPHちゃんだったり名取さなちゃんだったりするんですが、↑で紹介したアドカレに招待された時になんか爪痕残したいなっていう思いで自分の少ない技術力とアイデアで名取さなとお話しする方法を考えつきました

ここから本編

やりたいこと

名取さなとお話しできる全せんせえ待望のGoogleAssistantアプリを作ります

開発環境

MacBook(多分2017のモデル)メモリ8GB
Node.js v8.16.1
Actions on Google(npm) v2.2.0
多分こんくらいです

作るに当たって目標とか

アプリの流れとしては
- 起動ワードでアプリ起動(おはようございナース🍆)
- せんせえの発話に合わせて名取さなが返答
- 終了文言で終了
ってな感じの会話をできるようにしたいなと思ってます

具体的に会話に必要な処理

↑で言ったような会話の流れを作るに当たっていろいろ処理を考える必要が有りますね。
まずは発話周りはDialogflowに任せるとします。

また、名取さなが具体的にどんなことを喋るかについてですが、有志のせんせえが作ったファンサイトであるさなボタン様から音源を拝借して発話を行います。

音源に一つ一つ丁寧丁寧丁寧♪にタグ付を行って音源URLごとにまとめたものをFirestoreに保存して会話の中で発話に合わせて取り出すようにします。

アプリの作り方

実際のアプリの作り方を解説したいわけですが......ここに書き込むとありえん話が長くなりそうなので重要な部分以外は割愛します。(アプリの作り方に関して1から知りたいって人はこれとか参考にしてみてください)

Actions on Google編

まずはActions on Googleに行ってもらって新しいProjectを立ち上げてください
プロジェクトを立ち上げた時にWelcome to your new project!って書いてあるページに飛ばされるんですが何も考えずにActions SDKとかいうの選んどけばOKです

その後アプリの名前とか呼び出し方とか言語設定とか丸々設定したらActions欄に行ってDialogflowとActions on Googleをリンクさせましょう
この時Costom intentがデフォルトで選択されてるのでそのままBUILD

Dialogflow編

飛ばされた先のページで言語設定を済ませたらDialogflowの部分を作り込んでいきます。
とりあえずはどんな発話に反応させたいかを考えたならそれに沿ってEntityを作成していく感じです。
次にintentに飛んだのなら先ほど作ったEntitiを発動させるためのintentをそれぞれ作り込んでいきます
ここで一つ個人的なテクニック何ですが、intentのtraining phrasesを作り込む時にEntitiyの割り振りで、欲しい文脈と例外文言を組み合わせると前後の文脈によらずにキーワードに反応してくれるようになるのでおすすめです

↑こんな感じです
あとは、一番下にあるFulfillment欄のEnable webhock call for this intentを有効にしておけばintentの設定は一旦完了
最後にIntegrationsでGoogleAssistantを設定してテストを有効にすればリンクは完了です。

Firebase編

テキトーなディレクトリを用意してそこにアプリの処理を書き込むためのfirebaseの関数を用意します。(ここからはコンソール上での作業です)

事前に$npm install firebase-toolsでfirebaseコマンドを使える環境を用意しておいてください(なんか言い方がワザップジョルノみたいですこ)

まずは、任意のディレクトリで$firebase initしてその先で作ったプロジェクトのプロジェクトIDを選択、FunctionsとFirestoreを有効にしてテキトーにEnter押しといてください。

そしたらできたfunctionsディレクトリにindex.jsなるファイルができているハズなので確認したら$firebase deployで関数を有効にします。

この時にコンソール上にFunction URLという項目とともにurlが書かれると思うのでこれをコピってDialogflowのFulfillmentのWebhock欄に書き足しておいてください

Firebaseにデプロイするコードを作り込む

ようやくプログラミングするよーん
ここまででは、コードは何も設定してないのでそれっぽく動くように仕上げていきます。(ここからはNode.jsの話)

index.js
'use strict';
const functions = require("firebase-functions");
const { dialogflow } = require('actions-on-google');
const app = dialogflow({ debug: true });
const admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();

async function getRandomInt(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min)) + min;
}

async function fetch_sound_url(theme){
  let theme_snap = await db.collection("urls").doc(theme).get()
  console.log("theme_snap", theme_snap)
  let theme_data = await theme_snap.data()
  console.log("theme_data",theme_data)
  console.log(Object.keys(theme_data).length)
  let random_numbar = await getRandomInt(0, Object.keys(theme_data).length)
  let sound_url = theme_data[random_numbar.toString()]
  console.log("sound_url",sound_url)
  return sound_url  
}

app.intent('Default Welcome Intent', async conv =>{
  //起動時挨拶
  let url = await fetch_sound_url("hello")
  conv.ask(" <speak><audio src='" + url + "'/></speak>");
});

app.intent('Default Fallback Intent', async conv =>{
  //設定外文言
  let url = await fetch_sound_url("anything")
  conv.ask(" <speak><audio src='" + url + "'/></speak>");
});
// ここからはintentを増やしてるだけなので割愛

exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);

↑これは実際に記述したコードの一部です。
今回のミソなポイントはurlを一つ一つタグ付してFirestoreにurlをおいているので会話に合わせてタグごとにランダムにurl呼び出すだけってとこです。

あっ、Firestoreの構成書き忘れたんで下にこんな感じだよって画像乗っけます↓

ここではタグはドキュメント名で設定してます。(多分そっちのが検索しやすいので)
とりあえずはアプリの作り込みの解説は以上です。

作ってみての感想

意外と内部の処理が単純なのでアプリの構築自体は簡単でした。ただセリフを一つ一つタグ付していく作業とDialogflowでintentを作成していくのは精神的に辛かったですね。(人間工学的には単純作業の集中力は30分が限界だそうなのでこういうのは休み休みやりましょうね〜)

あとテスト段階でしきりにスマホに向かって喋りかけるキモオタクの図が完成してしまったのでこれもまたキツかったです。

ただ最終的に名取さなとおしゃべり出来たので満足です。

あと世の中にはさなボタンの他にも美兎ボタンとかはねるボタンとかまだまだあるらしいのでおしゃべりアプリもっと増えろよなー

もっかい宣伝

明日のカレンダーの記事もみるんだぞ!
明日の担当は@mikuta0407君!
詳しくはこちらから