こんにちは、サイオステクノロジー技術部の武井です。
今回は、LINEのBOTをRaspberry Piで作成し、BOTとのやりとりをOpen JTalkで読み上げ、Azure DocumentDBに保存し、Azure App Service上のWebアプリで一覧表示する仕組みを作ってみました。
写真を撮ってみました。こんなかんじです。
Open JTalk、Azure DocumentDB、Azure App Serviceとは?
Open JTalkはオープンソースの音声合成システムで、テキストを渡すとその内容を読み上げてくれます。
Azure DocumentDBはJSON形式の文書をそのまま保存できるスキーマレスのデータベースです。リレーショナル・データベースとは違い、スキーマの設計はいりません。やりとりされるJSONのデータをそのままデータベースに登録が可能であり、クエリ言語も慣れ親しんだSQLを使うので、データ操作も簡単です。
Azure App ServiceはAzure上でアプリケーションを動作することができるPaaS環境になります。OSやアプリケーションサーバーを構築する必要もなく、パッチ当てもする必要がありません。開発者はソースコードさえ登録すれば、後の面倒なことは全部Azureがやってくれます。
何ができるの?
つまり何をするものかというと、私が作成したLINEのBOTとお友達になって、そのBOTに対して発言すると、ちょっとした会話ができて、さらにその会話をOpen JTalk(音声合成システム)によって音声で読み上げ、その会話の内容をAzure DocumentDBにJSON形式で保存して、さらにAzure App Service上で作成したPHPのWebアプリケーションからDocumentDBにアクセスして、会話を一覧表示します。
文章だけではわかりにくいと思いますので、構成図を書いてみました。
どんな仕組み?
仕組みとしては以下です。
1.LINEのBOTを作成できるサービス「BOT API Trial Account」にアカウント登録する。
2.「BOT API Trial Account」の管理画面からコールバックURLを設定する。BOTアカウントに対して発言をすると、このコールバックURLに対して、JSON形式でPostリクエストが来る。コールバックURLのアクセス先はRaspberry Pi上にWebアプリケーションとして構築する。
3.BOTアカウントに対して発言したことによって、Raspberry Pi上のコールバックURL宛にリクエストがあったJSONを解析する。
4.3の内容をOpen JTalkに渡して、喋らせる。
5.Azure DocumentDBのRest APIを用いて、会話の内容をJSON形式のまま保存する。
6.Azure App Service上に作成したWebアプリケーションにアクセスすると、会話の内容が一覧表示される。
どうやって作るの?
構築手順を記載します。
まず、Raspberry Piを用意します。OSは何でもよいのですが、今回はRaspberry Piの本家サイトで提供されているRaspbianのイメージを使いました。このあたりの方法はWebにたくさんありますので、そちらを参照して下さい。
Open JTalkをインストールします。インストール対象のソフトウェアを以下のサイトからダウンロードして下さい。
■Open JTalk
https://downloads.sourceforge.net/open-jtalk/open_jtalk-1.09.tar.gz
■hts_engine API
https://osdn.jp/projects/sfnet_hts-engine/downloads/hts_engine%20API/hts_engine_API-1.10/hts_engine_API-1.10.tar.gz
# mkdir /usr/local/hts_engine_API # tar xzvf hts_engine_API-1.10.tar.gz # ./configure --prefix=/usr/local/hts_engine_API # make # make install # mkdir /usr/local/open_jtalk # tar xvzf open_jtalk-1.09.tar.gz # cd open_jtalk-1.09 # ./configure --prefix=/usr/local/open_jtalk --with-hts-engine-header-path=/usr/local/hts_engine_API/include --with-hts-engine-library-path=/usr/local/hts_engine_API/lib --with-charset=UTF-8 # make # make install # tar xzvf open_jtalk_dic_utf_8-1.09.tar.gz # cp -r open_jtalk_dic_utf_8-1.09 /usr/local/open_jtalk/ # unzip MMDAgent_Example-1.4.zip # mkdir /usr/local/hts_engine_API/hts_voice # cp -r MMDAgent_Example-1.4/Voice/mei/*htsvoice /usr/local/hts_engine_API/hts_voice/
試しに「こんにちは」と喋らせてみます。
# echo "こんにちは" | /usr/local/open_jtalk/bin/open_jtalk -m /usr/local/hts_engine_API/hts_voice/mei_normal.htsvoice -ow output.wav -x /usr/local/open_jtalk/open_jtalk_dic_utf_8-1.09
上記のコマンドを発行すると、「output.wav」というファイルができます。これをaplayで再生するのですが、Raspberry PiはデフォルトでLINE Out端子から音声が出力されません。以下のコマンドを実行して下さい。
# amixer cset numid=3 1
以下のコマンドで音量を調節して下さい。
# alsamixer
LINE Out端子にスピーカをつないで、以下のコマンドを実行して下さい。
# aplay output.wav
女性の声で「こんにちは」と聞こえてきたはずです。これでOpen JTalkのインストールは完了です。
※Open JTalkについては以下の記事を参考にしました。この記事を作成したH氏に感謝致します。
次は、LINE BOT API Trial Accountに登録します。これは、期間限定、人数限定でLINEのBOTを作成するためのAPIを無料で利用できるサービスです。以下のURLから登録して下さい。登録はお早めに。
https://business.line.me/services/products/4/introduction
申請する途中で「Callback URL」」を指定するところが出てきます。これはRaspberyy Pi上に構築したWebアプリケーションのURLである必要があり、BOTに対して発言した時に、このURLに発言内容がHTTPSプロトコルでJSON形式で送られてきます。こちらご注意頂きたいのが、このURLは公的な認証機関でサインした証明書を利用したサイトでないとダメなのです。オレオレ不可です。また、もちろん仕組み上このURLには外部からアクセスできる必要があります。セキュリティには十分お気をつけ下さい。
次は、DocumentDBを作成する準備をします。Azureポータルにログインし、「参照」→「データ + ストレージ」 → 「Azure DocumentDB」の順番にクリックして、一番右のブレードに出てくる画面で必要な事項を入力して「作成」をクリックして下さい。「ID」は任意の名称で結構です。サブスクリプションやリソースグループは環境に応じたものを入力して下さい。場所は自分の住んでいるところに一番近いほうが、お財布にやさしいかと思います。
これで、Azure DocumentDBを作成する準備が整いました。本来なら、、これからデータベースとコレクションの作成を行わなければいけないのですが、以降に紹介するPHPスクリプトで自動的に作成されるようになっていますので、割愛します。
次は、BOTに対して発言があった際にLINEからコールされるコールバックURLの呼び出し先をRaspberry Pi上に作成します。Raspberry PiにApache、PHPをインストールして、Document Rootに以下のソースを置いて下さい。
<?php require_once '/var/www/html/phpdocumentdb.php'; // DocmentDBのホスト名を指定する $host = 'https://hostname.documents.azure.com'; // DocumentDBのマスターキーを指定する $master_key = 'maserkey'; // DocumentDBに接続する $documentdb = new DocumentDB($host, $master_key,$debug = true); // LINEのBOTアカウント管理画面に表示されている「Channel ID」、「Channel Secret」、「MID」を指定する $channel_id = "1234567890"; $channel_secret = "abcdefghijklmnopqrstuvwxyz"; $mid = "aaaabbbbccccdddd"; // コールバックURL宛のリクエストを取得する $json_string = file_get_contents('php://input'); $json_object = json_decode($json_string); $content = $json_object->result{0}->content; $text = $content->text; $from = $content->from; $message_id = $content->id; $content_type = $content->contentType; // Azure DocumentDBに入れるためIDを生成する $json_object->id = $json_object->result{0}->content->id; // 送信者を入れる $json_object->sender = 'user'; $user_document = json_encode($json_object); // ユーザーの発言をDocumentDBに保存する $documentdb->createDocument("linebot","messages",$user_document); // ユーザーの発言をOpen JTalkで読み上げる exec("sudo /usr/local/bin/line_talk ".$text); // ユーザーに対して返信をする $url = "https://trialbot-api.line.me/v1/events"; $headers = array( "Content-Type: application/json", "X-LINE-ChannelID: {$channel_id}", "X-LINE-ChannelSecret: {$channel_secret}", "X-LINE-Trusted-User-With-ACL: {$mid}" ); // 下記の中からランダムで発言する $contents = array( "今日はいい天気ですね", "おなか空いたよ", "ごきげんはいかがですか?", "眠いよ", "おはようございます", "こんばんは" ); $content_key = array_rand($contents); $content = $contents[$content_key]; // BOTの発言内容のJSONを作成する $post = <<< EOM { "to":["{$from}"], "toChannel":1383378250, "eventType":138311608800106203, "content":{ "toType":1, "contentType":1, "text":"{$content}" } } EOM; // LINEのRestAPIを実行して実際に発言する $curl = curl_init($url); curl_setopt($curl, CURLOPT_POST, true); curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); curl_setopt($curl, CURLOPT_POSTFIELDS, $post); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); $output = curl_exec($curl); $posted_json_object = json_decode($post); $returned_json_object = json_decode($output); $posted_json_object->id = $returned_json_object->messageId; $posted_json_object->timestamp = $returned_json_object->timestamp; $posted_json_object->sender = 'bot'; $bot_document = json_encode($posted_json_object); // BOTの発言をDocumentDBに保存する $documentdb->createDocument("linebot","messages",$bot_document); // BOTの発言をOpen JTalkによって読み上げる exec("sudo /usr/local/bin/line_talk ".$content); ?>
上記のコード中に「phpdocumentdb.php」を読み込んでいる部分があります。これは、Azure DocumentDBを操作するRest APIのラッパークラスです。以下で公開しております。作成していただいた方に感謝です!!
https://github.com/cocteau666/AzureDocumentDB-PHP
ただしこれはこのままでは使用できません。ドキュメントの作成に失敗してしまいます。おそらく当時とAPIの仕様が変わったのだと思います。以下の変更を加えて下さい。
576c576,577 < $headers = $this->getAuthHeaders('POST', 'docs', $rid_col); --- > // $headers = $this->getAuthHeaders('POST', 'docs', $rid_col); > $headers = $this->getAuthHeaders('POST', 'docs', 'dbs/'.$rid_id.'/colls/'.$rid_col);
また、上記のソースコード中に
exec("sudo /usr/local/bin/line_talk ".$content);
の記述があります。こちらは、内部的には以下のようになっており、LINEのタイムラインで発言された内容をOpen JTalkに渡しているShellを呼び出しています。
#!/bin/bash echo $1 | /usr/local/open_jtalk/bin/open_jtalk -m /usr/local/hts_engine_API/hts_voice/mei_normal.htsvoice -ow /tmp/output.wav -x /usr/local/open_jtalk/open_jtalk_dic_utf_8-1.09 aplay /tmp/output.wav
次は、LINEからコールされるコールバックURLの呼び出し先を置くWebアプリケーション基盤を作成します。これはAzure App Serviceで実現します。
Azureポータルから「参照」→「App Service」の順にクリックします。
「追加」をクリックします。
「アプリ名」に任意の名前を入力し、「サブスクリプション名」や「リソースグループ」は環境に応じて必要なものを入力して、最後に「作成」をクリックします。
先ほど作成したアプリが一覧に表示されていることを確認します。
先ほど一覧に表示されていたアプリ名の右端に黒い点が3つ並んだアイコンがあります。それをクリックして「ツール」をクリックします。
「Visual Studio Online (プレビュー)」をクリックします。
「オン」をクリックします。
「移動」をクリックします。Visual Studio Onlineが起動します。
起動したVisual Studio Online上で以下のソースコードを作成して下さい。ファイル名はindex.phpとして下さい。
<?php // DocumentDB操作用Classをincludeする require_once 'D:homesitewwwrootphpdocumentdb.php'; // DocumentDBのホスト名 $host = 'https://hostname.documents.azure.com:443/'; // DocumentDBのマスターキー $master_key = 'masterey'; // DocumentDBに接続する $documentdb = new DocumentDB($host, $master_key,$debug = false); // データベースを選択する $db = $documentdb->selectDB("linebot"); // コレクションを選択する $col = $db->selectCollection("messages"); // クエリを実行する $user_json = $col->query("SELECT 'you' as sender,user.result[0].content.text as message, user.result[0].content.createdTime as time FROM c user where user.sender = 'user'"); $bot_json = $col->query("SELECT 'bot' as sender,bot.content.text as message, bot.timestamp as time FROM bot where bot.sender = 'bot'"); $user_json_object = json_decode($user_json); // ユーザーが発言した発言のJSONObject $bot_json_object = json_decode($bot_json); // BOTが発言した発言のJSONObject // ユーザーとBOTの発言をマージする $messages = array_merge($user_json_object->Documents,$bot_json_object->Documents); // 発言日付でソートする foreach ($messages as $key => $value) { $key_id[$key] = $value->time; } array_multisort( $key_id , SORT_ASC , $messages); // 発言を表示する echo "<table border=1>"; echo "<tr>"; echo "<td>送信者</td><td>発言内容</td><td>発言日付</td>"; echo "</tr>"; for ($i=0;$i < count($messages);$i++) { echo "<tr>"; echo "<td>".$messages[$i]->sender."</td><td>".$messages[$i]->message."</td><td>".date("Y年m月d日 H時i分s秒", $messages[$i]->time/1000)."</td>"; echo "</tr>"; } echo "</table>"; ?>
ここでも同様にphpdocumentdb.phpを呼び出しています。このphpファイルも同様に、Visual Studio Online上に配置して下さい。実は、このままではこのソースは動作しません。Azure Remote App上からcurlでhttps通信を行う場合、通信先の証明書発行元の認証局のルート証明書が必要になります。しかし、今回はそのようなことはせずに、SSL通信の証明書のチェックを無効にすることで回避します。ただ、こちら、本番環境ではこのようなことはやらないでください。
phpdocumentdb.phpを以下のように修正して下さい。
159a160 > curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
これで準備は整いました。では、早速動作確認してみましょう。
先ほど登録申請したBOTアカウントに話しかけてみます。すると、応答が返ってきます。こちら、先ほどRaspberryPi上に作成したスクリプトが応答をして、メッセージを返しています。
そして、写真だけではわかりにくいのですが、Rapbberry Piにつながったスピーカーから、BOTとのやりとりが音声で流れてます。
そして先ほどAzure App Service上に作成したWebアプリケーションを起動してみましょう。
BOTとのやりとりが一覧で表示されいます。こちらは、Raspberry Piから登録されたDocumentDB内のJSONデータを表示しています。
いかがでしたでしょうか?なんとなく面白そうなサービスをくっつけただけで実用性は皆無ですが、Azureで色々なことができるんだなと実感頂けましたら幸いです。