Introducing 

Prezi AI.

Your new presentation assistant.

Refine, enhance, and tailor your content, source relevant images, and edit visuals quicker than ever before.

Loading…
Transcript

http://emo-bot.appspot.com/_wave/robot/jsonrpc

Auto Generation

Active Operations

OAuth Handling

Non-App Engine Robots

Documentation

About Me

Google Wave APIs

Google Maps APIs

Spreadsheets, Gadgets, App Engine, Blogger, Calendar, Picasa,

OpenSocial, Flickr, Amazon, ...

JavaScript

Python

, PHP

, JavaScript

, Java

pamelafox@

@pamelafox

Building the Wave Robots API

Behind the Scenes

Google Wave Robots API

Examples

Tasky

Monty+Syntaxy

Ferry

Poker

API History

Wave Consumer Preview

Wave Developer Preview

Wave Robots API v1

Wave Robots API v2

March 1, 2010

Oct. 1, 2009

June 1, 2009

Google Wave

Wave

Events

Operations

Robot

Outgoing JSON

Incoming JSON

Behind the Scenes

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

}

Google Wave Conversation Model

wave

wavesandbox.com!w+YTiBcVOfA

wavelet

conv+root

wavelet

blip

conv+root

b+Dad5DLZkB

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)

participants

wavelet

blip

conv+O4Fsp5kfB

b+O4Fsp5kfA

blip

b+YTiBcVOfB

blip

b+YTiBcVOfD

blip

(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

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

tags

Events -> Operations

Operational Transforms

http://emo-bot.appspot.com/_wave/robot/jsonrpc

Outgoing JSON

Incoming JSON

document

operations

items+positions

skip 1, delete 1

skip 3, delete 1

skip, insert, delete

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

},

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

}

skip 1, delete 1

skip 2, delete 1

skip 3, delete 1

insert 'ABCDE'

app.yaml

appengine_robot_runner.py

main.py

robot.py

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

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)

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)

Capabilities

Solution

Problem

http://emo-bot.appspot.com/_wave/capabilities.xml

which protocol version?

which events?

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

app.yaml

appengine_robot_runner.py

main.py

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)

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)

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

Capabilities Hash

Problem

Solution

http://emo-bot.appspot.com/_wave/capabilities.xml

Outgoing JSON

http://emo-bot.appspot.com/_wave/capabilities.xml

[

{

"method": "robot.notifyCapabilitiesHash",

"params": {

"capabilitiesHash": "0xc5d908e"

}

}

]

cache:

emo-bot.appspot.com

0x10962d0

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

app.yaml

appengine_robot_runner.py

main.py

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)

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)

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

Solution

Problem

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"

}

appengine_robot_runner.py

main.py

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)

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

])

waveservice.py

main.py

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)

Solution

Problem

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)

__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

+

webapp

django_robot_runner.py

wave.py

+

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)

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)

urls.py

Profiles

from django.conf.urls.defaults import *

urlpatterns = patterns('',

(r'^_wave/*', 'testproject.testbot.wave.OnWaveAnything')

)

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

main.py

robot.py

appengine_robot_runner.py

...

def profile_json(self):

data = {'name': self.name,

'imageUrl': self.image_url,

'profileUrl': self.profile_url}

return simplejson.dumps(data)

...

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)

....

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)

index.html

blip.py

Makefile

index.rst

sphinx-build

-b html

-d _build/doctrees

. _build/html

=

+

...

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)

...

+

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:

Lessons Learned

Automation

Versioning

Authentication

Cross-Framework

Language-Appropriate

Learn more about creating dynamic, engaging presentations with Prezi