Linux上で動くSkype用のbotを作る方法

はじめに

以前、知人のやっているBeProudという会社を手伝ったのですが、BeProudでは、エンジニアの主要なコミュニケーション手段としてSkypeが使われていました。当時、趣味でたまたまSkypeAPIについて調べていたので、悪戯っ気を出して、開発環境に即席でSkype APIを使ったbotを設置してみたところ、思いのほか好評を博し、いまやインフラと言っても過言ではない存在 *1 *2と化したようです。

まあそんな状況を眺めつつ、自宅のサーバにSkype botを設置して、お気に入りのSkypeチャットにもbotを加えてみたところ、これも結構好評だったので、興味ありそうな人向けに作成方法をまとめることにします。

Skype Public API

Skype Public APIとは、Skypeを外部からコントロールするためのインターフェイスです。

Skype Public APILinux上で簡単に利用するには、スクリプト言語バインディングであるSkype4PyPHP 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を立ち上げます。
f:id:moriyoshi:20100926102728p:image

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()

最初にこの操作を行うとき、SkypeAPIを利用するアプリケーションを信用してもよいかを確認するプロンプトを出し、APIの呼び出し側はこのプロンプトに対して選択が行われるまでブロックします。ここでは、もちろん[はい]を選択します。
f:id:moriyoshi:20100926110238p:image

選択が終わると、再び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によってエコーバックされていることが確認できると思います。
f:id:moriyoshi:20100926112225p:image

では、メッセージ受信などのイベントが発生したときに特定の処理を行うイベントハンドラを試してみます。

>>> 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

のように起動して、別マシンから「やっぱり」を送信してみましょう。次のスクリーンショットのようになれば成功です。

f:id:moriyoshi:20100926122004p:image

例がすみませんね昭和で。

と、まあ、どうでしょう、たったこれだけの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クライアントで接続すると次のような画面となっているはずです。

f:id:moriyoshi:20100927005410p:image

ここで[I agree]を選択すると、自動的にログインが開始されます。

f:id:moriyoshi:20100927005411p:image
f:id:moriyoshi:20100927005412p:image

次に、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を使ってプロンプトを操作します。
f:id:moriyoshi:20100927010152p:image

必ず[Remember this selection]にチェックを入れてから[Yes]を選択してください。でないと、botを起動するたび、毎度この操作をVNCから行わなくてはならなくなってしまいます。

以上で初期起動時の設定は完了です。

botを動かす

上の初期起動時の設定に出てきたコマンドラインをそのまま利用します。

$ env DISPLAY=:20 XAUTHORITY=/var/run/skype/Xauthority python bot.py

のようにして起動してください。

おわりに

Skype public APIやその利用方法について解説している日本語の情報があまりないため、敷居が高くなっていた印象を受けますが、この記事が少しでも足しになれば幸いです。

注意点として、Skypeは基本的にP2Pアプリケーションなので、一般的なチャットクライアントとは違い、勝手にさまざまなノードと接続を開始してしまいます。サーバで運用するときは、そのサーバの属するネットワークの管理者に管理ポリシーなどを確認したほうがよいでしょう。

ちなみに、今のところ、さくらのVPS、EC2でSkypeを動かすことができています。

あわせて読みたい:
PythonでSkypeにロギングする

*1:http://twitter.com/shin_no_suke/status/25658845058

*2:http://www.slideshare.net/bpstudy/bpstudy36-beproudbot-5319457