A simple DSL for creating Jabber agents
Switch branches/tags
Nothing to show
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.




Uppercut is a little DSL for writing agents and notifiers which you interact with via your Jabber client.

Making an Agent

You could put together a very simple agent as follows:

    class BasicAgent < Uppercut::Agent
      command 'date' do |c|
        m.send `date`
      command /^cat (.*)/ do |c,rest|
        m.send File.read(rest)
      command 'report' do |c|
        m.send 'Hostname: ' + `hostname`
        m.send 'Running as: ' + ENV['USER']

With the above code, we’ve created an Agent template of sorts. It responds to three commands: {date cat report}.
The block which is passed to “command” should always have at least one paramater. The first parameter will
always be an Uppercut::Conversation object, which can be used to respond to the user who sent the input. When passing
a regular expression as the first parameter to “command”, all of the captures which the pattern match generates
will also be passed to the block. This can be seen in the “cat” example above. There is one capture and it is
passed in as rest.

Then to actually put the Agent to work…


This creates a new BasicAgent instance and sends it listen to make it start accepting messages.
Note that by default when an agent is listening, it ignores all errors. (In the future, I’ll have it log them.)

There are also event callbacks for agents. These events are based on XMPP presence messages. So, as of this writing,
the list of allowed callbacks is: signon, signoff, subscribe, unsubscribe, subscription_approval, subscription_denial,
status_change, and status_message_change.

You can use these callbacks as follows:

    class CoolAgent < Uppercut::Agent
      on :subscribe do |c|
        c.send 'Hey dude, thanks for subscribing!'

      on :signoff do |c|
        puts "LOG: #{c.to} signed off."

Some callbacks only work if the user allows the agent to subscribe to their presence notifications. This happens
automatically when a user subscribes to your agent. (Depending on their client) they’ll be presented with a choice
to authorize or deny the subscription. You can catch their answer with the ‘subscription_approval’ and ‘subscription_denial’
callbacks. Without their approving the subscription, your ‘signon’, ‘signoff’, ‘status_change’, and ‘status_message_change’
callbacks will not be fired by them. I suggest using the ‘subscription_denial’ callback to inform them of that.

Making a Notifier

    class BasicNotifier < Uppercut::Notifier
      notifier :error do |m,data|
        m.to 'tbmcmullen@gmail.com'
        m.send "Something in your app blew up: #{data}"

So, we make a new Notifier class and call a notifier block within it. This makes a notifier with the name :error, which sends a message to tbmcmullen@gmail.com when it fires.

    notifier = BasicNotifier.new('user@server/resource','password').connect
    notifier.notify :error, 'Sprocket Error!'

The purpose of a Notifier is just to make a multi-purpose event-driven notification system. You could use it notify yourself of errors in your Rails app, or ping you when a new comment arrives on your blog, or … I dunno, something else.

The way I intend a Notifier to be used though is with Starling. Take the following example…

    notifier = BasicNotifier.new('user@server','password', :starling => 'localhost:22122', :queue => 'basic')
    require 'starling'
    starling = Starling.new('localhost:22122')
    starling.set 'basic', :error

In that example, we have the same BasicNotifier. Except this time, we provide it with information on which Starling queue on which Starling server to monitor. Meanwhile, in the separate program, we attach to that same Starling server and push a symbol onto that same queue.

In this case, what happens is BasicNotifier sees that symbol and begins processing it, just as if it had been passed in via the #notify method.



If you intend to use this on an open Jabber network (read: Google Talk) take some precautions. Agent#new takes an optional list of JIDs that the agent will respond to.

    BasicAgent.new('user@server/res', 'pw', :roster => ['you@server'])

The agent created with the above statement will not respond to messages or subscription requests from anyone other than ‘you@server’.


Uppercut is currently very thin on features, as it’s in its infancy. Here’s a brief list of where I’m
currently intending to take the project:

  • send files to and receive files from agents
  • improving the way one writes executables (using the Daemons library is best for the moment)
  • auto-updating of agents
  • I swear I’m going to find a use for MUC
  • allow agents to establish communications on their own, rather than being reactionary (think RSS updates)
  • … other stuff that sounds fun to code up …