Linux上で動くSkype用のbotを作る方法
はじめに
以前、知人のやっているBeProudという会社を手伝ったのですが、BeProudでは、エンジニアの主要なコミュニケーション手段としてSkypeが使われていました。当時、趣味でたまたまSkypeのAPIについて調べていたので、悪戯っ気を出して、開発環境に即席でSkype APIを使ったbotを設置してみたところ、思いのほか好評を博し、いまやインフラと言っても過言ではない存在 *1 *2と化したようです。
まあそんな状況を眺めつつ、自宅のサーバにSkype botを設置して、お気に入りのSkypeチャットにもbotを加えてみたところ、これも結構好評だったので、興味ありそうな人向けに作成方法をまとめることにします。
Skype Public API
Skype Public APIとは、Skypeを外部からコントロールするためのインターフェイスです。
Skype Public APIをLinux上で簡単に利用するには、スクリプト言語バインディングであるSkype4PyかPHP Skype API wrapperがおすすめですが、ここでは準公式な位置付けでAPIが充実しているSkype4Pyを使います。
Skype4Pyのインストール
Ubuntuであれば、multiverseに「python-skype」というパッケージ名で収録されているので、aptitude一発でインストールできます。OpenSUSEにも「python-Skype4Py」というパッケージがあるようです。
他のディストリビューションでも、setuptools経由で簡単にインストールできます。
$ easy_install Skype4Py
Skype4Pyを試す
最初は肩ならしにPythonのインタラクティブモードでSkype4Pyを使ってみることにします。
手元にまずXが立ち上がっていてSkypeが使える環境を用意してください。また、必要に応じてbot用のアカウントも取得しておきます。
Skypeを起動し、ユーザ名パスワードを入力して、サインインしたところで、ターミナルを開き、Pythonを立ち上げます。
Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41) [GCC 4.4.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>>
Skype4Pyをインポートして、Skypeオブジェクトを生成し、Attach()メソッドを呼びます。
>>> import Skype4Py
>>> skype = Skype4Py.Skype()
>>> skype.Attach()
最初にこの操作を行うとき、SkypeはAPIを利用するアプリケーションを信用してもよいかを確認するプロンプトを出し、APIの呼び出し側はこのプロンプトに対して選択が行われるまでブロックします。ここでは、もちろん[はい]を選択します。
選択が終わると、再びPythonのインタラクティブモードに処理が戻ります。
手始めにコンタクト一覧を取得し、その名前を表示してみましょう。
>>> skype.Friends (<Skype4Py.user.IUser object at 0x7f6cb3876890 with Handle=u'echo123'>, <Skype4Py.user.IUser object at 0x7f6cb38766d0 with Handle=u'moriyoshi_koizumi_2nd'>) >>> for user in skype.Friends: print user.Handle, user.FullName ... echo123 Skype Test Call moriyoshi_koizumi_2nd moriyoshi
次に、Skype Test User (echo123) に向かって何かメッセージを送信してみましょう。
>>> chat = skype.CreateChatWith("echo123") >>> chat.SendMessage("test") <Skype4Py.chat.IChatMessage object at 0x7f67bde5c690 with Id=82> >>> chat.SendMessage(u"テスト") <Skype4Py.chat.IChatMessage object at 0x7f67bde5c950 with Id=85>
チャットウィンドウを開くと、echo123に対してメッセージが送られ、同じ内容がecho123によってエコーバックされていることが確認できると思います。
では、メッセージ受信などのイベントが発生したときに特定の処理を行うイベントハンドラを試してみます。
>>> def handler(msg, event): ... print event ... >>> skype.OnMessageStatus = handler >>> chat.SendMessage("???") <Skype4Py.chat.IChatMessage object at 0x7f67bde5c890 with Id=125> >>> SENDING SENT RECEIVED >>> def handler(msg, event): ... if event == u"RECEIVED": ... print msg.Body ... >>> skype.OnMessageStatus = handler >>> chat.SendMessage(u"\(^o^)/") <Skype4Py.chat.IChatMessage object at 0x7f67bc484190 with Id=138> >>> tof-bot 2010.09.26 02:33:10 \(^o^)/
以上を踏まえて、「やっぱり」というメッセージを受信すると「猫が好き」と返すbotを書いてみましょう。
# encoding: utf-8 import Skype4Py import time def handler(msg, event): if event == u"RECEIVED": if msg.Body == u"やっぱり": msg.Chat.SendMessage(u"猫が好き") def main(): skype = Skype4Py.Skype() skype.OnMessageStatus = handler skype.Attach() # イベントハンドラは別スレッドにて実行されるので、 # 本スレッドではひたすらsleepしてスクリプトが終了しないようにしておく。 while True: time.sleep(1) if __name__ == "__main__": main()
これを
$ python yappari.py
のように起動して、別マシンから「やっぱり」を送信してみましょう。次のスクリーンショットのようになれば成功です。
例がすみませんね昭和で。
と、まあ、どうでしょう、たったこれだけのAPIを覚えれば、botが作れてしまうということがわかったのではないかと思います。
Skypeをデーモン化する
と、これまでSkypeが予め立ち上げられたGUI環境上でSkype Public APIを利用する方法を説明しましたが、これではサーバに設置するのがやや面倒です。非GUI環境でSkypeを起動し、それを利用してbotを書くことはできないものでしょうか。そのような時に威力を発揮するのがXvfbです。Xvfbとは、その名前のvfbがvirtual framebufferから由来していることからもわかるように、フレームバッファ (画面への出力内容を蓄えておくメモリ) として、ビデオカードを使うのではなく、RAM上に確保した仮想フレームバッファを使うというXの実装です。これを使うことで、CUI環境下でXサーバを簡便に扱うことができます。
Xvfbの起動方法は簡単で、Xのディスプレイ番号を引数に与えるだけです。
$ Xvfb :20
デフォルトではスクリーンサイズが1280x1024でピクセルの深さが24となります。これを変更するときは
$ Xvfb :20 -screen 0 800x600x8
のように-screen
オプションを指定します。一般に、画面サイズが小さければ小さいほど、ピクセルの深さが小さければ小さいほど、必要なフレームバッファのサイズが小さくなり、省メモリとなります。Skypeはかろうじて画面サイズ800x600、ピクセルの深さ8で動作させることができます。
Xvfb上で動くアプリケーションを外部から操作するには、VNCを利用します。VNCサーバの実装はいろいろありますが、コマンドラインから手軽に使える、各種ディストリビューションでパッケージが用意されているという点でx11vncをおすすめします。
x11vnc の利用方法も簡単で、
$ x11vnc -display :20
のようにXのディスプレイ番号を与えるだけです。デフォルトの接続ポート番号は5900番ですが、これを変更するには-rfbport
オプションを指定します。
と、能書きはここまでにして、CentOSなどRedHat系のディストリビューションやDebian系のディストリビューションで動くinitスクリプトをとりあえず貼ります。sudoを使っているので、デフォルトでインストールされていない環境にはあらかじめインストールしておいてください。
このスクリプトはDAEMON_USERで指定されたユーザでXvfbおよびskypeを起動または停止します。/etc/init.d以下にinitスクリプトとして設置して以下のように使います。
- 起動
$ /etc/init.d/launch-skype.sh start
- 停止
$ /etc/init.d/launch-skype.sh stop
initスクリプトがOS起動時に自動的に呼び出されるように設定する方法は、各ディストリビューションのドキュメントを参照してください。
起動ログは/var/log/skype以下に、xauth cookieは/var/run/skype以下に保存するので、あらかじめこれらのディレクトリをDAEMON_USERで読み書きできるように作成しておく必要があります。
Skypeでサインインするユーザ名とパスワードはそれぞれUSERNAMEとPASSWORDで指定しますので、ここを適宜編集してください。
XSERVERNUMはXvfbのディスプレイ番号を指定します。
DBPATHはSkypeが設定情報などを書き込む先のディレクトリです。これもDAEMON_USERで読み書きできるようにしておいてください。
このスクリプトで起動されたskypeの環境にx11vncでアクセスするには、次のようにします。
$ sudo -u skype x11vnc -xauth /var/run/skype/Xauthority
初期起動時の設定
初期起動時、VNCクライアントで接続すると次のような画面となっているはずです。
ここで[I agree]を選択すると、自動的にログインが開始されます。
次に、Skype4Pyをインタラクティブモードで起動して、Skype Public APIへのアクセス許可を設定します。
x11vncの起動で指定したように、envコマンドなどでDISPLAY環境変数とXAUTHORITY環境変数をあらかじめ設定した状態でpythonを起動する必要があります。
$ env DISPLAY=:20 XAUTHORITY=/var/run/skype/Xauthority python Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41) [GCC 4.4.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import Skype4Py >>> skype = Skype4Py.Skype() >>> skype.Attach()
前節で説明したように一旦Attach()でブロックした状態になりますので、ここでVNCを使ってプロンプトを操作します。
必ず[Remember this selection]にチェックを入れてから[Yes]を選択してください。でないと、botを起動するたび、毎度この操作をVNCから行わなくてはならなくなってしまいます。
以上で初期起動時の設定は完了です。
おわりに
Skype public APIやその利用方法について解説している日本語の情報があまりないため、敷居が高くなっていた印象を受けますが、この記事が少しでも足しになれば幸いです。
注意点として、Skypeは基本的にP2Pアプリケーションなので、一般的なチャットクライアントとは違い、勝手にさまざまなノードと接続を開始してしまいます。サーバで運用するときは、そのサーバの属するネットワークの管理者に管理ポリシーなどを確認したほうがよいでしょう。
ちなみに、今のところ、さくらのVPS、EC2でSkypeを動かすことができています。
*1:http://twitter.com/shin_no_suke/status/25658845058
*2:http://www.slideshare.net/bpstudy/bpstudy36-beproudbot-5319457