先日横浜みなとみらいのパシフィコ横浜においてGoogle Developer Day 2009(GDD)が開催されました。会場に足を運び、実際にGoogle Waveが動作する様子を目にした方も多いのではないでしょうか。
嬉しいことにその基調講演の中でGDD参加者には数週間以内にGoogle Waveのアカウントが発行されるという発表がありました。GDDに参加してもうしばらくすればアカウントが手に入るという予定の方は、ぜひこの連載で予習して数週間後に備えていただければ幸いです。
また、残念ながらGDDに参加できずGoogle Waveアカウントの発行が遅れそうな方も、この連載でWave API利用の雰囲気だけでも感じて頂ければと思います。
ロボットとは
Google Waveでいう「ロボット」とは、Wave内で参加者と同じように振る舞えるWebサーバー上で動くプログラムのことです。
ロボットがどういうものか理解するには、実際にロボットを使ってみるのが早いでしょう。ロボットをWaveに追加するには、参加者をWaveに追加するときと同じようにWave ヘッダの「+Add」ボタンを押下したあとWave IDを指定します。
図1 ロボットのWave IDを入力
図2 ロボットが追加された
ここではGoogle API Expertの松尾さんが作ったKayという赤ん坊ロボットを試してみることにします。アイコンからもプロフィールからも通常の参加者と全く区別がつきませんが(実際のところ作成者がそれと分かるように作らない限り、現時点ではロボットと参加者を区別する方法はありません) 、実は彼はロボットで私の発言を単純におうむ返ししようとします。それではなにか話しかけてみましょう。
図3 Kay
残念ながらKayは赤ん坊なので、まだうまく私の発言を真似できないようですね(実を言えばKayは発言を少しランダマイズして返すロボットです) 。このようにWave上で発生したイベントを受け取って、返事を返す、文章を書き変える、参加者を追加する、など様々な処理をサーバー側からWaveに対して施すものがWaveロボットです。
ロボットが処理するイベント
ロボットはWave上でイベントが発生したときに、そのイベント内容を伴うPOSTリクエストで呼び出されます。ロボットの処理するイベントについて説明する前に、まずはその発生元となるGoogle Waveのデータモデルについて説明しましょう。
Wave グローバルで一意なIDを持つエンティティ。複数のWaveletからなる。Playback機構はこの内部で動作する
Wavelet Waveに含まれ、Wave内で一意なIDを持つエンティティ。参加者の一覧と複数のドキュメントからなる。同時編集の制御はこのエンティティ内で行われる
Document Waveletに含まれ、Wavelet内で一意なIDを持つエンティティ。一つのXMLドキュメントと、それとは独立したアノテーションの組からなる。アノテーションはXMLドキュメント内の特定の場所を指し、テキストフォーマット、スペルサジェスト、ハイパーリンクなどを表す。またドキュメントはWavelet内でツリー構造になっている
Blip ドキュメントの一種でリッチテキスト形式のもの
上記のエンティティのうちイベントを発生するのはWaveを除く3つです。
そこから発生するイベントの一覧は、現在のところ以下のようになっています。ただし、イベントは今後もまだまだ変更される可能性が高いのでご注意ください(実際、Google I/O翌日に行われたHackathonの時とはすでに少し変わっています) 。
wavelet_blip_created
wavelet内でblipが新しく作成された場合に発生するイベント
wavelet_participants_changed
参加者(ロボットも含む)がwaveletに追加またはwaveから削除された場合に発生するイベント
wavelet_title_changed
waveletのタイトルが変更された場合に発生するイベント
blip_contributors_changed
blipの編集者が変更された場合に発生するイベント
blip_deleted
blipがwaveletから削除された場合に発生するイベント
blip_submitted
blipが確定された場合に発生するイベント。このイベントはユーザーがDoneボタンをクリックするか、他のblipにフォーカスを移動したときに一度だけ発生します
document_changed
blipにコンテンツが追加された場合にさまざまな間隔で発生するイベント
もちろんロボットがこれらのイベント全てに反応するわけではありません。ロボットが反応するイベントはロボットと同じサーバー上に置かれたXMLファイル(capabilities.xml)で指定されていて、例えばKayの場合は以下のようになっています。
リスト1 capabilities.xml
<?xml version="1.0"?>
<w:robot xmlns:w="http://wave.google.com/extensions/robots/1.0" >
<w:version >1.0</w:version >
<w:capabilities >
<w:capability name="BLIP_SUBMITTED" />
<w:capability name="WAVELET_SELF_ADDED" />
</w:capabilities >
<w:profile name="Kay" imageurl="http://nikkeisoftware.appspot.com/icon.png"
profileurl="http://nikkeisoftware.appspot.com/_wave/robot/profile" />
</w:robot >
二つの<capability>タグで指定されているイベント、BLIP_SUBMITTEDとWAVELET_SELF_ADDEDがKayの受け取るするイベントです。
一点注意事項ですが、ロボットは自分自身が原因となったイベントは受け取りません。もしこの制約がなければKayが赤ちゃん言葉で真似した発言をWave に追加したときにもBLIP_SUBMITTEDは発生しますので、自分の発言を再び真似することになり無限ループに陥ってしまいます。
ロボットを作成
それでは実際にAPIを使用して簡単なロボットを作ってみましょう。
ただしここで一つ、ロボットAPIには大きな制限があることを白状しなければいけません。現在のところロボットはGoogle App Engine (GAE)を使ってappspot.com上にデプロイする必要があります 。この制限は一時的なものでいずれ撤廃される予定ですが、それまではWaveロボットの開発にはGAEアカウントが必要です。さらにそれに伴う制約として、ロボットを開発するために使用できる言語はGAEで使用できる言語と同じ、つまりPythonとJavaだけです。
本稿ではサンプルは全てPythonで記述しますので、Java版を利用する場合はGoogleのドキュメント を参考にしてください。
またGAEは本連載の主題ではないためGAE開発環境の構築についても省略しています。この先に進む前に「一番かんたんなGoogle App Engineの使い方 」などを参考にしてGAEの開発環境を準備しておいてください。
それでは以下、GAEの開発環境はすでにあるものとして、簡単な挨拶ロボットの開発を進めましょう。
ロボット名を取得
WaveロボットはグローバルなURIを持つ必要があり、ローカルな環境では動作を確認できません。つまりWaveロボットはGAEの本番環境で動作させる必要があります。GAEのアプリケーションIDは早い者勝ちですから、まず最初にロボットの名前を決めて本番環境用のGAEのアプリケーションIDを取得してしまいましょう[1] 。
アプリケーションIDを取得するためにはGAE管理コンソール にアクセスします。
[1] KayのようにGAEのアプリケーションID(nikkeisoftware)とロボット名(Kay)を無関係にすることも可能です。ただ、ロボットを初めて呼び出す際にアプリケーションIDが必要ですので、混乱を招かないためにも、一致させておくことをお勧めします。
図4 GAE管理画面
「Create an Application」ボタンをクリックすると以下のような画面が開きます。使用したいアプリケーションID(ロボット名)と、任意のタイトル(Waveロボットでは使われません)を入力し、「 Save」ボタンを押下してください。アプリケーションIDが重複していなければ登録に成功します(これ以降、ロボット名は仮にgihyo-robotとしますが、みなさんが実際に試すときには適宜読み替えてください) 。
図5 アプリケーション情報入力
Python Client Libraryをインストール
ロボットの名前が決まりましたので、ここからは実際にGAEを使用してロボットの作成に移ります。まずはロボット開発用のディレクトリを作成して、Python版のClient Libraryをダウンロードします。
$ mkdir gihyo-robot
$ cd gihyo-robot
$ svn checkout http://wave-robot-python-client.googlecode.com/svn/trunk/src/waveapi waveapi
アプリケーションの設定ファイルを作成
GAEの設定ファイルであるapp.yamlファイルを作成して、ロボットアプリケーションの設定を記述します。
リスト2 app.yaml
application: gihyo-robot
version: 1
runtime: python
api_version: 1
handlers:
- url: /image
static_dir: image
- url: /_wave/.*
script: gihyo-robot .py
内容は上記と同じで構いませんが、赤字の部分だけ自身のアプリケーションIDと置き換えてください。Waveロボットが対応すべきURLは下の三つですが、app.yamlの最後の二行でそれらを全てgihyo-robot.pyで処理するよう指示しています。
http://applicationURL/_wave/capabilities.xml
http://applicationURL/_wave/robot/jsonrpc
http://applicationURL/_wave/robot/profile
したがってPythonの場合は、capabilities.xmlもロボットが生成します。XMLファイルを自分で記述する必要はありません(Javaで記述する場合はロボット本体の記述とは別にcapabilities.xmlファイルを自身で用意してください) 。
ロボット本体のPythonコードを記述
それでは、ロボット自身がWaveに追加されたときに参加者に挨拶をする簡単なロボットを作成してみます。
リスト4 gihyo-robot.py
from waveapi import events
from waveapi import model
from waveapi import robot
# ロボット自身が追加されたときに呼び出される関数を定義
def OnSelfAdded(properties, context):
# ルートのWaveletをコンテキストから取得
root_wavelet = context.GetRootWavelet()
# ルートWaveletに新しくBlipを作成して挨拶を書き込む
root_wavelet.CreateBlip().GetDocument().SetText("Hi, I'm Gihyo Robo. ")
# 最初に実行される処理
if __name__ == '__main__':
# 名前、バージョン、アイコン、プロフィールURLを指定してロボットを作成
gihyo = robot.Robot('Gihyo Robo', version='1.0',
image_url='http://gihyo-robot.appspot.com/image/icon.png ',
profile_url='http://gihyo-robot.appspot.com/ ')
# WAVELET_SELF_ADDEDイベントのハンドラとしてOnSelfAdded関数を登録
gihyo.RegisterHandler(events.WAVELET_SELF_ADDED, OnSelfAdded)
# ロボットの処理を開始
gihyo.Run()
処理の詳細についてはソースコード内のコメントを読んでいただくとして、おおまかな流れは以下のようになります。
イベントのハンドラ関数を定義
各種パラメータを指定してロボットをインスタンス化
ロボットインスタンスに処理するイベントとそのハンドラを登録
ロボットを起動
とりたてて難しいところはありませんが、OnSelfAdded関数でRobot APIを利用してクライアント上のWavelet, Blip, Documentにそのままアクセスできている(ように見える)ことに注目してください。
ロボットのデプロイ
これで必要なファイルの作成が終わりました。この時点でディレクトリの構成は次のようになっています。
gihyo-robot /
image /
waveapi /
app.yaml
gihyo-robot.py
GAEを利用しているため、デプロイは非常に簡単です。ターミナルでロボットと同じディレクトリ(gihyo-robot/)に移動し、以下のコマンドを実行しましょう。
$ appcfg.py update .
初めてのデプロイでは、GMailのアカウントとパスワードを尋ねられます。GAEのアプリケーションIDを取得したときに使用したアカウントとパスワードを入力してください。デプロイはこれで終わりです。
ロボットの動作確認
ロボットを動かしてみる前に、デプロイの確認の意味も込めてcapabilities.xmlが正しく表示されるかどうかを確認してみます。ブラウザの URL欄に http://gihyo-robot.appspot.com/_wave/capabilities.xml と打ち込んでください。
リスト4 http://gihyo-robot.appspot.com/_wave/capabilities.xml
<?xml version="1.0"?>
<w:robot xmlns:w="http://wave.google.com/extensions/robots/1.0" >
<w:version >1.0</w:version >
<w:capabilities >
<w:capability name="WAVELET_SELF_ADDED" />
</w:capabilities >
<w:profile name="Gihyo Robo"
imageurl="http://wave-instruments.appspot.com/image/icon.png"
profileurl="http://wave-instruments.appspot.com/" />
</w:robot >
<w:profile>の属性はgihyo-robot.py内でRobotのコンストラクタに渡した値になっていますし、<w:capability>タグは一つだけで、そのname属性もRobotのインスタンスにRegisterHandlerしたWAVELET_SELF_ADDEDになっています。どうやら正しくデプロイできているようですね。
では、実際にロボットの動きを見てみましょう。ロボットを会話に参加させるためにはロボットのWave IDが必要ですが、GAEにデプロイしたロボットの場合はWave IDは [アプリケーションID]@appspot.com になります。Waveヘッダの「+Add」ボタンを押下したあと、gihyo-robot@appspot.comと入力して、今回作成したばかりのロボットを選択してください。
図6 ロボットをWaveの参加者に追加
gihyo-robot.pyに間違いがなければ、以下のように新しくBlipが追加され、メッセージが表示されます。
図7 メッセージが表示
ロボットの動きに不具合があったときには、App Engineコンソール でログを確認できます。ログレベルをInfoに設定すればJSON形式でやり取りされているデータを直接確認できますので、活用してください。
図8 App Engineコンソール
まとめ
今回はロボットAPIについて説明しました。ここで作成したロボットは、たった一つのイベントしか扱わず、反応も固定のメッセージが書き込まれたBlipを追加するだけという非常に簡単なものでしたが、開発の流れは掴んでいただけたのではないかと思います。より複雑なロボットのサンプルはWaveドキュメントのサンプルページ にありますので、本格的なロボットを作るときには先に見ておくようにしてください。
ロボットは処理の流れが単純で、簡単なものでもアイデア次第でWave上で面白い反応を返すことができますから、GAEの入門としても非常におすすめです。GAEを使ったことがないから、Pythonを知らないから、などと変に尻込みせず、本連載を参考に気楽に試してみてください。
Wave自体を拡張するExtensionsについては今回で終了です。次回はWaveの機能をWaveの外で利用するEmbed APIについて説明します。お楽しみに。