This week some code was push to the repository to implement some kind of agents for the federation server. A simple EchoAgent was added to demonstrate the code.

With this as starting point I implemented a small TwitterAgent. Just add the TwitterAgent to a wave.  The agent listens to every message  and updates your twitter status. I took the twitter4j API which is pretty easy to use.

package org.waveprotocol.wave.examples.fedone.agents.twitter;

import java.util.ArrayList;
import java.util.List;
import org.waveprotocol.wave.examples.fedone.agents.agent.AbstractAgent;
import org.waveprotocol.wave.examples.fedone.agents.agent.AgentConnection;
import org.waveprotocol.wave.examples.fedone.util.Log;
import org.waveprotocol.wave.model.document.operation.AnnotationBoundaryMap;
import org.waveprotocol.wave.model.document.operation.Attributes;
import org.waveprotocol.wave.model.document.operation.AttributesUpdate;
import org.waveprotocol.wave.model.document.operation.BufferedDocOp;
import org.waveprotocol.wave.model.document.operation.DocOpCursor;
import org.waveprotocol.wave.model.operation.wave.WaveletDocumentOperation;
import org.waveprotocol.wave.model.wave.ParticipantId;

import twitter4j.Twitter;
import twitter4j.TwitterException;

public class TwitterAgent extends AbstractAgent {
  private static final Log LOGGER = Log.get(TwitterAgent.class);
  private Twitter twitter;

  private TwitterAgent(String username, String hostname, int port, String twitterUsername, String twitterPassword) {
    super(AgentConnection.newConnection(username, hostname, port));
    this.twitter = new Twitter(twitterUsername, twitterPassword);

  private void tweetLines(List lines) {
    for (String line : lines) {"updateStatus: " + line);
      try {
      } catch (TwitterException e) {
        LOGGER.warning(e.getMessage(), e);

  public void onDocumentChanged(WaveletData wavelet, WaveletDocumentOperation documentOperation) {
    BufferedDocOp docOp = documentOperation.getOperation();
    final List lines = new ArrayList();
    docOp.apply(new DocOpCursor() {
      @Override public void annotationBoundary(AnnotationBoundaryMap map) { }
      @Override public void characters(String chars) { lines.add(chars); }
      @Override public void deleteCharacters(String chars) { }
      @Override public void deleteElementEnd() { }
      @Override public void deleteElementStart(String type, Attributes attrs) { }
      @Override public void elementEnd() { }
      @Override public void elementStart(String type, Attributes attrs) { }
      @Override public void replaceAttributes(Attributes oldAttrs, Attributes newAttrs) { }
      @Override public void retain(int itemCount) { }
      @Override public void updateAttributes(AttributesUpdate attrUpdate) { }

  public void onParticipantAdded(WaveletData wavelet, ParticipantId participant) { }

  public void onParticipantRemoved(WaveletData wavelet, ParticipantId participant) { }

  @Override public void onSelfAdded(WaveletData wavelet) { }

  @Override public void onSelfRemoved(WaveletData wavelet) { }

  public static void main(String[] args) {
    try {
      if (args.length == 5) {
        int port;
        try {
          port = Integer.parseInt(args[2]);
        } catch (NumberFormatException e) {
          throw new IllegalArgumentException("Must provide valid port.");

        TwitterAgent agent = new TwitterAgent(args[0], args[1], port, args[3], args[4]);;
      } else {
        System.out.println("usage: java TwitterAgent
    } catch (Exception e) {
      System.err.println("Catastrophic failure: " + e);


One issue with the current implementation should be mentioned: If you’ll restart the TwitterAgent all the messages will be resubmitted to your twitter account! If someone knows how to avoid this, please leave a comment.

Within in the next days, I’ll add some more little features (e.g. grab status updates and add them to a wave, twitter direct message gateway, …). What else could you think of?

This should be considered as a small demonstration and is far away from a Twitter and Google Wave integration 😉

Leave a reply