なぜ作ったか
ハッカソンのサンプルに作りました。以下のストーリーは妄想です
今の世の中、安心して外を出歩くこともできず、Uber Eatsなどで出前を取る方も多かったのではないかと思います。
そして、テイクアウトのお店も増えてきました。スマートフォンがあれば、注文も調べることもできて、とても便利な世の中になりました。
しかし、スマートフォンを持っていないお年寄りはどうだったでしょうか?
調べることもできず、スーパーに買い出しをしにいかなければならなかったのではないでしょうか
こんなアプローチ
すごくシンプルに、電話かけて、市区町村と料理ジャンルをしゃべれば、テイクアウトできるオススメの1件の住所を教えてくれる。というもの。
準備物
-
ぐるなびのAPIキー
- こちらから申し込みできます
-
Twilioのアカウント
- こちらで作成できます
- 執筆時点(2020年5月22日)では、Twilioオンラインコンテストが行われており、初めてアカウントを作り、オンラインコンテストに参加する条件でポイントをもらうことができます
-
Google Cloud Platformのアカウント(手順は結構省きます)
- こちら にアクセスしてください
- プロジェクトを作成して、Cloud Natural Language APIを有効にする必要があります
- APIキーの生成を行なってください
全体の流れ
- 電話を着信します。
- 応答メッセージが流れ、市区町村、料理のジャンルを言います(今回は広島県限定)
- 音声認識した結果を、Googleの自然言語分析にかけて、市区町村、料理のジャンルの固有名詞を抜き出します
- ぐるなびのレストラン検索APIを利用して、対象のお店1件を取得します(テイクアウトでき、クレジットカードが使えるお店で検索します)
- 取得できたお店の名前を音声で読み上げます(一応、住所とTELも取得しています)
Twilioの基本的な使い方
- 電話番号の取得については、こちら をご確認ください
自然言語解析で、ぐるなびを検索するためのワードを抽出するためのファンクションを作成する
Google Cloud PlatformのNatural Languageを利用するにあたり、こちら を見ていただくとよいと思います。
Twilio「Function」を設定する
Twilioの左側のメニューに「RUNTIME」Functionsというメニューがあります。
Functionsを選択してください。

そうすると、Functionsの詳細メニューが開きます。ベータってなんだろ・・・

設定をクリックしてください
そうすると、以下の画面が表示されます。
まず、Enable ACCOUNT_SID and AUTH_TOKENのチェックをONにしてください

次に、Functionsで利用する定数を入力します。
ボタンを押すことにより、キーを追加できますので、以下を設定してください
- ぐるなびAPIキー
- Key
- GNAVI_API_KEY
- Value 値
- ぐるなびさんから付与されたAPIキーを入力
- Key
- ぐるなびAPIの都市コード(広島県決めうちしてます)
- Key
- GNAVI_PREF
- Value 値
- PREF34
- Key
- Google Cloud Platformのnatural-languageのAPIキー
- Key
- GOOGLE_API_KEY
- Value 値
- Googleで取得したAPIキー
- Key
次にFunctionsで利用する追加のライブラリーの設定です。
FunctionsでHTTPリクエストを使いたい為、request-promiseを活用しています。
ボタンを押すことにより、追加できますので、以下を設定してください
- NAME
- request-promise
- VERSION
- 4.2.2
Twilio「Function」を作成する
次に管理をクリックします
以下の画面が表示されますので、
以下のウィンドウが上がるので、+Blankを選択して、
をクリックしてください。
こんなBlankな画面が表示されます。
自然言語解析処理のFunctionを作成する
- FUNCTION NAME
- NLS
- PATH
- /nls
- ACCESS CONTROL
- チェックを外す
- EVENT
- 何も指定しない
CODE部分には以下を貼り付けます。
const request = require('request-promise');
exports.handler = function(context, event, callback) {
var options = {
method: 'POST',
url: 'https://language.googleapis.com/v1/documents:analyzeEntities',
qs: { key: context.GOOGLE_API_KEY },
headers: {
'cache-control': 'no-cache',
'content-type': 'application/json'
},
json: {
document: {
type: 'PLAIN_TEXT',
content: event.text || '',
language: 'ja-JP' },
encodingType: 'UTF8' },
};
request(options)
.then((parsedBody) => {
console.log(parsedBody);
for (const entity in parsedBody.entities) {
console.log(`${parsedBody.entities[entity].name}`);
}
//期待値としては、市区町村名、お店か料理のジャンルの順番でくる
//エンティティが1つの場合、お店の名前か料理のジャンルの名前しかないという想定
if (parsedBody.entities.length == 1) {
callback(null, {freeword:parsedBody.entities[0].name});
}else if (parsedBody.entities.length > 1){
var freeword = '';
var count = 0;
for (const entity in parsedBody.entities) {
if(count == 0){
freeword = `${parsedBody.entities[entity].name}`;
}else{
freeword = freeword + ',' + `${parsedBody.entities[entity].name}`;
}
count++;
}
console.log(freeword);
callback(null, {freeword: freeword});
}
})
.catch((err) => {
console.error(`problem with request: ${err.message}`);
callback(err.message);
});
};
ぐるなびAPIのFunctionを作成する
以下のファンクションを新規で作成する
- FUNCTION NAME
- GNavi
- PATH
- /gnavi
- ACCESS CONTROL
- チェックを外す
- EVENT
- 何も指定しない
CODE部分には以下を貼り付けます。
const request = require('request-promise');
exports.handler = function(context, event, callback) {
//console.log(`text: ${event.text} `);
var options = {
method: 'GET',
url: 'https://api.gnavi.co.jp/RestSearchAPI/v3',
qs: { keyid: context.GNAVI_API_KEY,takeout: '1', pref: context.GNAVI_PREF,hit_per_page: '1',card:'1',freeword: event.text},
headers: {
'cache-control': 'no-cache',
'content-type': 'application/json'
}
};
request(options)
.then((parsedBody) => {
console.log(`name: ${JSON.parse(parsedBody).rest[0].name},address: ${JSON.parse(parsedBody).rest[0].address},tel: ${JSON.parse(parsedBody).rest[0].tel}`);
callback(null, {name:JSON.parse(parsedBody).rest[0].name, address:JSON.parse(parsedBody).rest[0].address, tel:JSON.parse(parsedBody).rest[0].tel});
})
.catch((err) => {
console.error(`problem with request: ${err.message}`);
callback(err.message);
});
};
電話を受けて、自然言語解析、ぐるなび検索の一連の動作を作る
左側のメニューの「RUNTIME」にあるStudioをクリックします。

以下のウィンドウが表示されるので、好きな名前をつけて
ボタンを押します。
以下の画面が表示されるので、Start from scratchを選択して、
ボタンを押します。
そうすると、まっさらなフローが立ち上がります。
右にあるメニューの中にVOICE Say/Playをクリックドラックしましょう
フローに以下のように表示されたと思います。
say_play_1と表示されているボックスをクリックし、以下の表示を確認してください。

WIDGET NAMEはお好みでいいですが、以下のように設定してください
- WIDGET NAME
- SayWelcome
- TEXT TO SAY(自動音声で喋らせるワードです)
- いらっしゃいませ、テイクアウトができるお店をお教えするサービスです
そして、TriggerにあるIncoming CallをSayWelcomeとクリックドラッグで線を繋げます。以下の感じになるはずです。

次に電話から音声入力してもらい、テキストに変換する部分です。
右にあるメニューの中にVOICE Gather Input on Callをクリックドラックしましょう
これも同様に設定していきます。
WIDGET NAMEはお好みでいいですが、以下のように設定してください
- WIDGET NAME
- WhatAreYouLookingFor
- TEXT TO SAY(自動音声で喋らせるワードです)
- 市区町村、料理のジャンルを言ってください。
その他の設定は以下のようにしています。

ボタンを押して忘れずに保存してください。
SayWelcomeのAudio Completeから** WhatAreYouLookingFor**に接続します。
次に、Functionの呼び出しを設定します。右側メニューTOOLS & EXECUTE CODEからRun Functionをクリックドラックしてください
フローにfunction_1が配置されました。
function_1を選択して以下のように設定します。
- WIDGET NAME
- NLS
- FUNCTION URL(Functionsで作成したFunction名を指定)
- NLS
FunctionsParametersの![]()
をクリックして、以下のKey,Valueを入力します

- Key
- text
- Value(** WhatAreYouLookingFor**で音声入力されたテキストデータを設定)
- {{widgets.WhatAreYouLookingFor.SpeechResult}}
以下のように** WhatAreYouLookingForのUser Said SomethingとNLS**を接続します。

ボタンを押して忘れずに保存してください。
もう一つFunctionの呼び出しを設定します。右側メニューTOOLS & EXECUTE CODEからRun Functionをクリックドラックしてください
function_2を選択して以下のように設定します。
- WIDGET NAME
- CallGNavi
- FUNCTION URL(Functionsで作成したFunction名を指定)
- GNavi
- Key
- text
- Value(** NLS**で自然言語解析されたテキストデータを設定)
- {{widgets.NLS.parsed.freeword}}
NLSのSuccessと** CallGNavi**を以下の図のように線を繋ぎます。
右にあるメニューの中にVOICE Say/Playをクリックドラックしましょう
say_play_2と表示されているボックスをクリックし、以下の表示を確認してください。
WIDGET NAMEはお好みでいいですが、以下のように設定してください
- WIDGET NAME
- END
- TEXT TO SAY(自動音声で喋らせるワードです)
- お店は{{widgets.CallGNavi.parsed.name}}でいかがでしょうか?
** CallGNaviのSuccessと END**を以下の図のように線を繋ぎます。
電話番号とFlowを紐づける
右のメニューにある**#Phone Numbers**を選択します
保有している電話番号をクリックすると、以下の表示が出てきます。
A CALL COMES INを*Studio Flowに変更し、左のプルダウンのところを作成したFlowを設定します。
あとは電話をかければ、自動的に作成したフローに接続されますので、試してみてください。



