2010年4月14日水曜日

一つのGoogle App Engineアプリケーションで複数のWaveボットを作成する

このブログ記事をはてなブックマークに追加

Google App Engine (GAE)では現在10個までのアプリケーションを登録することができる。そして、GAEを利用してGoogle Waveのボットを作成することができる。しかし、多種多様な利用方法が存在するWaveボットをGAEアプリケーションとして登録していてはすぐに上限に達してしまうだろう。そこで、一つのGAEアプリケーションで複数のWaveボットを作成するためのいくつかの方法を利用することになる。サブドメインを利用する方法が一般的だろうか。

しばらく前からサブドメインを利用して複数のWaveボットを作成しようと思っていたのだが、ちょっと面倒に思えて手を付けていなかった。そうしたら、technohippy氏によるappengine_multi_robot_runnerというライブラリが公開され、それがとても便利そうだったので簡単なWaveボットを作成してみた。ただし、Google Wave Robots API v2が3月30日にアップデートされて実装の一部が変更されたことにより、appengine_multi_robot_runner.pyがそのままでは動作しなかったので該当箇所を修正した。それについては最後に記述しておく。

追記(2010/4/15): 現在、appengine_multi_robot_runnerは最新版に更新されているので、後述の修正は必要無くなっている。

作成したのはインチキ日本語ボット(fake-japanese.robotic-wave@appspot.com)とインチキ英語ボット(fake-english.robotic-wave@appspot.com)の二つのWaveボットだ。インチキ日本語ボットでは、書き込んだ日本語を一度Googleにより英語に翻訳し、それを再度日本語に翻訳し直して表示する。インチキ英語ボットは、日本語を英語に翻訳したものを表示する。インチキと書いたが、特にでたらめにするために細工しているわけではない。現在の翻訳技術ではインチキっぽく見えてしまうというだけである。因みに、これらのボットのアイコン画像はAtnet Japan!を利用させてもらった。

どのように動作するか、以下の例文を実際に入力して動作を見てみよう。

「花便り」
「ここ数日は暖かな日が続いていますが、その後いかがお過ごしでしょうか。」
「待ちに待った桜の花もそろそろ咲き始めております。近いうちにお花見にお誘いしようと思っておりますが、ご都合はいかがでしょうか。」

インチキ日本語ボット(fake-japanese.robotic-wave@appspot.com)では以下の通り。


インチキ英語ボット(fake-english.robotic-wave@appspot.com)では以下の通り。


後述のソースコードを見てもらっても分かるように、この程度の内容であれば非常に短いコードで済んでしまう。Waveボットは一般的なネットボットよりもインタラクティブに利用でき、効果も大きいので、このように簡単に作成できることは非常に重要だと思う。簡単に作成できると言えば、technohippy氏によるas-a-robot@appspot.comを利用することで、GAEを意識せずにGoogle Wave上で簡単にWaveボットを作成することができる。

以下にWaveボットのソースコードを示す。

robotic-wave.py

#!/usr/bin/env python # -*- coding: utf-8 -*- """robotic-wave.py by nox, 2010.4.14""" import urllib, urllib2 from waveapi import robot from waveapi import events from waveapi import simplejson import appengine_multi_robot_runner def Translate(text, from_lang, to_lang): url = "http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=%s&langpair=%s%%7C%s" % (urllib.quote(text.encode("utf-8")), from_lang, to_lang) data = simplejson.loads(urllib2.urlopen(url).read()) return data["responseData"]["translatedText"] class FakeJapaneseRobot(robot.Robot): def __init__(self): robot.Robot.__init__(self, "FakeJapanese", image_url="http://robotic-wave.appspot.com/assets/edodaikan03i.gif", profile_url="http://robotic-wave.appspot.com/") self.register_handler(events.BlipSubmitted, self.OnBlipSubmitted) def OnBlipSubmitted(self, event, wavelet): blp = event.blip text = blp.text.strip() text = Translate(Translate(text, "ja", "en"), "en", "ja") blp.range(1, len(blp.text)).replace(text.encode("utf-8")) class FakeEnglishRobot(robot.Robot): def __init__(self): robot.Robot.__init__(self, "FakeEnglish", image_url="http://robotic-wave.appspot.com/assets/202aschwarzenegger.gif", profile_url="http://robotic-wave.appspot.com/") self.register_handler(events.BlipSubmitted, self.OnBlipSubmitted) def OnBlipSubmitted(self, event, wavelet): blp = event.blip text = blp.text.strip() text = Translate(text, "ja", "en") blp.range(1, len(blp.text)).replace(text.encode("utf-8")) def main(): appengine_multi_robot_runner.compound_and_run([ ("fake-japanese", FakeJapaneseRobot()), ("fake-english", FakeEnglishRobot()) ]) if __name__ == "__main__": main()

app.yaml

application: robotic-wave version: 1 runtime: python api_version: 1 handlers: - url: /_wave/(.*) script: robotic-wave.py - url: /assets static_dir: assets - url: /(.*) script: index.py

Google Wave Robots API v2が3月30日にアップデートされ実装が一部変更された。そのため、appengine_multi_robot_runner.pyが動作しなくなったのでそれを修正するために加えた変更を以下に示しておく。

変更箇所その1。

class GetHandler(appengine_robot_runner.GetHandler): def __init__(self, method, contenttype): appengine_robot_runner.GetHandler.__init__(self, method, contenttype) def get(self): self.response.headers['Content-Type'] = self._contenttype self.response.out.write(self._method(host=self.request.host))

上記のコードを以下のように変更する。

class CapabilitiesHandler(appengine_robot_runner.CapabilitiesHandler): def __init__(self, method, contenttype): appengine_robot_runner.CapabilitiesHandler.__init__(self, method, contenttype) def get(self): self.response.headers['Content-Type'] = self._contenttype self.response.out.write(self._method(host=self.request.host)) class ProfileHandler(appengine_robot_runner.ProfileHandler): def __init__(self, method, contenttype): appengine_robot_runner.ProfileHandler.__init__(self, method, contenttype) def get(self): self.response.headers['Content-Type'] = self._contenttype if self.request.get('name'): self.response.out.write(self._method(self.request.get('name'), host=self.request.host)) else: self.response.out.write(self._method(host=self.request.host))

変更箇所その2。

([('/_wave/capabilities.xml', lambda: GetHandler(robot.capabilities_xml, 'application/xml')), ('/_wave/robot/profile', lambda: GetHandler(robot.profile_json, 'application/json')),

上記のコードを以下のように変更する。

([('/_wave/capabilities.xml', lambda: CapabilitiesHandler(robot.capabilities_xml, 'application/xml')), ('/_wave/robot/profile', lambda: ProfileHandler(robot.profile_json, 'application/json')),

0 コメント: