Present Online
Send the link below via email or IM to invite your audience
Start the presentation
- 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
- To open PEZ file, please download 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.
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
Tweet
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




