Prezi

Share this prezi

Who can edit:

Present Online

Send the link below via email or IM to invite your audience

Copy

Start the presentation

Start presenting

  • Invited audience will follow you as you navigate and present
  • This link expires 10 minutes after you close the presentation
  • A maximum of 30 users can view together your prezi
  • Learn more about this feature in the manual

Download prezi for:

Present offline on a PC or Mac.

  • Embedded YouTube videos need an active Internet connection to play.
  • Portable prezis are not editable.

Edit and present offline with Prezi Desktop

Do you really want to delete this prezi?

Neither you, nor the coeditors you shared it with will be able to recover it again.

DeleteCancel

Make your likes visible on Facebook?

Connect your Facebook account to Prezi and let your likes appear on your timeline.
You can change this under Settings & Account at any time.

Wave Robots API: Behind the Scenes

Robots, Gadgets, Embed
by Pamela Fox on 6 July 2010

Comments (0)

Please log in to add your comment.

Report abuse

Prezi Transcript

"events": [ { "type": "WAVELET_SELF_ADDED", "modifiedBy": "pamela.fox@wavesandbox.com", "timestamp": 1269400482868, "properties": { "blipId": "b+FYYeTpCXJ" } } ], "wavelet": { "creationTime": 1269400451853, "lastModifiedTime": 1269400482868, "version": 14, "participants": [ "pamela.fox@wavesandbox.com", "emo-bot@appspot.com" ], "creator": "pamela.fox@wavesandbox.com", "rootBlipId": "b+FYYeTpCXJ", "title": "Dear Diary,", "waveId": "wavesandbox.com!w+FYYeTpCXI", "waveletId": "wavesandbox.com!conv+root" }, {"params": { "blipId": "b+FYYeTpCXJ", "waveletId": "wavesandbox.com!conv+root", "waveId": "wavesandbox.com!w+FYYeTpCXI", "modifyAction": { "modifyHow": "REPLACE", "elements": [ { "type": "IMAGE", "properties": { "url": "http://emo-bot.appspot.com/smile.gif" } } ] }, "modifyQuery": { "textMatch": ":)", "maxRes": -1 } }, "method": "document.modify", "id": "op2" } Events Operations Robot Wave Wave Building the Wave Robots API import logging from waveapi import appengine_robot_runner from waveapi import element from waveapi import events from waveapi import ops from waveapi import robot anim_base = 'http://emo-bot.appspot.com/' anim_ext = '.gif' emoticons = { ':)' : 'smile', ':(' : 'frown', '(heart)': 'heart' } def ProcessBlip(event, wavelet): blip = event.blip for emoticon in emoticons: r = blip.all(emoticon) if r: r.replace(element.Image(anim_base + emoticons[emoticon] + anim_ext)) pass if __name__ == '__main__': emobot = robot.Robot('Emo Bot', image_url='http://emo-bot.appspot.com/public/thumbnail.png', profile_url='http://emo-bot.appspot.com') emobot.register_handler(events.BlipSubmitted, ProcessBlip) emobot.register_handler(events.WaveletSelfAdded, ProcessBlip) appengine_robot_runner.run(emobot, debug=True) Profiles http://emo-bot.appspot.com/_wave/robot/profile {"profileUrl": "http://emo-bot.appspot.com", "imageUrl": "https://emo-bot.appspot.com/public/thumbnail.png", "name": "Emo Bot"} app.yaml application: emo-bot version: 1 runtime: python handlers: - url: /_wave/.* script: main.py - url: /public/(.*) static_files: public/\1 upload: public/(.*) import logging from waveapi import appengine_robot_runner from waveapi import element from waveapi import events from waveapi import ops from waveapi import robot anim_base = 'http://wave-skimmy.appspot.com' anim_ext = '.gif' emoticons = { ':)' : 'smile', ':(' : 'frown', '(heart)': 'heart' } def ProcessBlip(event, wavelet): blip = event.blip for emoticon in emoticons: r = blip.all(emoticon) if r: r.replace(element.Image(anim_base + emoticons[emoticon] + anim_ext)) pass if __name__ == '__main__': emobot = robot.Robot('Emo Bot', image_url='http://emo-bot.appspot.com/public/thumbnail.png', profile_url='http://emo-bot.appspot.com/') emobot.register_handler(events.BlipSubmitted, ProcessBlip) emobot.register_handler(events.WaveletSelfAdded, ProcessBlip) appengine_robot_runner.run(emobot, debug=True) main.py .... class ProfileHandler(webapp.RequestHandler): def __init__(self, method, contenttype): self._method = method self._contenttype = contenttype def get(self): self.response.headers['Content-Type'] = self._contenttype self.response.out.write(self._method()) def create_robot_webapp(robot): return webapp.WSGIApplication([ ('.*/_wave/robot/profile', lambda: ProfileHandler( robot.profile_json, 'application/json')), ('.*/_wave/capabilities.xml', lambda: CapabilitiesHandler( robot.capabilities_xml, 'application/xml')), ('.*/_wave/robot/jsonrpc', lambda: RobotEventHandler(robot)), ('.*/_wave/verify_token', lambda: RobotVerifyTokenHandler(robot)), ]) def run(robot): app = create_robot_webapp(robot) run_wsgi_app(app) appengine_robot_runner.py robot.py ... def profile_json(self): data = {'name': self.name, 'imageUrl': self.image_url, 'profileUrl': self.profile_url} return simplejson.dumps(data) ... Capabilities which protocol version? which events? Incoming JSON Outgoing JSON http://emo-bot.appspot.com/_wave/capabilities.xml <w:robot xmlns:w="http://wave.google.com/extensions/robots/1.0"> <w:version>0xc5d908e</w:version> <w:protocolversion>0.2</w:protocolversion> <w:capabilities> <w:capability name="BLIP_SUBMITTED"/> <w:capability name="DOCUMENT_CHANGED"/> <w:capability name="OPERATION_ERROR"/> </w:capabilities> </w:robot> application: emo-bot version: 1 runtime: python handlers: - url: /_wave/.* script: main.py - url: /public/(.*) static_files: public/\1 upload: public/(.*) import logging from waveapi import appengine_robot_runner from waveapi import element from waveapi import events from waveapi import ops from waveapi import robot anim_base = 'http://emo-bot.appspot.com' anim_ext = '.gif' emoticons = { ':)' : 'smile', ':(' : 'frown', '(heart)': 'heart' } def ProcessBlip(event, wavelet): blip = event.blip for emoticon in emoticons: r = blip.all(emoticon) if r: r.replace(element.Image(anim_base + emoticons[emoticon] + anim_ext)) pass if __name__ == '__main__': emobot = robot.Robot('Emo Bot', image_url='http://emo-bot.appspot.com/public/thumbnail.png', profile_url='http://emo-bot.appspot.com/') emobot.register_handler(events.BlipSubmitted, ProcessBlip) emobot.register_handler(events.WaveletSelfAdded, ProcessBlip) appengine_robot_runner.run(emobot, debug=True) appengine_robot_runner.py robot.py class CapabilitiesHandler(webapp.RequestHandler): def __init__(self, method, contenttype): self._method = method self._contenttype = contenttype def get(self): self.response.headers['Content-Type'] = self._contenttype self.response.out.write(self._method()) def create_robot_webapp(robot): return webapp.WSGIApplication([ ('.*/_wave/robot/profile', lambda: ProfileHandler( robot.profile_json, 'application/json')), ('.*/_wave/capabilities.xml', lambda: CapabilitiesHandler( robot.capabilities_xml, 'application/xml')), ('.*/_wave/robot/jsonrpc', lambda: RobotEventHandler(robot)), ('.*/_wave/verify_token', lambda: RobotVerifyTokenHandler(robot)), ]) def run(robot): app = create_robot_webapp(robot) run_wsgi_app(app) app.yaml main.py def capabilities_xml(self): """Return this robot's capabilities as an XML string.""" lines = [] for capability, payloads in self._handlers.items(): for payload in payloads: handler, event_class, context, filter = payload line = ' <w:capability name="%s" >/\n' % capability lines.append(line) return ('<?xml version="1.0"?>\n' '<w:robot xmlns:w="http://.../robots/1.0">\n' '<w:version>%s</w:version>\n' '<w:protocolversion>%s</w:protocolversion>\n' '<w:capabilities>\n' '%s' '</w:capabilities>\n' '</w:robot>\n') % (self.capabilities_hash(), ops.PROTOCOL_VERSION, '\n'.join(lines)) [ { "method": "robot.notifyCapabilitiesHash", "params": { "capabilitiesHash": "0xc5d908e" } } ] cache: emo-bot.appspot.com 0x10962d0 Outgoing JSON http://emo-bot.appspot.com/_wave/capabilities.xml <w:robot xmlns:w="http://wave.google.com/extensions/robots/1.0"> <w:version>0xc5d908e</w:version> <w:protocolversion>0.2</w:protocolversion> <w:capabilities> <w:capability name="BLIP_SUBMITTED"/> <w:capability name="DOCUMENT_CHANGED"/> <w:capability name="OPERATION_ERROR"/> </w:capabilities> </w:robot> Problem Solution Auto Generation <w:robot xmlns:w="http://wave.google.com/extensions/robots/1.0"> <w:version>0xc5d908e</w:version> <w:protocolversion>0.2</w:protocolversion> <w:capabilities> <w:capability name="BLIP_SUBMITTED"/> <w:capability name="DOCUMENT_CHANGED"/> <w:capability name="OPERATION_ERROR"/> </w:capabilities> </w:robot> http://emo-bot.appspot.com/_wave/capabilities.xml Problem Solution def register_handler(self, event_class, handler, context=None, filter=None): payload = (handler, event_class, context, filter) self._handlers.append(payload) self._capability_hash = (self._capability_hash * 13 + hash(ops.PROTOCOL_VERSION) + hash(event_class.type) + hash(context) + hash(filter)) & 0xfffffff def process_events(self, json): parsed = simplejson.loads(json) pending_ops = ops.OperationQueue() event_wavelet = self._wavelet_from_json(parsed, pending_ops) for event_data in parsed['events']: for payload in self._handlers.get(event_data['type'], []): handler, event_class, context, filter = payload event = event_class(event_data, event_wavelet) handler(event, event_wavelet) pending_ops.set_capability_hash(self.capabilities_hash()) return simplejson.dumps(pending_ops.serialize()) appengine_robot_runner.py main.py application: emo-bot version: 1 runtime: python handlers: - url: /_wave/.* script: main.py - url: /public/(.*) static_files: public/\1 upload: public/(.*) app.yaml import logging from waveapi import appengine_robot_runner from waveapi import element from waveapi import events from waveapi import ops from waveapi import robot anim_base = 'http://emo-bot.appspot.com' anim_ext = '.gif' emoticons = { ':)' : 'smile', ':(' : 'frown', '(heart)': 'heart' } def ProcessBlip(event, wavelet): blip = event.blip for emoticon in emoticons: r = blip.all(emoticon) if r: r.replace(element.Image(anim_base + emoticons[emoticon] + anim_ext)) pass if __name__ == '__main__': emobot = robot.Robot('Emo Bot', image_url='http://emo-bot.appspot.com/public/thumbnail.png', profile_url='http://emo-bot.appspot.com/') emobot.register_handler(events.BlipSubmitted, ProcessBlip) emobot.register_handler(events.WaveletSelfAdded, ProcessBlip) appengine_robot_runner.run(emobot, debug=True) class RobotEventHandler(webapp.RequestHandler): def __init__(self, robot): self._robot = robot def post(self): json_body = self.request.body json_response = self._robot.process_events(json_body) self.response.headers['Content-Type'] = 'application/json; charset=utf-8' self.response.out.write(json_response.encode('utf-8')) def create_robot_webapp(robot): return webapp.WSGIApplication([ ('.*/_wave/robot/profile', lambda: ProfileHandler( robot.profile_json, 'application/json')), ('.*/_wave/capabilities.xml', lambda: CapabilitiesHandler( robot.capabilities_xml, 'application/xml')), ('.*/_wave/robot/jsonrpc', lambda: RobotEventHandler(robot)), ('.*/_wave/verify_token', lambda: RobotVerifyTokenHandler(robot)), ]) def run(robot): app = create_robot_webapp(robot) run_wsgi_app(app) Auto Generation robot.py Capabilities Hash {"params": { "blipId": "b+FYYeTpCXJ", "waveletId": "wavesandbox.com!conv+root", "waveId": "wavesandbox.com!w+FYYeTpCXI", "modifyAction": { "modifyHow": "REPLACE", "elements": [ { "type": "IMAGE", "properties": { "url": "http://wave-skimmy.appspot.com/smile.gif" } } ] }, "modifyQuery": { "textMatch": ":)", "maxRes": -1 } }, "method": "document.modify", "id": "op2" } Outgoing JSON Problem Solution Outgoing JSON {"params": { "blipId": "b+FYYeTpCXJ", "waveletId": "wavesandbox.com!conv+root", "waveId": "wavesandbox.com!w+FYYeTpCXI", "modifyAction": { "modifyHow": "REPLACE", "elements": [ { "type": "IMAGE", "properties": { "url": "http://wave-skimmy.appspot.com/smile.gif" } } ] }, "modifyQuery": { "textMatch": ":)", "maxRes": -1 } }, "method": "document.modify", "id": "op2" } if __name__ == '__main__': emobot = robot.Robot('Emo Bot', image_url='http://emo-bot.appspot.com/public/thumbnail.png', profile_url='http://emo-bot.appspot.com/') emobot.register_handler(events.BlipSubmitted, ProcessBlip) emobot.register_handler(events.WaveletSelfAdded, ProcessBlip) emobot.set_verification_token_info('AOijR2fFR5awOGHmU...', '4203') appengine_robot_runner.run(emobot, debug=True) if __name__ == '__main__': emobot = robot.Robot('Emo Bot', image_url='http://emo-bot.appspot.com/public/thumbnail.png', profile_url='http://emo-bot.appspot.com/') emobot.register_handler(events.BlipSubmitted, ProcessBlip) emobot.register_handler(events.WaveletSelfAdded, ProcessBlip) emobot.setup_oauth('TOPSECRETKEY', 'TOPSECRETSECRET') appengine_robot_runner.run(emobot, debug=True) def make_rpc(self, operations): data = simplejson.dumps(queue.serialize()) oauth_request = oauth.OAuthRequest.from_consumer_and_token( self._consumer, http_method='POST', http_url=self._server_rpc_base) oauth_request.sign_request(WaveService.SIGNATURE_METHOD, self._consumer) headers = {'Content-Type': 'application/json'} headers.update(oauth_request.to_header()); status, content = self._http_post( url=self._server_rpc_base, data=data, headers=headers) class RobotVerifyTokenHandler(webapp.RequestHandler): def __init__(self, robot): self._robot = robot def get(self): token, st = self._robot.get_verification_token_info() if self.request.get('st') != st: self.response.out.write('Invalid st value passed') return self.response.out.write(token) def create_robot_webapp(robot): return webapp.WSGIApplication([('.*/_wave/capabilities.xml', lambda: CapabilitiesHandler( robot.capabilities_xml, 'application/xml')), ('.*/_wave/robot/profile', lambda: ProfileHandler( robot.profile_json, 'application/json')), ('.*/_wave/robot/jsonrpc', lambda: RobotEventHandler(robot)), ('.*/_wave/verify_token', lambda: RobotVerifyTokenHandler(robot)), ]) appengine_robot_runner.py main.py waveservice.py main.py OAuth Handling Active Operations Problem webapp + + + __init__.py appengine_robot_runner.py blip.py element.py errors.py events.py oauth/ ops.py robot.py search.py simplejson/ util.py wavelet.py waveservice.py webapp + class CapabilitiesHandler(webapp.RequestHandler) class RobotEventHandler(webapp.RequestHandler) class ProfileHandler(webapp.RequestHandler) class RobotVerifyTokenHandler(webapp.RequestHandler) def appengine_post(url, data, headers) def operation_error_handler(event, wavelet) def create_robot_webapp(robot, debug=False) def run(robot, debug=False, log_errors=True) Solution from django.conf.urls.defaults import * urlpatterns = patterns('', (r'^_wave/*', 'testproject.testbot.wave.OnWaveAnything') ) import logging from waveapi import django_robot_runner from waveapi import element from waveapi import events from waveapi import ops from waveapi import robot anim_base = 'http://emo-bot.appspot.com' anim_ext = '.gif' emoticons = { ':)' : 'smile', ':(' : 'frown', '(heart)': 'heart' } def ProcessBlip(event, wavelet): blip = event.blip for emoticon in emoticons: r = blip.all(emoticon) if r: r.replace(element.Image(anim_base + emoticons[emoticon] + anim_ext)) pass def OnWaveAnything(request): emobot = robot.Robot('Emo Bot', image_url='http://emo-bot.appspot.com/public/thumbnail.png', profile_url='http://emo-bot.appspot.com/') emobot.register_handler(events.BlipSubmitted, ProcessBlip) emobot.register_handler(events.WaveletSelfAdded, ProcessBlip) emobot.setup_oauth(credentials.KEY, credentials.SECRET) return django_robot_runner.handle(request, emobot) import logging import sys from django.http import HttpResponseRedirect from django.http import HttpResponse from django.shortcuts import render_to_response import events def CapabilitiesHandler(request, robot): return HttpResponse(robot.capabilities_xml(), content_type='text/xml') def ProfileHandler(request, robot): response = robot.profile_json() return HttpResponse(response, content_type='text/plain') def RobotEventHandler(request, robot): if request.method == 'POST': json_body = request.raw_post_data json_body = unicode(json_body, 'utf8') json_response = robot.process_events(json_body) response = (json_response.encode('utf-8')) return HttpResponse(response, content_type='application/json; charset=utf-8') else: return HttpResponse('GET not implemented') def operation_error_handler(event, wavelet): if isinstance(event, events.OperationError): logging.error('Previously operation failed: id=%s, message: %s', event.operation_id, event.error_message) def RobotVerifyTokenHandler(request, robot): token, st = robot.get_verification_token_info() if request.GET['st'] != st: response = 'Invalid st value passed' return response = token return HttpResponse(response) def handle(request, robot): robot.register_handler(events.OperationError, operation_error_handler) if request.path.find('_wave/capabilities.xml') > -1: return CapabilitiesHandler(request, robot) if request.path.find('_wave/robot/profile') > -1: return ProfileHandler(request, robot) if request.path.find('_wave/robot/jsonrpc') > -1: return RobotEventHandler(request, robot) if request.path.find('_wave/verify_token') > -1: return RobotVerifyTokenHandler(request, robot) + urls.py wave.py django_robot_runner.py Non-App Engine Robots Behind the Scenes Google Maps APIs Google Wave APIs Spreadsheets, Gadgets, App Engine, Blogger, Calendar, Picasa, OpenSocial, Flickr, Amazon, ... JavaScript Python , PHP , JavaScript , Java About Me Wave Robots API v1 Wave Developer Preview June 1, 2009 March 1, 2010 Wave Robots API v2 Wave Consumer Preview Oct. 1, 2009 API History wave wavelet wavelet conv+O4Fsp5kfB conv+root wavesandbox.com!w+YTiBcVOfA wavelet conv+root blip b+O4Fsp5kfA b+YTiBcVOfB blip blip b+YTiBcVOfD blip b+Dad5DLZkB tags participants <body> <line a="c"></line> The pizza was awesome, can't believe you missed it! <line></line> <image url="http://www.p.com/p.gif"> <line></line>More pics here. </body> (17,24) : style/fontSize=1.3em (17,24) : style/fontWeight=bold (72,76) : link=http://www.p.com/p.gif (1,77) : lang=en b+Dad5DLZkB blip Google Wave Conversation Model Operational Transforms document items+positions operations skip, insert, delete insert 'ABCDE' skip 3, delete 1 Lessons Learned Automation Versioning Authentication Cross-Framework Language-Appropriate Google Wave Robot Python API v2 ============================================ The :mod:`blip` Module ------------------------- .. automodule:: blip :members: :undoc-members: The :mod:`wavelet` Module ------------------------- .. automodule:: wavelet :members: :undoc-members: The :mod:`robot` Module ------------------------- .. automodule:: robot :members: :undoc-members: index.rst ... class Blips(object): """A dictionary-like object containing the blips, keyed on blip ID.""" def get(self, blip_id, default_value=None): """Retrieves a blip. Returns: A Blip object. If none found for the ID, it returns None, or if default_value is specified, it returns that. """ return self._blips.get(blip_id, default_value) ... blip.py sphinx-build -b html -d _build/doctrees . _build/html + + Makefile = index.html Documentation Events -> Operations def process_events(self, json): parsed = simplejson.loads(json) pending_ops = ops.OperationQueue() event_wavelet = self._wavelet_from_json(parsed, pending_ops) for event_data in parsed['events']: for payload in self._handlers.get(event_data['type'], []): handler, event_class, context, filter = payload event = event_class(event_data, event_wavelet) handler(event, event_wavelet) return simplejson.dumps(pending_ops.serialize()) appengine_robot_runner.py app.yaml main.py class RobotEventHandler(webapp.RequestHandler): def __init__(self, robot): self._robot = robot def post(self): json_body = self.request.body json_body = unicode(json_body, 'utf8') json_response = self._robot.process_events(json_body) self.response.headers['Content-Type'] = 'application/json; charset=utf-8' self.response.out.write(json_response.encode('utf-8')) def create_robot_webapp(robot): return webapp.WSGIApplication([ ('.*/_wave/robot/profile', lambda: ProfileHandler( robot.profile_json, 'application/json')), ('.*/_wave/capabilities.xml', lambda: CapabilitiesHandler( robot.capabilities_xml, 'application/xml')), ('.*/_wave/robot/jsonrpc', lambda: RobotEventHandler(robot)), ('.*/_wave/verify_token', lambda: RobotVerifyTokenHandler(robot)), ]) def run(robot): app = create_robot_webapp(robot) run_wsgi_app(app) robot.py application: emo-bot version: 1 runtime: python handlers: - url: /_wave/.* script: main.py - url: /public/(.*) static_files: public/\1 upload: public/(.*) import logging from waveapi import appengine_robot_runner from waveapi import element from waveapi import events from waveapi import ops from waveapi import robot anim_base = 'http://emo-bot.appspot.com' anim_ext = '.gif' emoticons = { ':)' : 'smile', ':(' : 'frown', '(heart)': 'heart' } def ProcessBlip(event, wavelet): blip = event.blip for emoticon in emoticons: r = blip.all(emoticon) if r: r.replace(element.Image(anim_base + emoticons[emoticon] + anim_ext)) pass if __name__ == '__main__': emobot = robot.Robot('Emo Bot', image_url='http://emo-bot.appspot.com/public/thumbnail.png', profile_url='http://emo-bot.appspot.com/') emobot.register_handler(events.BlipSubmitted, ProcessBlip) emobot.register_handler(events.WaveletSelfAdded, ProcessBlip) appengine_robot_runner.run(emobot, debug=True) Incoming JSON {"params": { "blipId": "b+FYYeTpCXJ", "waveletId": "wavesandbox.com!conv+root", "waveId": "wavesandbox.com!w+FYYeTpCXI", "modifyAction": { "modifyHow": "REPLACE", "elements": [ { "type": "IMAGE", "properties": { "url": "http://wave-skimmy.appspot.com/smile.gif" } } ] }, "modifyQuery": { "textMatch": ":)", "maxRes": -1 } }, "method": "document.modify", "id": "op2" } "events": [ { "type": "WAVELET_SELF_ADDED", "modifiedBy": "pamela.fox@wavesandbox.com", "timestamp": 1269400482868, "properties": { "blipId": "b+FYYeTpCXJ" } } ], "wavelet": { "creationTime": 1269400451853, "lastModifiedTime": 1269400482868, "version": 14, "participants": [ "pamela.fox@wavesandbox.com", "wave-skimmy@appspot.com" ], "creator": "pamela.fox@wavesandbox.com", "rootBlipId": "b+FYYeTpCXJ", "title": "Dear Diary,", "waveId": "wavesandbox.com!w+FYYeTpCXI", "waveletId": "wavesandbox.com!conv+root" }, Outgoing JSON Google Wave Behind the Scenes Google Wave Robots API Examples pamelafox@ @pamelafox skip 3, delete 1 skip 1, delete 1 skip 1, delete 1 skip 2, delete 1 Ferry Tasky Monty+Syntaxy Poker http://emo-bot.appspot.com/_wave/robot/jsonrpc http://emo-bot.appspot.com/_wave/robot/jsonrpc
See the full transcript