Author Archives: rutoru

PHPマイクロフレームワークSlimで作るTwilioコールセンターシステム(2.環境設定:3.CSSとJavascript編)

PHPマイクロフレームワークSlimで作るTwilioコールセンターシステム
 1.アプリ紹介編
 2.環境設定
  1.ライブラリインストール編
  2.Apache mod_rewrite設定編

の続きです。

WebアプリケーションにはCSSフレームワークのBootstrapを使っています。bootstrapサイトからダウンロードし、

/var/www/html/runa-cca/
 |-htdocs ... 公開用ディレクトリ
 |   |-vendor
 |       |-bootstrap ... ここ!

に展開します。Bootstrap単体ではなく、テーマであるBootswatchを使っています。Bootswatchサイトからbootstrap.min.cssと、bootstrap.cssとをダウンロードし、/htdocs/vendor/bootstrap配下のファイルを置き換えます。

ほかに、

・JQuery
・Twilio.js(Twilioクライアントライブラリ)

を使っていますが、ソース指定で利用するため、インストールは不要です。

PHPマイクロフレームワークSlimで作るTwilioコールセンターシステム(2.環境設定:2.Apache mod_rewrite設定編)

WebサーバにApacheを使っている場合の設定は、SlimのGitHubに極めてシンプルにまとめられています。

https://github.com/codeguy/Slim

以下のように.htaccessに書いてね、とのことです。さっぱりとした説明ですな。

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]

Slimのパッケージにもサンプルが付いてきます。
RewriteEngine Onとありますので、mod_rewriteを使うということがわかります。
むろん、.htaccessを使うので、ApacheにAllowOverrideの設定が必要になります。

まず、Apacheのconfファイルの設定は、エイリアスを切る場合だと、

Alias /runa-cca/ "/var/www/html/runa-cca/"
<Directory "/var/www/html/runa-cca/">
     AllowOverride All
</Location>

のようになります。

.htaccessをどう書くかなのですが、以下のようにしました。

RewriteEngine On
RewriteBase /runa-cca
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [QSA,L]

「RewriteEngine On」は、Apacheでmod_rewriteを使うために必要です。
「RewriteBase /runa-cca」は、エイリアスを切っているので必要になります。URL書き換えのベースとなります。

実験してみたのですが、RewriteBaseが無いと、

Request exceeded the limit of 10 internal redirects due to probable configuration error.

というエラーがapache_error.logに出力されます。

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^/runa-cca index.php [QSA,L]

と書くと、エラーは止まります。しかし、Runa-CCAのインデックスページ(/runa-cca/)からログインしたとき(/runa-cca/login)にファイルが見つからないと怒られます。
ということで、RewiteBaseは設定が必要。

「RewriteCond」と「RewriteRule」ですが、入り口としては「Apache mod_rewrite Introduction」のページが参考になります。わかりやすく説明されていました。私が使ったのはApache 2.2系列ですが、ドキュメントは新しい方がいいかなあと思って、2.4系列のURLをご紹介しております。

「RewriteCond」は「Apache Module mod_rewrite」を参照。細かく書いてあります。

「RewriteCond %{REQUEST_FILENAME} !-f」は、指定されたファイルが存在しない場合
「RewriteCond %{REQUEST_FILENAME} !-d」は、指定されたフォルダが存在しない場合

です。
Slimのサンプルに「!-d」が無い理由はわかりませんでした。私は付けておくことにしました。

「RewriteRule」は「RewriteRule Flags」を参照。これまた細かく書いてあります。
「RewriteRule ^ index.php [QSA,L]」は、すべてをindex.phpにまわせ、クエリーストリング(/pages/123?one=twoのone=two)は付けたままにしろ(QSA|qsappend)、書き換えルールはここでストーップ(L|last)という意味です。「^」の解釈に悩みましたが、「.*」にしても同じ動作でした。すべて、ということでしょう。

調べるの大変でした・・・。

PHPマイクロフレームワークSlimで作るTwilioコールセンターシステム(2.環境設定:1.ライブラリインストール編)

こちらのページでも紹介していますが、Twilioを使ったアプリケーション(Webクライアント+コールフロー)を作成しました。

以下の本を読みまして、PHPマイクロフレームワークSlim、テンプレートエンジンTwig、ORマッパーilluminate/Eloquentを使って開発しています。入力値検証用にRespect/Validationも試用しています。

あとはフロントエンドにJQueryとBootstrap/Bootswatchを使っています。もちろん、Twilio-phpも使っています。

フォルダ構造は以下の通りとしました。詳細は別途。ルートは/var/www/html/runa-cca/というフォルダとします。

/var/www/html/runa-cca/
 |-composer.json ... Composer定義ファイル
 |-composer.lock ... Composerバージョンロックファイル
 |-create_table_for_Runa-CCA.sql ...テーブル定義・初期データ投入SQLファイル
 |
 |-htdocs ... 公開用ディレクトリ
 |   |-vendor ... 外部のライブラリ
 |       |-bootstrap ... bootstrap
 |
 |-lib ... 作成したクラスライブラリ
 |   |-Base ... 設定クラス置き場
 |   | 
 |   |-Controller ... コントローラクラス置き場
 |   |   |-CallFlow ... コールフロー毎のコントローラクラス置き場
 |   |
 |   |-Model ... モデルクラス置き場
 |   |   |-CallFlow ... コールフロー毎のモデルクラス置き場
 |   |
 |   |-Model ... ビュークラス置き場
 |
 |-templates ... 作成したテンプレートファイル
 |
 |-vendor ... ライブラリ(Composerでインストール)

まずは環境設定です。

そもそもの環境ですが、Macbook Pro Retina ME865J/AMAMPを動かしています。バージョンは3.0.3を使いました。なので、Apacheは2.2.26、PHPは5.5.10、MySQLは5.5.34になります。

その上で、PHPのパッケージマネージャであるComposerを使って、必要なライブラリをインストールします。
Composerの定義ファイルcomposer.jsonは以下の通り。

{
    "require": {
        
        "slim/slim" : "2.*",
        "slim/views" : "0.1.*",
        "slim/extras" : "2.0.*",
        "twig/twig" : "1.*",
        "illuminate/database" : "4.0.*",
        "Respect/Validation" : "0.5.*",
        "twilio/sdk": "dev-master"
        
    },
    "autoload": {
        "psr-0": {"": "lib/"}
    }
}

参照書籍の記載の通り、以下のようにしてComposerをダウンロードします。

$ cd /var/www/html/runa-cca/
$ php -r "eval('?>'.file_get_contents('https://getcomposer.org/installer'));"
#!/usr/bin/env php
All settings correct for using Composer
Downloading...

Composer successfully installed to: /var/www/html/runa-cca/composer.phar
Use it: php composer.phar

composer.jsonをアップロードしておきます。Runa-CCAをディレクトリごとアップしておけばよいかと。
で、以下のコマンドを実行します。
composer.lockがある場合は、引数をinstallにすると、lockの内容を再現するようにインストールされます。

$ php composer.phar update
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing respect/validation (0.5.0)
    Downloading: 100%         

  - Installing slim/slim (2.4.3)
    Downloading: 100%         

  - Installing slim/views (0.1.2)
    Downloading: 100%         

  - Installing slim/extras (2.0.3)
    Downloading: 100%         

  - Installing twig/twig (v1.15.1)
    Downloading: 100%         

  - Installing nesbot/carbon (1.8.0)
    Downloading: 100%         

  - Installing illuminate/support (v4.0.10)
    Downloading: 100%         

  - Installing illuminate/container (v4.0.10)
    Downloading: 100%         

  - Installing illuminate/events (v4.0.10)
    Downloading: 100%         

  - Installing illuminate/database (v4.0.10)
    Downloading: 100%         

slim/slim suggests installing ext-mcrypt (Required for HTTP cookie encryption)
slim/views suggests installing smarty/smarty (Smarty templating system)
Writing lock file
Generating autoload files

#twilio/sdkは省略

これで、ライブラリがvendor配下にダウンロードされます。
続きはまた別途。

https://github.com/rutoru/Runa-CCA

PHPマイクロフレームワークSlimで作るTwilioコールセンターシステム(1.アプリ紹介編)

PHPマイクロフレームワークSlimを使って、Twilioコールセンターシステムを作成しました。
まだいろいろとわからないところ、足りないところも多く、作成中の段階です。

技術的な内容は別途として、まずはアプリの説明からなのですが、こちらのページに書いてみました。よろしければご覧ください。

https://github.com/rutoru/Runa-CCA

PHPテンプレートエンジンTwigでセッション情報を取得する(PHPフレームワークSlimを使って)

Webアプリを作っていまして、フレームワークはSlimを使い、テンプレートエンジンはTwigを使っていました。

はじめのうちは、セッションに入れた情報をそのまま使わずに、PHPから変数でTwigに渡していました。しかし、いちいち変数を渡すのはめんどくさい。ViewたるTwigから、セッション情報を直接参照しても、何も問題はないでしょう。Twigでセッションを参照できる方法ないかなあと調べていたところ、ありました。

Using Twig functions inside of Slim

上記サイトに紹介されているとおりに書けば良さそうなのですが、少しハマりました。
セッションに情報を埋め込んだ後に書く必要があります。
つまり、

$app = \Slim\Slim::getInstance();

$twig = $app->view()->getEnvironment();
$twig->addGlobal("session", $_SESSION);

$_SESSION['hoge'] = "hohoho";

ではなく

$app = \Slim\Slim::getInstance();

$_SESSION['hoge'] = "hohoho";

$twig = $app->view()->getEnvironment();
$twig->addGlobal("session", $_SESSION);

と書かなければなりません。

これで、Twigでは、

{{session.hogehoge}}

という形で参照できます。

MySQL #1452 – Cannot add or update a child row: a foreign key constraint fails

MySQLで外部キーを設定しようとして、

ALTER TABLE `child` add FOREIGN key(`id`) REFERENCES parent(`id`);

とした場合、「#1452 – Cannot add or update a child row: a foreign key constraint fails」エラーが返ってくることがあります。

childテーブルのidに、parentテーブルのidに存在しない値が入っているとこのエラーになります。childテーブルのidの値を修正すればOKです。

こちらのサイトを参照しました。

PHPマイクロフレームワークSlim利用時の設定とフォームのaction指定

Webアプリエンジニア養成読本』を読みながら、PHPマイクロフレームワークであるSlimを使ってプログラミングしています。

Slimはルーティング中心のシンプルなフレームワークです。RubyのSinatraを参考に作られたらしい。Sinatraもそうですが、読んだり調べたりするだけだと、何が何やらさっぱりわからない・・・。書いてみて、あーそういうことかーとわかります。テンプレートエンジンTwig、データベース操作illuminate/Eloquentとの組み合わせで、MVCを実現できます。

SlimをApacheで利用する場合、mod_rewriteの設定が必要です。『Webアプリエンジニア養成読本』にはこの部分がすっぽりと抜けているので、わかりにくいです。

例えば、Apache本体で、

Alias /hoge/ "/var/www/http/hoge/htdocs/"
<Directory "/var/www/http/hoge/htdocs/">
     AllowOverride All
     Options All
     Order allow,deny
     Allow from all
</Directory>

となっている場合、.htaccessは、

RewriteEngine On
RewriteBase /hoge
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [QSA,L]

とし、/var/www/http/hoge/htdocs/配下(index.phpと同じ場所)に置きます。
.htaccessのテンプレートは、Slimパッケージの中にあります。

index.phpは、

<?php
$app = new \Slim\Slim();
$app->get('/', function () use ($app) {
    $app->render(“welcome.php”);
});

$app->post('/post/login', function () use ($app) {
    $app->render(“login.php”);
});
$app->run();

として、welcome.phpに、

<form action="post/login/" method="post">

とフォームを書いておけば、login.phpが呼び出されるかと思ったのですが、呼び出せませんでした。「404 Page Not Found」のエラーになってしまいました。

しばらくハマったのですが、そりゃそうです。formのaction指定に誤りがありました。

<form action="post/login" method="post">

というように、loginの後のスラッシュを消しておかなければなりません。そうしないとどこにもマッチせず、Not Foundになります。むろん、index.php側を「$app->post(‘/post/login/’, function ($app) {」と修正しても構いません。HTMLのフォームの記述と、Slimの記述と、両者を一致させる必要があります。

mod_rewriteは間違いも多いところなので、httpd.confとかに、

RewriteLog "/tmp/rewrite.log"
RewriteLogLevel 9

としておいて、設定・試験中はログを見ながら作業するといいと思います。ログが膨大に出ちゃいますが、試験環境ならいいかと。

MAMPでXdebugを有効化

Macbook Proに、MAMPを利用して、PHPの開発環境を作っています。

NetBeansでデバッグのためにステップ実行をしようと考えました。その場合Xdebugを有効化する必要があります。
php.iniに以下を記載し、Apacheを再起動。phpinfoを開きましたが、xdebugセクションが表示されません(有効になっていない)。

zend_extension="/Applications/MAMP/bin/php/php5.5.10/lib/php/extensions/no-debug-non-zts-20121212/xdebug.so"
xdebug.remote_enable = 1

xdebug.soは存在するし。設定の構文も間違っていません。
うーん。

改めてphpinfoを見直してみて、やっとわかりました。修正するべきphp.iniファイルを誤っていたのです。MAMPにおいて、php.iniはなぜか2カ所にあります。

/Applications/MAMP/bin/php/php5.5.10/conf/php.ini
/Applications/MAMP/conf/php5.5.10/php.ini

修正するべきは、

/Applications/MAMP/bin/php/php5.5.10/conf/php.ini

の方でした。

うーん、ハマったなあ。

Twilioコールセンターシステムにヒストリカルレポート機能を搭載した

Twilioコールセンターシステム(Twilio-MiniCC)にヒストリカルレポート機能を搭載しました。MySQLにデータを貯めるので、レポート参照はいろいろなツールを使えばいいでしょう、というのを言い訳にして、データ収集機能までの実装になります。

アプリケーションなんだから、適当なところでデータベースにデータ入力しておけばいいだろう、くらいのノリだったのですが、結構大変でありました。さんざん調べたり、考えたりして、とりあえず以下の3ポイントで、取れるデータをすべて取ってみたつもりです。さすがに不要なので、標準リクエストパラメータからAccountSidは抜きました。
これからの記述でおかしなところやもっとカイゼンするべき点があったら、ぜひご指摘ください。

データ取得のポイントは以下の3つ。本当はIVRの分岐点でもログを取って、お客様がどのようなルートを通っているのかを把握するべきなのですが、今回は割愛しました。

  1. キューのお客様側(Enqueue)から出た時
  2. キューのオペレータ側(Queue)から出た時
  3. 通話終了時

Twilioのキューの考え方はこちらが参考になります。お客様とオペレータとで同じQueueに入ります。入り方が異なっていて、お客様はTwiMLのEnqueueタグ(動詞)で、オペレータはDialタグ(動詞)のQueueタグ(動詞)で入ります。

まずはじめに、キューのお客様側(Enqueue)から出た時のデータ取得方法です。
Enqueue動詞ではaction属性が設定できます。ややこしいのですが、Enqueueのaction属性は、以下の動きになります。日本語サイトはやや怪しい(一部しか翻訳されていないところがある)ので、英語のサイトから引用します。

In the case where a call is dequeued due to a REST API request or the verb, the action URL is requested right away. In the case where a call is dequeued via the verb, the action URL is hit once when the bridged parties disconnect. If no ‘action’ is provided, Twilio will fall through to the next verb in the document, if any.

Twilio-MiniCCでは、今のところDial動詞のQueue動詞を使って呼をキューから出しますので、Enqueueのaction属性に指定されたURLは、接続された相手同士が切断されたときに、一度実行されることになります。
(キューから出たときではありません)

Enqueue動詞のaction属性でデータを取る意味は、「放棄呼」の情報を取ることができる点にあります。
一般的にコールセンターにおいて、お客様がキューに入って自分から切断した場合、その呼は「放棄呼」とされます。放棄呼が発生した場合には、actionに指定してあるURLが実行されます。そのURLで、データベースにデータを登録するプログラムを書いておけば、放棄呼の情報を取得することが出来ます。具体的には、QueueResultカラムでhangupが記録されることになります。放棄呼はコールセンターにとっては良くないことです。本来受け付けるべき呼を受け付けられなかったわけですから。放棄呼のデータを収集し、対策を打つことが極めて重要です。

なお、特に記載はないのですが、Enqueueのaction属性は、おそらく非同期で実行されています。Basic認証を使用している場合、以下のように書くとエラーが発生します。TwilioのAPPモニターに「11200 – HTTP 復帰エラー」が出て、内容を見ると、「401 Authorization Required」が出ます。

$response->enqueue('キュー名', array('waitUrl' => 'wait.php',
                                    'action' => 'action.php');

そのため、以下のように修正する必要があります。

$response->enqueue('キュー名', array('waitUrl' => 'wait.php',
                                    'action' => 'http://A:B@C/D/action.php');

A: Basic認証のユーザ名
B: Basic認証のパスワード
C: Webサーバのドメイン名
D: action.phpを置いた場所

あと、Twilio-MiniCCにおいて、本機能はexit_enqueue.phpで実装されているのですが、何もレスポンスが無いとオペレータから切断したときエラーメッセージになるため、空のTwiMLを出力するようにしています。

続いて、キューのオペレータ側(Queue)から出た時のデータ取得方法です。
Queue動詞においては、url属性を指定できます。英語のサイトから引用します。

The ‘url’ attribute takes an absolute or relative URL as a value. The url points to a Twiml document that will be executed on the queued caller’s end before the two parties are connected. This is typically used to be able to notify the queued caller that he or she is about to be connected to an agent or that the call may be recorded. The allowed verbs in this TwiML document are Play, Say, Pause and Redirect.

Queue動詞はオペレータが使うものなのですが、Queue動詞のurl属性には「お客様側」で実行したいことを書きます。ややこしい。オペレータとの接続前に、録音することをアナウンスするとか、そういうために使うそうです。お客様にアナウンスを流しつつ、裏でデータベースにデータを格納すれば、Queueのデータを取得できます。

最後に通話終了時のデータ取得方法です。
How To Track and Report Your Twilio Usage」という記事に書いてありました。通話終了後にStatusCallback Requestを投げることが出来、そこでレポートを取得します。

StatusCallback occurs asynchronously and after a call has completed, this is an opportune time to update properties of your local call record. Your application also has more flexibility at this time to process database updates or compile larger reports without impacting user experience.

非同期処理なので負担もかからずオススメということらしい。
デフォルトではオフ。Twilioのページから、「電話番号」をクリックした後、 「Optional Voice Settings」をクリック。表示される「Status Callback URL」に、プログラムへのURLを書けばオッケー。通話終了後に実行されます。Basic認証を使っている場合は、先ほどのEnqueueのときと同様の書式にする必要があります。

最後に、データベース構造なのですが、まず型がわかりませんでした。Twilioのサンプルをいくつか見ましたが、全部SQLiteのTEXT型になっているし。
REST APIの資料とか見て、MySQLの型を決めましたが、これでいいのかどうかはわかりません。そもそも正規化とかしてないので、まだまだ検討が必要そうです。

ともあれ、Twilio-MiniCCのVersion 2が完成しました。
ヒストリカルレポート収集機能の追加と同時に、データベースアクセスをPDOにしたり、設定共通クラスを作ったりと、ソースコードをカイゼンしています。
Webクライアントも作成済みなのですが、まだ公開するまでのレベルではないので、次のバージョンとします。

Raspberry Pi上でAsteriskを動かして自宅内PBX兼VoiceMailサーバにする

Raspberry Piですが、VPNサーバにしたところで、いよいよやりたかった自宅PBX化に取り組みました。
自宅のひかり電話(ルータはRT-200KI)とつなぎます。自宅に電話がかかってきたら、録音してメール送信するようにします。自宅PBXというか、自宅VoiceMailサーバ(留守録)ですな。

調べたところ、RasPBXというディストリビューションがあります。それを使えば早かったのですが、せっかくSoftEtherとか設定してきて、クリアするのももったいなかったので、Asteriskを新規にインストールすることにしました。

Raspberry Pi でAsterisk」という素晴らしいサイトに、インストール手順が載っています。が、Raspbianでは、apt-get install asteriskでインストール出来てしまいます。こちらの方が簡単です。asteriskユーザも作ってくれます。サービス起動でエラーが出る場合は、上記サイトを参考にして、権限設定したりすればオッケーです。なお、設定ファイルは、VoIP-Info.jp Wikiで公開されている1.6のものを使いました。こちらを参照。

内線とひかり電話の設定は、sip.confで行います。
私の環境だけかもしれませんが、RT-200KIのIP内線で、独自のパスワードに変えると接続できませんでした。RT-200KIデフォルトのパスワードに戻したところ、うまくレジストすることができました。
外線発信・着信の設定は、extensions.confです。読むと何となくわかります。色々なサイトを参考に設定しました。

asterisk -vvvrでコンソールに入ります。SIPソフトフォンをレジストしたりすると、ログが表示されます。Asteriskのログはわかりやすくてよいです。
なお、SIPソフトフォンは、MacではX-Lite、iPhoneではZoiperを利用しました。

あとはボイスメール設定です。
extensions.confについては、こちらの「asteriskの導入 (3)」というサイトが一番参考になりました。外線着信して、SIPソフトフォンが出れなかったら、ボイスメールに着信させます。
ボイスメールからメールを送信する設定は、voicemail.confに書きます。「Asterisk サンプル設定ファイル voicemail.conf」に解説されています。これらの設定で、Asteriskはボイスメールシステムとなり、電話がかかってきたら音声を録音し、メール送信します。

Raspberry PiとAsteriskでボイスメールって、かなり実用的で、Piのいい活用例だと思いますが、いかがでしょうか。
まあ、あまり自宅に電話がかかってくることは無いですけどね。