My favorites | English | Sign in

Faster apps faster - GWT 2.0 with Speed Tracer New!

WaveProtocol.org

Learn about the Google Wave Federation Protocol and get involved

Google Wave Robots: Python Tutorial

The easiest way to understand how extensions work in Wave is to build a Wave robot. Robots are applications that interact with a Wave through the Wave protocol (HTTP interface). Currently, we only support robots hosted with Google App Engine. In the future, we will support any client architecture that implements the Wave protocol.

In this tutorial, we'll use the Python client library to develop a sample robot. (The concepts apply here equally well to users of the Java™ client library, though implementation details will be different.) In this brief tutorial, you will create a simple robot, upload it to App Engine, and see it working with Wave.

Before you get started, make sure you have Python 2.5 or higher installed on your system. You can determine if you have Python installed (and what version is installed) by executing the following command from the command line:

hostname$ python --version
Python 2.6.2

You can download Python from the Python downloads page.

The Python Client Library

Development of Google Wave robots requires compiling with a client library. This tutorial uses the Wave Robot Python Client Library available on Google Project Hosting. This project contains the source code for the library itself at the following location:

Create a directory to hold your source code for your Python robot and extract the Python client library into a waveapi directory using the following svn command:

hostname$ svn checkout http://wave-robot-python-client.googlecode.com/svn/trunk/src/waveapi waveapi

This command will create a waveapi directory within your current source code directory. Note that if you check out the code using the HTTPS protocol and authenticate, you can contribute back to the open source project directly.

Setting Up App Engine

You can develop Wave robots and other web applications for App Engine using your Python development tools. Before you continue, follow the instructions for installing the Python SDK in the App Engine Documentation.

Registering Your Application with App Engine

You will also want to register your robot using an Application ID at https://appengine.google.com.. You are allowed to register up to 10 application IDs, and application registrations cannot be undone or deleted, nor can an application ID be changed after it is registered. If you wish to conserve your allotted application registrations, you may want to choose an application ID you know you will use for a future project. As well, you may wish to reserve one ID for testing purposes of your robots.

Go to the App Engine Administrator Console in your web browser. Sign in using your Google account, creating one if necessary. If you haven't used this account with App Engine before, you may also be prompted to verify your account using SMS and a mobile phone.

Under My Applications click the Create an Application button. Choose an application ID, and follow the prompts to complete the registration. The new application appears in the list. You can click its name to visit the Administrator Console for this application.

Configuring Your Python Application

Once you have a valid application ID, create a new app.yaml application specification within the root of your source directory with the following content:

application: applicationName
version: 1
runtime: python
api_version: 1

handlers:
- url: /_wave/.*
  script: applicationName.py
- url: /assets
  static_dir: assets

You don't need to name your Python script to the same name as your application ID, though this is customary. We also specify a URL pattern to serve any static assets for the application, such as images, stylesheets, etc.

For more information on getting started with App Engine, see the App Engine Getting Started Guide for Python.

Hello, Robot!

Now it's time to write our Python code. First create a *.py file with the same name as you noted in your app.yaml file (e.g. applicationName.py). Add the following code:

from waveapi import events
from waveapi import model
from waveapi import robot

def OnParticipantsChanged(properties, context):
  """Invoked when any participants have been added/removed."""
  added = properties['participantsAdded']
  for p in added:
    Notify(context)

def OnRobotAdded(properties, context):
  """Invoked when the robot has been added."""
  root_wavelet = context.GetRootWavelet()
  root_wavelet.CreateBlip().GetDocument().SetText("I'm alive!")

def Notify(context):
  root_wavelet = context.GetRootWavelet()
  root_wavelet.CreateBlip().GetDocument().SetText("Hi everybody!")

if __name__ == '__main__':
  myRobot = robot.Robot('appName', 
      image_url='http://appName.appspot.com/icon.png',
      version='1',
      profile_url='http://appName.appspot.com/')
  myRobot.RegisterHandler(events.WAVELET_PARTICIPANTS_CHANGED, OnParticipantsChanged)
  myRobot.RegisterHandler(events.WAVELET_SELF_ADDED, OnRobotAdded)
  myRobot.Run()

We'll explain this code in the next sections.

Importing the Client Libraries

To make a python application into a robot, we need to include the Python robot library using the following import statements:

from waveapi import events
from waveapi import model
from waveapi import robot

Defining your Main Function

Once you have imported the proper libraries, define your main function. Note that we do this at the bottom of the file, so that we can refer to defined functions above the __main__ declaration.

if __name__ == '__main__':
  myRobot = robot.Robot('appName', 
      image_url='http://appName.appspot.com/icon.png',
      version='1',
      profile_url='http://appName.appspot.com/')
  myRobot.RegisterHandler(events.WAVELET_PARTICIPANTS_CHANGED, OnParticipantsChanged)
  myRobot.RegisterHandler(events.WAVELET_SELF_ADDED, OnRobotAdded)
  myRobot.Run()

This code simply defines a new robot with the given name, sets some additional profile information, registers a single event handler, telling the system we are interested in the WAVELET_PARTICIPANTS_CHANGED and WAVELET_SELF_ADDED events, and then runs the robot.

Note that the Robot() constructor implicitly defines the Robot's Profile by assigning constructor arguments to profile settings. The Robot's name, passed as the first argument, is assigned as the Robot profile's name property, for example. An image passed within image_url will be used as the Robot's avatar, while profile_url specifies a URL which explains the purpose and usage of the robot.

Accessing Waves

We want to use the WAVELET_SELF_ADDED event to detect when the robot itself is added to the wave, and write information back into the wave. We'll write an OnRobotAdded() function to handle our event first:

def OnRobotAdded(properties, context):
  """Invoked when the robot has been added."""
  root_wavelet = context.GetRootWavelet()
  root_wavelet.CreateBlip().GetDocument().SetText("I'm alive!")

We'll also want to to use the WAVELET_PARTICIPANTS_CHANGED event to detect when anyone other than a robot is added to the wave. We'll write an OnParticipantsChanged() function to handle that event:

def OnParticipantsChanged(properties, context):
"""Invoked when any participants have been added/removed"""
  added = properties['participantsAdded']
  for p in added:
    Notify(context)

Whenever any participants are added or removed from the wave, it will receive this event. For each participant that is added, we'll invoke the Notify() function. Notice that we explicitly call Notify() for each participant added, as we are not guaranteed to get discrete events for each participant. (The event system may bundle multiple participants together into one event.)

The Notify() function does the business of actually writing to the wave:

def Notify(context):
  """Called when this robot is first added to the wave."""
  """Also called whenever a new participant is added to the wave>"""
  root_wavelet = context.GetRootWavelet()
  root_wavelet.CreateBlip().GetDocument().SetText("Hi everybody!")

Note that we create a new Blip within the Wavelet, retrieve the document for that blip, and set the text explicitly.

Robot Versioning

Robots within the Wave API are versioned. This allows the Wave system to detect when robots have changed and/or their capabilities have been altered. If you modify a robot's capabilities (by adding or removing monitored events, for example), you should also modify the version identifier in the Robot's constructor.

When deploying a robot, the Wave system will check if the robot identifer is different than what it has cached. (The robot identifier is simply a text string.) If so, Wave will refresh the robot and alter the system to generate any new events you've indicated interest in.

Deploying the Robot

You can test your new robot by deploying it to App Engine, and then adding it to a wave.

Note: No mechanism currently exists to test Wave robots on your local machine with the App Engine development server. A future release of the Wave SDK will include tools to test robots locally before deploying them to App Engine.

To deploy the application to App Engine, use whichever App Engine launcher is applicable for your Operating System. (This tutorial was written using the Mac OS GoogleAppEngineLauncher.) When deploying, the App Engine launcher will ask you for the username and password associated with the registered application, and execute appfg.py.

The following is sample output from the Mac OS launcher:

*** Running appfg.py with the following flags:
    --no_cookies --email=username@gmail.com --passin update
Scanning files on local disk.
Initiating update.
Password for username@gmail.com: Cloning 1 application file.
Deploying new version.
Checking if new version is ready to serve.
Closing update: new version is ready to start serving.
Uploading index definitions.
If deploy fails you might need to 'rollback' manually.
The "Make Symlinks..." menu option can help with command-line work.
*** appcfg.py has finished with exit code 0 ***

You can check that your application is available by loading the Robot's http://appName.appspot.com/_wave/capabilities.xml file. This XML file is auto-generated by the Python client library and indicates the events with which the robot is programmed to respond. A typical file is shown below:

Note: this capabilities.xml is not generated automatically by the Java client library. For that library, you must create and supply the file manually. For more information, see Configuring Your Robot in the Java tutorial.

Adding the Robot to the Wave

You add a robot to a wave by adding it as a participant in the wave with which you want it to interact. To do so, you must first add the Robot's address to your existing contacts. (You must do this outside of the current wave, currently.)

Within Wave, now create a new wave. Add your robot to the wave using its Wave ID, which is the App Engine application ID followed by @appspot.com (for example, dummyrobot@appspot.com.) The robot joins the wave, and adds its greeting.

Congratulations! You've built your first Wave Robot!