Author Archives: rutoru

Twilioでコールセンターシステムを作ってみた

Twilioでコールセンターシステムを作ってみました。

※続編は「Twilioコールセンターシステムにヒストリカルレポート機能を搭載した」です。

※今は「Runa-CCA」を鋭意開発中になります。

何で作ろうかなあと考えました。
Rubyで作ろうかとも思ったのですが、Webアプリですし、フレームワーク上に構築するまでの規模でもなかったので、手軽なPHPにしました。

まず以下の本を買ってお勉強。
私がプログラマーだったとき、PHPはPHP3でありまして、今やPHP5.5です。もはやわかりません。ということで一から勉強し直してみました。PHP、MySQL、JavaScriptとCSSの基礎を学べる、非常にいい本です。オススメします。

コールセンターの機能としては、

音声自動応答(IVR)
音声ガイダンス
キューイング(待ち順番アナウンス付き)

の3つを搭載しようと考えました。基本的なフローは以下の通り。

Twilio-MiniCC Sequence

実装を考えます。

IVRが出来るということは、Twilioのブログにも書いてあったので、それほどハードルは高くないだろうと考えていました。コールセンターシステムで必要なのはキューです。オペレータに着信させなければならないのですが、オペレータがすべて電話業務(よく「受電」とか言います)をしているとき、お客様を待たせる必要があります。オペレータが忙しいからって、すぐに電話を切ってしまうのは良いセンターとは言えませんよね。その待たせる機能である「キュー」がTwilioにあるのかなあ・・・と思って探してみたら、ありました。こちらに紹介されています。

うーん、簡単すぎるぞ。オペレータはQueueタグで、お客様はEnqueueタグを使うのか。それだけでいいのかなあ・・・と思いましたが、まあ作ってみることにしました。

Twilioには、PHP用のHelperライブラリがあります。それを使うと実装は簡単です。今回のような、“着信”を処理する場合は、TwiMLというXMLライクなマークアップ言語を用いてTwilioに命令する形になるので、Helperライブラリ無しでも作れてしまいます。が、Responseタグの閉じ忘れとかなんとか、いろいろと発生しましたので、素直にHelperライブラリに頼るのが良いでしょう。

久々のPHP言語に不慣れだったので、PHPでのデータベースアクセス処理とか、クラスの定義方法とかに時間がかかりました。また、実行しても結果が無い(白いページが表示される)のにも悩まされました。白いページになる場合は、php.iniでエラーが出力される設定になっていない可能性があります。iniファイルの設定を変更するか、ini_set( ‘display_errors’, 1 );を書く必要があります。エラー出力される設定になっていても、白いページになることがあります。その場合は、php.iniに指定されているログを見ると、エラー内容を確認することが出来ます。当たり前かもしれませんが、なにぶん不慣れなので、こういったところに時間がかかってしまいました。

Twilioの部分は極めて簡単です。PHPを知っている人だったら、すぐに作れちゃうと思います。キューの実装もブログにある通り、超簡単でした。

完成したものをGitHubに公開しております。勉強用に、細かいところまでコメントを書いたつもりですので、よろしければご覧ください。

Raspberry PiからSSMTPとMailコマンド(mailutils)でメール送信

Raspberry Piからメール送信させたいなあと思ったのですが、Postfixを立てるのは面倒。
メールサーバはさくらのVPSにアカウント作ってそれを利用すればいいし。

調べたところ、SSMTPというソフトが良さそうだったので、早速インストールして使ってみました。

$ sudo apt-get install ssmtp
$ sudo vi /etc/ssmtp/ssmtp.conf

mailhub=利用するメールサーバ名:ポート番号
AuthUser=SMTP Auth使う場合のユーザ名
AuthPass=SMTP Auth使う場合のパスワード
AuthMethod=サーバ設定にあわす(LOGINとか)
UseSTARTTLS=同上(Yesとか)
UseTLS=同上(Yesとか)

以下のようなファイルを作ります。

From:送信元アドレス
to:送信先アドレス
Subject:Test

Test

送るのは、

$ sendmail -t < testmail.txt

でオッケー。

Mailコマンドを使う場合は、mailutilsをインストール。

$ sudo apt-get install mailutils

で、mailコマンドを使ってメールしようとするも、うまく行きませんでした。

$ mail -s test 送信先アドレス
Cc: 
Test
[Ctrl+D] 
cannot send message: Process exited with a non-zero status

/var/log/mail.logをチェックしたところ、「Sender address rejected: Domain not found」となっていました。Fromを指定していないから送れなかったのね。man mailとすると、

-a,  --append=HEADER:  VALUE  append  given header to the message being sent

とありました。これを手がかりにして、

$ mail -s test 送信先アドレス -aFrom:送信元アドレス

としたところ、うまく送信できました。
ログ確認とmanページ確認重要(当たり前だ)。

Raspberry Piの無線LANのIPアドレスを固定IPにする

iface wlan0 inet static
 address 192.168.1.5
 netmask 255.255.255.0
 network 192.168.0.0
 gateway 192.168.1.1

って書いて、ifupしたら怒られました。

$ sudo ifup wlan0                                              
ioctl[SIOCSIWENCODEEXT]: Invalid argument
ioctl[SIOCSIWENCODEEXT]: Invalid argument
RTNETLINK answers: File exists
Failed to bring up wlan0.

はい、デフォルトゲートウェイは1つしか指定できませんよね。
eth0の方にデフォルトゲートウェイの設定を入れていたのでした。

iface wlan0 inet static
 address 192.168.1.5
 netmask 255.255.255.0
 network 192.168.0.0
#gateway 192.168.1.1

でおっけー。
もちろん最後の行は消すべきですが、説明の都合上、コメントにしました。

Raspberry Piのバックアップ(Macでddコマンド使用)

Raspberry Piをシャットダウン。
MacにSDカードを刺してから、

MacBookPro:~ user$ diskutil list
/dev/disk0
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *251.0 GB   disk0
   1:                        EFI EFI                     209.7 MB   disk0s1
   2:                  Apple_HFS Macintosh HD            250.1 GB   disk0s2
   3:                 Apple_Boot Recovery HD             650.0 MB   disk0s3
/dev/disk1
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     FDisk_partition_scheme                        *15.8 GB    disk1
   1:             Windows_FAT_32 boot                    58.7 MB    disk1s1
   2:                      Linux                         15.7 GB    disk1s2
MacBookPro:~ user$ sudo dd bs=1m if=/dev/rdisk1 of=~/Desktop/Pi-backup-YYYYMMDD.img

でおっけー。

Back-up a Raspberry Pi SD card using a Mac
http://smittytone.wordpress.com/2013/09/06/back-up-a-raspberry-pi-sd-card-using-a-mac/

などを参照のこと。

Raspberry Piを無線LANでつなごうとしたらハマった話

ハマりました。
使ったのは以下のUSB無線LAN子機。

さっぱりと結論を書きますと、以下のように設定したら、うまく無線親機に接続でき、DHCPサーバからIPアドレスを取得することができました。ちなみに、親機のセキュリティはWPA2-PSK(AES)、ステルスモードにしています。

auto wlan0
allow-hotplug wlan0
iface wlan0 inet dhcp
wpa-driver wext
wpa-scan-ssid 1
wpa-ssid "SSID"
wpa-psk "PSK"

ポイントは以下の通り。

・/etc/wpa_supplicant/wpa_supplicant.confを使わない。
・/etc/network/interfacesにwpa-driver wextを設定する。

/etc/wpa_supplicant/wpa_supplicant.confを使っていたとき、

DHCPDISCOVER on wlan0 to 255.255.255.255 port 67 interval 3
DHCPDISCOVER on wlan0 to 255.255.255.255 port 67 interval 6
DHCPDISCOVER on wlan0 to 255.255.255.255 port 67 interval 11
DHCPDISCOVER on wlan0 to 255.255.255.255 port 67 interval 18
DHCPDISCOVER on wlan0 to 255.255.255.255 port 67 interval 15
DHCPDISCOVER on wlan0 to 255.255.255.255 port 67 interval 8
No DHCPOFFERS received.
No working leases in persistent database - sleeping.

となってしまい、うまく親機に接続できず、DHCPからアドレスも接続できませんでした。
そんな中、こちらのスレッド「Issue with known working WIFI」に出会い、freezer burnさんの、

Instead of using wpa.conf, try this (directly in /etc/network/interfaces):

auto wlan0
iface wlan0 inet dhcp
wpa-ssid “NETWORK SSID”
wpa-psk “NETWORK PASSWORD”

という書き込みに出会い、/etc/network/intarfacesに設定を書きましたが、それでも接続できませんでした。
ステルスモードにしているので、wpa-scan-ssid 1を追加しましたが、接続できない。
ダメもとで、wpa-driver wextを設定したら、うまくいった次第です。

うーん、ハマった。

SoftEtherでRaspberry Piに通信できない

Raspberry PiでSoftEtherを動かし、L2TP/IPSecで接続しています。

しかし、VPNの接続先となるRaspberry PiのIPアドレスに通信できませんでした。
SoftEtherで家へのVPN接続は出来ておりますし、家のインターネットに出ることもできているのですが、Raspberry Pi自身と通信ができません。これではPiメンテナンスできないし、他のサービスを利用できないし、悩んでおりました。

調べたところ、通信できないのはLinuxカーネルの制約事項とのこと。
商用版であるPacketiX VPNのオンラインマニュアルに書いてありました。引用します。

11.1.2 ローカルブリッジを使用する際に VPN 内部からローカルブリッジに使用している仮想 LAN カードの IP アドレスと通信できない場合
Linux および Solaris オペレーティングシステムでは、仮想 HUB (VPN) の内側からローカルブリッジ先のLANカードから LAN への通信は行うことが出来ますが、ローカルブリッジしている LAN カード自体に対して通信することはできません。これは Linux カーネルの制限事項です。
http://www2.softether.jp/en/vpn2/manual/web/11-1.aspx

うーん、そうなのか・・・。仕様でしたか。
回避策はもう一つLANカードを用意することだそうな。

PiにUSBのWifiカードを搭載するしかないか。今度やってみよう。

Raspberry PiをSoftEtherでVPNサーバにする

Raspberry Pi活用の第一弾として、VPNサーバに仕立てることにしました。

Raspberry Pi

使ったのはSoftEther。SoftEtherが出たときは業界が騒然としたものです。NATやファイアウォール越しにVPN張れてしまうんですからね。それを自宅に入れられる日が来ようとは。うれしいことであります。
SoftEther VPNプロジェクトのページはこちらです。

インストールは、PCリンクスさんのページが参考になります。SoftEtherは、ダウンロードページに行って、URLを確認し、Raspberry Piからwgetでダウンロードします。「SoftEther VPN Server」を選んで、OSやCPUは、OS: Linux, CPU: ARM EABI (32bit)を指定すればOKです。私は参考サイトを見ながら作業しましたが、公式ドキュメントも丁寧にインストールについて解説してくれていますので、ぜひご参考を。

一通りインストールが終わったら、Windowsを起動。VPN管理マネージャをインストールします。参考サイトを参照しつつ設定します。ダイナミックDNSサービスも提供してくれるのがありがたいですね。

設定後、iPhoneをクライアントにしてVPN接続を試みたのですが、つながらず・・・。
/usr/local/vpnserver/server_log配下のログに以下の記述がありました。

2014-03-30 23:42:11.438 L2TP PPP セッション [xxx.xxx.xxx.xxx:1701]: クライアントは認証プロトコルとして “PAP” (Password Authentication Protocol、クリアテキストパスワード認証プロトコル) および MS-CHAP v2 プロトコルの両方を拒否しました。クライアント側の接続設定で PAP または MS-CHAP v2 のいずれかを有効にして再試行してください。

うーん。わからん。えーい、と思って、iPhoneのVPN設定をいったんクリアし、再登録したら、このエラーは出なくなりました。何だったのだろうか・・・。
もう一つ出ていたエラーはこちら。

2014-03-31 00:03:34.846 L2TP PPP セッション [xxx.xxx.xxx.xxx:1701]: DHCP サーバーからの IP アドレスの取得に失敗しました。PPP の通信を受諾するためには DHCP サーバーが必要です。仮想 HUB の Ethernet セグメント上で DHCP サーバーが正しく動作しているかどうか確認してください。DHCP サーバーを用意することができない場合は、仮想 HUB の SecureNAT 機能を用いることもできます。

うーん、自宅ではルータをDHCPサーバにしているんだけどなあ。
これまた悩みましたが、VPN管理マネージャのローカルブリッジ設定がtapになっていました。eth0にしたところ、つながるようになりました(iPhoneにローカルのIPアドレスが割り振られました)。

iPhoneにはAGEphoneというアプリを入れていまして、ひかり電話ルータにレジストしていました。なので、iPhoneでVPN接続で自宅につないで、AGEphoneを立ち上げて、電話をかけると、自宅から電話したこととなり、電話した先で自宅の番号が表示されるのです。おー、ワンダフル。自宅にVPNでつないで何をするかは、ほかにも考えてみることにしよう。

Raspberry Pi購入とセットアップ

Raspberry Piを買ってしまった。
ま、ほら、消費増税前だし。

セットアップ!
私はCentOSが好きなのです。CentOSのロングサポートはすごいと思います。が、Raspberry PiにインストールしたのはRaspbian。情報が多かったので、Raspbianにしました。

インストールはいろいろなサイトに書いてあるので、省略します。
MacではddコマンドでSDカードにインストールすると良いでしょう。

HDMIケーブルでテレビにつないで、Desktopを試しました。軽量と言われるブラウザMidoriも、重い・・・。w3mとかにするべきかも。そういえば、前の会社でよくw3m使ってたなあ。上司に遊んでいるのがバレないのがいいんですよね(おいおい)。さておき、w3m使うんじゃあ、別にXでログインする必要も無いか。

ということで、使い道としては、サーバ用途だろうと思います。消費電力も少ないし。ホームサーバとして今後は使っていこうかな。何に使うかなあ。悩むなあ。まずはVPNサーバかなあ。

自慢の腕時計Olient Starと比べてみました。小さい。かわいい。無線LANにするためのUSBのアダプタも買ったのですが、電力消費が多くなるそうなので、しばらくは有線で使ってみます。動かさないしね。

Raspberry Pi

RubyでTwilio使って電話してみた

池澤あやかさんがTwilio使って電話しているのを見まして(MacPeople)、面白そうだなあと思ったので、私もやってみました。前に「Zabbix Conference Japan 2013」で「Zabbix × Twilio でサーバー監視電話連絡の自動化」という発表を聞いたことがあって、Twilioが気になってはいたんですよね。けけけ、けっして、池澤あやかさんがやってたから真似した訳ではないです。

そもそもTwilioとは何か?
公式ページから引用しましょう。

「Twilio を使えば、標準的なウェブ言語を使用して、簡単に、インテリジェントな音声通話アプリケーションを構築できます。 Twilio は従来のテレコム サービスの世界と IPコミュニケーションの橋渡しをします。」

自由かつオープンにテレコムサービスを利用できる。素晴らしいです。本来はNGN(Next Generation Network)にこのような役割が期待されていたと思うんですけどね。残念ながら、いまだNGNは自由でもオープンでもないですからねえ。残念なことです。

利用するにはまず登録が必要です。Twilioのサイトに行く。
日本だとKDDIのグループ会社であるKDDIウェブコミュニケーションズが事業として運営しているようです。うーん、通信キャリアの色が付くのは微妙だなあ。
ともあれ、登録。利用登録ボタンから登録するだけです。テストコールもかかってきます。トライアル中は無料。ただ、いつトライアル終わるのかが不明瞭です。使用量が一定量に達したら使えなくなるみたいです。

Rubyから電話をかける方法は、Twilioのブログに載っていますし、ログイン後の「Api Explorer」というコーナーでサンプルスクリプトを見ることもできます。サンプルは、なんと、Curl/Ruby/PHP/Python/Node.js/Java/C#が用意されています。好きな言語で書けるのはいいですなあ。

Twilioはマークアップ言語TwiMLを使います。TwiMLでTwilioにしゃべらす内容などを書きます。TwiMLはTwilioが読める場所に無いといけません。つまりインターネット公開する必要があります。私は自分のWebサーバを使いました。持っていない人は、TwiML用のホスティングサービスなどがありますので、そちらを使うといいかも。

日本語も話せますが、とりあえず英語にしてみました。

このwelcome.xmlをWebサーバに置きます。
ここでは仮にhttp://hogehoge/welcome.xmlとしておきます。

続いて、Rubyでコードを書きます。Ruby 2.1.1を使用。
まずはtwilio-rubyを入れます。

# gem install twilio-ruby

サンプルを参考に(というかそのまま)書く。

これで準備完了。

$ ruby call.rb

と実行すると、電話がかかってきました!
試用期間中はTwilioの宣伝文言が入りますが、その後、男性の声で「Welcome to Twilio!」と話してくれました。

TwilioのWebサイトを見てみると、IVRとかも作れちゃうようです。
これは色々な可能性があるなあ。

さくらのVPSにRuby 2.1.1を入れた

さくらのVPS(CentOS 6.5)にRuby 2.1.1を入れました。
こちらのサイトを参考にさせていただきました。

普通にyumでインストールしようとしたのですが、

# yum install ruby
================================================================================
 Package                Arch         Version                Repository     Size
================================================================================
Installing:
 ruby                   x86_64       1.8.7.352-13.el6       updates       534 k

なー、古い!

インストール方法をいろいろと調べたところ、Ruby Version Manager(RVM)というものを使うべきだそうな。本家でも紹介されていました。

事前準備。

# yum install gcc-c++ patch readline readline-devel zlib zlib-devel libffi-devel openssl-devel make bzip2 autoconf automake libtool bison gdbm-devel tcl-devel tk-devel libxslt-devel libxml2-devel
# yum install --enablerepo=epel libyaml-devel

RVMのインストール。私しか使わないサーバなので、ユーザ毎に環境を作る必要性はなく、rootでインストールすることにしました。/usr/local配下にインストールされます。

# curl -L https://get.rvm.io | bash -s stable
...
  * First you need to add all users that will be using rvm to 'rvm' group,
    and logout - login again, anyone using rvm will be operating with `umask u=rwx,g=rwx,o=rx`.

  * To start using RVM you need to run `source /etc/profile.d/rvm.sh`
    in all your open shell windows, in rare cases you need to reopen all shell windows.

上記のメッセージが出ますが、rootで使うだけだったら、いったんログアウトしてログインすればオッケーみたい。

# rvm -v
rvm 1.25.22 (stable) by Wayne E. Seguin <wayneeseguin@gmail.com>, Michal Papis <mpapis@gmail.com> [https://rvm.io/]

# rvm list known
# MRI Rubies
[ruby-]1.8.6[-p420]
[ruby-]1.8.7[-p374]
[ruby-]1.9.1[-p431]
[ruby-]1.9.2[-p320]
[ruby-]1.9.3[-p545]
[ruby-]2.0.0-p353
[ruby-]2.0.0[-p451]
[ruby-]2.1[.1]
[ruby-]2.1-head
ruby-head

インストール。コンパイルするので時間がかかります。

# rvm install 2.1.1
# rvm docs generate-ri

無事にインストールできました。

# ruby -v
ruby 2.1.1p76 (2014-02-24 revision 45161) [x86_64-linux]

# irb
2.1.1 :001 > puts "Hello! World!"
Hello! World!
 => nil 

一般ユーザでもRubyを使う場合は、source /etc/profile.d/rvm.shを実行します。

$ source /etc/profile.d/rvm.sh

$ ruby -v
ruby 2.1.1p76 (2014-02-24 revision 45161) [x86_64-linux]

$ irb
2.1.1 :001 > puts "Hello! World!"
Hello! World!
 => nil 

終わり。