Here's how to install and use Switchboard for a few basic use-cases:
$ # install Switchboard
$ sudo gem install switchboard
$ # list everyone on your roster (buddy list)
$ switchboard --jid client@example.com --password pa55word \
roster list
$ # add a friend to your roster
$ switchboard --jid client@example.com --password pa55word \
roster add friend@example.com
$ # listen for PubSub events
$ switchboard --jid subscriber@example.com --password pa55word \
pubsub --server <pubsub server> listen
curl
is the ultimate Swiss Army Knife for HTTP. Switchboard aims to be the
same for XMPP.
HTTP is (relatively) easy. It's stateless and has fairly limited semantics.
However, if you're trying to debug a web service and want to determine what
certain ETag
s, additional headers, or specific content types respond with,
curl
is your go-to.
XMPP is a bit more complicated; it's stateful, it (usually) requires login credentials, it's asynchronous, and it has many extensions to the core protocol that are in varying levels of use. Traditionally, in order to explore an XMPP service, you'd have to delve into the advanced features of a client like Psi or Synapse, often dropping to the level of entering raw XML to see what happens in response.
Switchboard (and its command-line equivalent, switchboard
) simplifies the
process of repeatedly making common requests (e.g. roster manipulation,
probing, and PubSub node operations) and is easily extended to support new
behaviors.
Copying and pasting XML isn't the least error-prone process I can think of. In addition, I found myself repeatedly testing the same types of stanzas (e.g. PubSub requests) and attempting to implement extensions for which no implementations existed (i.e. OAuth over XMPP).
Diving into xmpp4r
's functionality led me to
expose additional features to the command-line tool such as
PEP and Last
Activity to see how difficult it
would be.
Since all of its dependencies have been released and are installable via RubyGems now, people who aren't me can finally use Switchboard without much hassle. In honor of this milestone, Switchboard has been updated to 0.1.0. Real mature, I know, but it is still quite useful.
People who are me have been using this tool since last November (in fact, this is the project that inspired "My (public) Git Workflow"). (Some people have been using it to consume Fire Eagle's Location Streams, but it hasn't been for the faint of heart.)
Switchboard's primary strength is certainly the command-line interface, but it
turns out that it's a pretty good abstraction over xmpp4r
for building
clients and servers (as components).
Here are a few projects I've worked on that use Switchboard:
The first thing you'll probably want to do with Switchboard is to provide some basic configuration so you won't have to constantly enter your login credentials:
$ switchboard config jid jid@example.com
$ switchboard config password pa55word
To get the value of a setting, don't include a value:
$ switchboard config jid # => jid@example.com
Some additional useful settings to set defaults for are:
debug
- Turn on verbose mode: good for debugging. (true
/ false
)resource
- Override the default resource of /switchboard
(don't include
the /
). This is useful if you want to run multiple instances with the same
JID. This is equivalent to switchboard --resource <resource>
. (String)pubsub.server
- Specify a default server to make PubSub requests against.
This is equivalent to switchboard pubsub --server <server>
. (String)oauth
- Use OAuth when making PubSub requests. This
is equivalent to switchboard pubsub --oauth
and requires that the
oauth
gem be installed. (true
/
false
)oauth.consumer_key
- Specify a default OAuth consumer key. This is
equivalent to switchboard pubsub --oauth-consumer-key <key>
. (String)oauth.consumer_secret
- Specify a default OAuth consumer secret. This is
equivalent to switchboard pubsub --oauth-consumer-secret <secret>
.
(String)oauth.token
- Specify a default OAuth token. This is equivalent to
switchboard pubsub --oauth-token <token>
. (String)oauth.token_secret
- Specify a default OAuth token secret. This is
equivalent to switchboard pubsub --oauth-token-secret <secret>
. (String)In general, anything that is referred to in the library in the form
OPTIONS["pubsub.node"]
can be set as a Switchboard setting.
Roster manipulation is something that can be easily handled by a desktop XMPP client, but sometimes it's more convenient to be able to do it from the command-line.
Rosters can be listed, added to, or removed from. I'll assume you've configured Switchboard with some login credentials.
$ switchboard roster list
$ switchboard roster online
$ switchboard roster add friend1@example.org friend2@example.org
$ switchboard roster remove friend2@example.org enemy@example.org
XMPP provides some pretty heady functionality when it comes to determining
what a server is capable of. A disco#info
query is the first step to use
when determining capabilities:
$ switchboard disco --target jabber.org info
The response to this query includes http://jabber.org/protocol/disco#items
,
which means that jabber.org
supports disco#items
queries, which allow you
to determine what top-level items (services) are available:
$ switchboard disco --target jabber.org items
This response includes conference.jabber.org
. Let's list items available
there:
$ switchboard disco --target conference.jabber.org items
Whoa. A list of MUCs (multi-user chats) hosted on conference.jabber.org
.
You can do the same thing to list available PubSub nodes if you're running a
local copy of ejabberd or another XMPP server that
supports it. (Note that you may need to prefix your hostname with pubsub
in
order to see the nodes.)
Switchboard supports more of PubSub than any other extension, mainly because that's been my primary focus of XMPP experimentation. To get a full list of available PubSub commands:
$ switchboard pubsub
A basic sequence of events is to subscribe:
$ switchboard pubsub --server <server> subscribe --node <node>
List subscriptions:
$ switchboard pubsub --server <server> subscriptions
Listen for notifications:
$ switchboard pubsub --server <server> listen
Unsubscribe:
$ switchboard pubsub --server <server> unsubscribe --node <node>
Let's walk through a couple examples.
First, Superfeedr, which bills itself as "real-time feed parsing in the cloud". To begin, you'll need to register and activate your account. Once you've done that, set up a subscription a Twitter search for "xmpp":
$ switchboard --jid <username>@superfeedr.com --password <password> \
pubsub --server firehoser.superfeedr.com \
subscribe --node "http://search.twitter.com/search.atom?q=xmpp"
We would next list subscriptions, but Superfeedr uses a non-standard mechanism to do so. Instead, let's listen for new results:
$ switchboard --jid <username>@superfeedr.com --password <password> \
pubsub --server firehoser.superfeedr.com listen
If you're lucky, you'll get an Atom payload or two. Here's one:
<event xmlns='http://jabber.org/protocol/pubsub#event'>
<status feed='http://search.twitter.com/search.atom?q=xmpp' xmlns='http://superfeedr.com/xmpp-pubsub-ext'>
<http code='200'>16933 bytes fetched in 0.600034s</http>
<next_fetch>2009-07-22T17:26:22Z</next_fetch>
</status>
<items node='http://search.twitter.com/search.atom?q=xmpp'>
<item chunk='1' chunks='1'>
<entry xmlns='http://www.w3.org/2005/Atom'>
<title>Great... trillian update has killed my ability to view my xmpp rosters</title>
<summary>Great... trillian update has killed my ability to view my <b>xmpp</b> rosters</summary>
<link href='http://superfeedr.com/entries/tr5gfgstf8oqlcgr5opaeotxk39ovtos0oiat7h12mqfuoxdmgbjz1rnjtzswqvja2dqh8cgg31' rel='alternate' type='text/html'/>
<published>2009-07-22T17:10:52Z</published>
<id>tag:search.twitter.com,2005:2781212809</id>
</entry>
</item>
<item chunk='1' chunks='1'>
<entry xmlns='http://www.w3.org/2005/Atom'>
<title>usando Tkabber: TKabber is Tcl/Tk Jabber-client with great functionality. It supports MUC, XMPP-statuses a.. http://bit.ly/AsFPg</title>
<summary>usando Tkabber: TKabber is Tcl/Tk Jabber-client with great functionality. It supports MUC, <b>XMPP</b>-statuses a.. <a href="http://bit.ly/AsFPg">http://bit.ly/AsFPg</a></summary>
<link href='http://superfeedr.com/entries/w6cezbjniqqgga3bd79ruklxhu7f4a6qqpro76hgz50gzccuekgmehz39yb1zi1cclgo83s' rel='alternate' type='text/html'/>
<published>2009-07-22T17:07:54Z</published>
<id>tag:search.twitter.com,2005:2781162048</id>
</entry>
</item>
</items>
</event>
It includes a Superfeedr-specific <status/>
element with information on the
most recent fetch as well as standard Atom feeds contained within standard
<item/>
elements. This means that you can extract the Atom elements from the
PubSub payload and hand it to a feedparser of some variety to work its magic.
Fire Eagle's Location Streams also use PubSub, but subscription management requires that requests are signed using OAuth. This allows 3rd party applications to make requests on behalf of specific users without having to obtain their actual credentials (in short, it's exactly the same use-case for OAuth over HTTP).
Let's start with a subscriptions list request, since we have the credentials ("General Purpose Access Token") immediately after registering a "web" application with Fire Eagle.
$ switchboard pubsub --oauth \
--oauth-consumer-key <consumer key> \
--oauth-consumer-secret <consumer secret> \
--oauth-token <general token> \
--oauth-token-secret <general token secret> \
--server fireeagle.com \
subscriptions
Odds are, you'll have nothing there. Let's change that. Send yourself through the authorization process in order to get a valid OAuth token and secret:
$ oauth --consumer-key <consumer key> \
--consumer-secret <consumer secret> \
--access-token-url https://fireeagle.yahooapis.com/oauth/access_token
--authorize-url https://fireeagle.yahoo.net/oauth/authorize
--request-token-url https://fireeagle.yahooapis.com/oauth/request_token
authorize
With that token and secret, subscribe to your Location Stream:
$ switchboard pubsub --oauth \
--oauth-consumer-key <consumer key> \
--oauth-consumer-secret <consumer secret> \
--oauth-token <token> \
--oauth-token-secret <token secret> \
--server fireeagle.com \
subscribe --node "/api/0.1/user/<token>"
Now you'll have a subscription to query for:
$ switchboard pubsub --oauth \
--oauth-consumer-key <consumer key> \
--oauth-consumer-secret <consumer secret> \
--oauth-token <general token> \
--oauth-token-secret <general token secret> \
--server fireeagle.com \
subscriptions
Listen for location updates:
$ switchboard pubsub --server fireeagle.com listen
Update your current location and watch as the update rolls in. If you'd like to visualize updates with Google Earth, check out fire-hydrant on GitHub.
We're done, so we may as well clean up and unsubscribe:
$ switchboard pubsub --oauth \
--oauth-consumer-key <consumer key> \
--oauth-consumer-secret <consumer secret> \
--oauth-token <token> \
--oauth-token-secret <token secret> \
--server fireeagle.com \
unsubscribe --node "/api/0.1/user/<token>"
PEP is a specialized version of PubSub, intended to allow individuals to associate data with their JIDs. Switchboard supports publishing of User Tune and User Location.
Due to the ability of XMPP to allow multiple instances of the same account online (identified with different resources), Switchboard can serve up User Tune and User Location for the same account you're already online with. Not every client supports displaying them, but they're fun to play with regardless.
To publish User Tune, you need to be on a Mac, running iTunes, and have the
rb-appscript
gem installed (sudo gem install rb-appscript
). Once that's
done:
$ switchboard --resource switchtunes pep tune
To publish User Location, you need to be updating Fire
Eagle
(Clarke is an excellent background
updater for OS X) and have the fire-hydrant
gem installed from GitHub (sudo
gem install mojodna-fire-hydrant -s http://gems.github.com
). Once that's
square:
$ switchboard --resource switchfire pep location
Switchboard supports more functionality than I've described above. To get a list of general switchboard
commands (some of which may have sub-commands):
$ switchboard
In theory, if you want more information about a specific command, you can use
switchboard help <command>
. For example:
$ switchboard help pubsub
For now, you'll notice that it's not particularly useful. If you'd like to
help rectify this, you can implement various help
methods that are lying
around, such as Switchboard::Commands::PubSub.help
.
Writing new Switchboard commands is really easy, assuming that the primary
application logic that you're depending exists elsewhere (i.e. in xmpp4r
).
I was on a panel with Peter St. Andre and Jack Moffitt at the Glue Conference in Denver this Spring and we got to talking about tools like Switchboard. Jack wondered how hard it would be to implement something like grep
for XMPP.
(Jack is one of the authors of a Python project similar to Switchboard named poetry
).
I took a whack at it and it came out like this:
module Switchboard
module Commands
class Grep < Switchboard::Command
description "Search for an XPath expression"
def self.run!
expr = ARGV.pop
switchboard = Switchboard::Client.new
switchboard.plug!(AutoAcceptJack, NotifyJack)
switchboard.on_stanza do |stanza|
# TODO doesn't handle default namespaces properly
REXML::XPath.each(stanza, expr) do |el|
puts el.to_s
end
end
switchboard.run!
end
end
end
end
Jacks (not Moffitt) deserve their own discussion, but the thrust of this
piece of code is the #on_stanza
callback (which yields a REXML::Node
object) and the XPath expression. Note that for whatever reason, you can't
search for nodes that have a default namespace (e.g. <presence />
); I'm
going to assume that this is a REXML quirk.
If you want to take a shot at implementing a Switchboard command,
Ping should be pretty simple.
Alternately, Switchboard doesn't support sending or receiving basic <message
/>
stanzas from the command-line. Supporting those would make it simple to
interact with a service like identi.ca.
There are a few simple ways to help out with Switchboard's development:
What's your favorite use for Switchboard?