Activity Streams in practice
Atom and RSS feeds are typically used to support syndication of
existing works, most commonly weblog entries. They are XML documents
that provide a common representation that can be consumed by feed
readers, unlike the HTML pages for such a work. ActivityStreams is a
format for syndicating social activities around the web
.
Based on the Atom Syndication Format, it tries to provide a feed for
activities, rather than existing works. This includes the
act of posting a blog entry, but can also express
activities typical for social networking sites, like adding friends,
liking something or affirming an RSVP for an
event.
At Mediamatic Lab, we've recently gave notifications an overhaul. We had some code scattered around for sending notices to users, like when they received a friend request. We wanted to add a bunch of notifications so that people are aware of what happens in the network of sites, with their profile or works they've created. For example, when someone tags a person as being in a picture, it would be nice for that person to get a message about that. We also have a collection of RFID-enabled Interactive Installations that generate XMPP notifications for our backchannel system. I'll come back on this.
Whenever something happens that you want to send a notification for, there are a couple of things that you want to include in the notification: what happened, when it happened, and which persons and/or things are involved. The concepts of ActivityStreams turned out to coincide with how we wanted the notification to work. It abstracts activities in actors, objects and targets, along with a human-readable text to describe each activity.
A verb is an identifier for the kind of activity that has taken
place. A verb takes the form of a URI, much like rel
attributes in Atom link elements, or properties in RDF. The most basic
verb is post
, with the URI
http://activitystrea.ms/schema/1.0/post
.
An actor is the (usually) person that has performed the activity. Objects are the main persons or things that the activity was performed upon. For example, when I post a picture, I am the actor, and the picture is the object. A target is an object an action was done to. An example could be the photo album my uploaded picture was posted to. Actors, objects and targets usually have a URI, a title and an object type, similar to RDF classes.
Our new notification system does a couple of things whenever a an activity has taken place. It figures out the verb, actor, object and possibly the target and then creates a notification entry. It then calculates the parties that should get this notification in their inbox. This is usually the actor and the owner of the object. A person's profile is always owned by itself, so when the object is a person, that person would get a notification on things happening to them. When a party is not local (i.e. on another site in the federation), the notification is sent to the other site to be processed there. Each person's inbox is viewable as a stream of activities, much like Jaiku or Facebook, and is also published as an ActivityStreams feed (e.g. ralphm's activities. New notifications can then be processed by other modules.
One of them is the Message module, that sends out e-mails for notifications, according to personal preferences. For now, you can choose what kind of notifications you want to receive an e-mail about, by choosing the verbs you are interested in. Examples currently include: friend requests/confirmations, changes to things you own, people liking, linking to, RSVPing (for events), sharing (to Twitter, Facebook, etc) or commenting on things you own, and people tagging you in a picture.
Another module is the Publish-Subscribe module, that provides XMPP Publish-Subscribe nodes for each person, along with a node for all notifications for that site. This allows for applications that use the stream of activities for a person or the whole site, in near-real-time. An example could be a mobile app to track activity for you and/or your friends, or IM notifications much like Identi.ca or Jaiku.
Another possibility is a backchannel. We developed a backchannel system for events we deploy our RFID-enabled Interactive Installations at. A backchannel feed is aggregated from a configurable set of sources, of which the incoming items are formatted into notifications to be put up on a live stream. Every time someone takes a (group) picture with our ikCam, the image is posted on the backchannel, along with a text listing the people in the picture.
On top of that, we also can include tweets by tracking particular keywords and people. We use (and improved) twitty-twister to interact with Twitter's Streaming API from Twisted. I've recently changed the streaming code of twisty-twitter to consume JSON instead of the deprecated XML (with a bunch of tests), and a way to detect unresponsive connections.
With activities now also available as XMPP notifications, the logical step was to consume these for the backchannel as well. We have an office backchannel on a big screen that tracks Twitter for keywords related to Mediamatic and its events, and the notifications from our interactive installations. It now also includes activity on our sites, and this turns out to be a great way to see everything happening in our sites.
So, did it all go smoothly? No. We found quite some things in the ActivityStreams' concepts in combination with anyMeta and our interactive installations that we didn't expect when we started the project.
One of the big ones was Agents. Our interactive installations have
their own user accounts to take pictures, process votes, etc. These
accounts have special privileges to perform actions like making all
people in an ikCam picture contacts in the network. We also have a
Physical I-like-it button, which is an RFID reader placed next to a
physical object (e.g. a painting) that has a representation in one of
the sites. When reading a tag, it creates a like
relationship between the holder of the RFID tag and the object. When we
just enabled the first enabled the new notifications functionality, a
message popped up on the backchannel: ikPoll Agent likes
iTea
.
That was quite unexpected but quite logical when we thought about it. ikPoll Agent is the user account for the I-like-it button, that is powered by the same software as our more generic ikPoll installations. We defaulted the actor of an activity to the user account performing the action. Although the agent creates a link from the person to the object, the link was not created on behalf of the physical user. So we needed to introduce the concept of Agents, and have that also stored and communicated along with activities. The same action would now yield an entry with the title 'ralphm likes iTea (via ikPoll Agent).
Another was pictures taken with the ikCam. Besides posting the
image, all actors are tagged in the picture, the picture is optionally
linked to an event and a location. This yields a bunch of
notifications, where we would like to have only one: ralphm took
a self-portrait
. We have started work on compound activities
that would have the enclosed activities linked to it and back, a bit
like the Atom
Threading Extension.. This would allow aggregators like the
backchannel only show the umbrella notification.
A final one was our verb link
. This was supposed
to be a catch-all verb for the activity of creating a semantic link
between two things, of which the predicate didn't already have its own
verb (like friending, liking, etc.). It now looks like having a
notification like 'person A linked to thing B' might need some more
information. An e-mail notification at least has the links to
respective pages, but that doesn't quite work on a backchannel beamed
on a big screen. For now we ignore such notifications for the
backchannel, until we have a better solution. It might be that we need
to include the link's predicate in the notification, or make links
themselves first-class citizens (with their own URI).
Going to FOSDEM and/or the 10th XMPP Summit in Brussels? I'll be talking about this and other topics in my talk on Federating Social Networks on Saturday 5 February.