jab -- CLI-based Jabber client of/by/for Ruby

Author:	Chip Camden
Date:	August, 2010
Site:	http://chipstips.com/?tag=rbjab
Hg:	http://bitbucket.org/sterlingcamden/jab


jab provides a minimal, text-only, serial (no cursing) XMPP or IRC client
written in Ruby, that uses Ruby as its command language.  jab provides
methods to handle the common set of chat tasks, but of course you can
execute any Ruby code within its read-eval loop.

jab binds the execution context of all evaluated commands to a
proc inside the Jab class.  This means any local variables you define
will not conflict with variables in the calling program, and any
methods or instance variables you define will be contained within the
Jab class.

To set up your environment each time you start jab, you can place
commands in a file named ~/.jabrc -- if that file exists, it will be
executed before jab asks you for input.  You can also override the name 
of this file with the -r command-line option.  Typical commands placed in
.jabrc include connecting to the server, defining aliases, selecting
colors, enabling extensions, and user-specific functions.  An example
.jabrc file is included with this distribution as dot.jabrc, along with
two files that it relies upon: .jab-aliases (dot.jab-aliases) and 
.jab-colors (dot.jab-colors).  A second example can be found in dot.jabrc-irc,
which shows how to use jab for IRC.  You can use these as patterns for
creating your own customized jab session.

For the list of common commands, type 'help' at the prompt (:).  Help
pages its output by piping it to the executable named in the PAGER
environment variable, or "less" if PAGER is not set.  For full command
reference, see the REFERENCE section below.

jab supports command history and either vi-style or emacs-style editing,
using Readline.  Whenever a prompt includes a list of possible
responses, command completion is available by pressing Tab.  You can
suppress the use of Readline by setting @@use_readline to false.

jab does not provide conversation logging, but you can easily accomplish
that by redirecting output through tee(1).  jab does not separate multiple,
simultaneous conversations, but it does provide visual cues to tell you
not only who said something but to whom your next line of text will go if
you don't switch jabs.

Because chat operates asynchronously, you will often receive a message or
notification while composing a message.  If that confuses you to the
point of not being able to finish your thought, use Ctrl+U to erase your
pending input, press Return to get a fresh prompt, and begin again.  An
empty line is always ignored when entering commands or messages.  If you
panic and press Ctrl+C, no worries -- the interrupt will be reported to
you, and you'll get another prompt.  Alternatively, see the provided
extension 'keep-prompt', which scrolls all output above the bottom line
and keeps the prompt line intact.

When you're all done, you can press EOF (usually Ctrl+D on Unix systems)
to exit jab, or use the 'q' or 'exit' commands.


You must first install Ruby and the xmpp4r gem.

Place the file jab.rb in a directory that is included in your Ruby load
path.  To see what directories are included, use the command:

ruby -e "puts $:"

You can also specify a directory to add to the load path using Ruby's -I

Place the file jab in a directory in your PATH, unless you want to always
execute it by specifying the full path.

Edit default commands into ~/.jabrc, and you're ready to go!



-e, --emacs		Use emacs line editing (default is vi)
-h, --help		Display help and exit
-r, --rcfile RCFILE	Use a different startup file (can use more than one)
-v, --version		Display version and exit


This section documents all of the methods of the Jab class, which can
naturally be used as commands in jab along with other Ruby statements.


about => nil

Prints version, author, and web site information.


ask(prompt, silent=nil, call=nil) => string or nil

Displays prompt to the user (applying any color filters), and returns the
user's response.  If silent is true and $stdin is a terminal, turns echo
off and back on using stty(1).  Prompt is prefixed by the name of the
caller, in parentheses -- but that can be suppressed by passing call as
''.  You can also override the name of the caller by passing call as a
string containing the desired caller in `' (back-quote, single-quote).

The prompt may be further filtered by providing a proc for the :prompt
event.  See 'on' and EVENTS.

If the user presses the EOF key, ask returns nil -- otherwise, whatever
the user entered.  However, input may be filtered by assigning a proc
to the :input event.  See 'on' and EVENTS.

When $stdin is a terminal, command line editing and recall are enabled
using the Readline module.  Editing mode is vi by default, but can be
overridden to emacs using the -e command line switch.


bg(color) => string

Synonym for get_color('AB', color).


signal_event(event, *args) => Object or nil

Calls all procs associated with event, in the order they were specified.
Returns the result of the last such proc, or nil if no such events are
specified.  See 'on'.  Any arguments passed to signal_event will in turn
be passed to each proc.


color(what, *how) => Array

Adds a color filter to all output.  The what parameter is required and
must be a regular expression (or something that can be converted into a
regular expression via Regexp.new) that specifies the portion of the
string to which to apply color.  Thus, if you want to color the whole
string, be sure that your expression includes the beginning and end of
the string.

The remaining parameters specify colors to apply.  They should be
terminal-specific strings of escape sequences to apply colors.  You can
generate these sequences using the 'fg', 'bg', or 'get_color' methods.
The sequences will be prefixed, in order, to the matching portion of the
string.  If one or more sequences are specified, then the matching
portion of the string will be followed by the terminal-specific sequence
for the "me" termcap capability.

Care should be taken when specifying two or more color filters that may
apply to the same string.  All color filters will be applied in the order
specified, but the application of one filter may prevent another one from
matching -- because each escape sequence gets inserted into the original

Some examples of color filters can be found in the file dot.jab-colors.


connect(user=nil, password=nil) => Boolean

Connects to a server.  The user argument should be in the form
"user@domain/resource" for XMPP (Jabber).  When using the 'protocol-irc'
extension, it should be in the format "nick@domain".  If nil, the user
will be asked to enter it.  Likewise, if password is not provided, the
user will be asked for it, using the silent option on 'ask'.  Passing an
empty string (or pressing Enter at the prompt) will leave the password
unspecified (common for IRC).

Returns true if the connection was established.  Returns false if the
user entered EOF at any prompt.  Raises an error if the connection

After a connection is successfully established, the :connect event is


disconnect => nil

Disconnects any current session.  In interactive mode, 'interact' will
immediately prompt for a new connection.

Prior to closing the connection, the :disconnect event is signaled.


filter_event(event, arg) => Object

Applies the procs associated with event as filters to arg.  Each
proc associated with event (if any) is called in the order specified,
passing arg as an argument.  Whatever the proc returns becomes the new
value of arg.  Returns the final value, or the original value if no
procs are defined for event.  See 'on'.


fg(color) => string

Synonym for get_color('AF', color).


get_color(cap, color) => string

Returns the character sequence for the termcap capability named cap for
the current terminal, given the argument color.  By default, this uses
tput(1), but you can override that by setting @@termcap['AF'] and

While this is used for colors in jab, it could also be used for any
termcap capability (by passing any arguments joined by spaces in color).


getcmd => string or nil

Asks the user for input using only a ':' prompt (still applying any color
filter).  Rescues Interrupt and prints "interrupted" instead of a full
exception.  Returns the command typed, or '' if interrupted, or nil if
the user entered EOF.


handle_requests => Boolean

Asks the user to confirm any pending subscription requests when the
subscription handling action is :ask (see 'when_tapped').  This method
is called automatically within the 'interact' loop, so you only need to
call it if you are using these methods outside of 'interact'.


help => nil

Outputs helpful information by piping it through the utility named in the
environment variable PAGER.  If PAGER is not set, "less" is used.


hush(what) => false

Synonym for notify(what, false).


id_verbosity(level) => nil

Sets the verbosity level for usernames in messages.  The default is 2.
For the Jabber protocol, the following levels apply:

0 = Just the username portion
1 = Username@domain
otherwise = Username@domain/resource

For the IRC protocol, if set to 0, only the username is displayed.
Otherwise, the entire user ID is displayed.


interact => nil

A read/eval loop for interactive jabbing.  As long as the user does not
press EOF or enter some equivalent of 'exit', the loop continues.  Each
command is evaluated within the sandboxed binding.  Any errors are
rescued and reported to the user.

This method cannot proceed without a connection, so if one is not
established it will ask the user for login credentials and attempt to
connect.  If a connection is not established, this method also returns


interject(type, sender, message) => nil

Provides a layer over 'say' for filtering messages based on notification
preferences.  The type argument is a symbol for filtering, per the
'notify' method -- typically one of :errors, :info, :status, :jabs, or
:always.  If the corresponding element of the @notify array is true, the
message will be displayed, otherwise suppressed.

The sender argument becomes the first element of the message.  It may be
nil to suppress that portion.

The message will be terminated with a newline.


irc(command) => nil

When using IRC, sends command directly to the server.

This command has no effect when using XMPP.


jab(whom=nil, message=nil) => nil

Sends a chat message to whom.  The whom argument should be in the
form 'user@domain' or 'user@domain/resource' for XMPP -- for IRC it can
be either a nickname or a channel.  The user will be asked for
it if nil.

If the message argument is nil, jab accepts messages in a loop until EOF.
Each prompt includes the destination, so you can easily tell to
whom you are chatting.  When using IRC, a line of text beginning with '/'
will be taken as an IRC command.

Message send errors are not synchronous, so this method always returns
nil.  If an error occurs, it will be displayed to the user via
'interject' with type :errors.


multiask(choices, default=nil, call=nil) => string or nil

Uses ask to retrieve a multiple-choice answer from the user.  The choices
argument should be an array of strings containing the possible choices,
and the prompt will be composed of these, separated by '/'.  If default
is non-nil, the choice that matches it will be enclosed in [] in the
prompt, and will be returned if the user enters only Return.  Otherwise,
the user's input must match one of the choices.  Auto-completion is
enabled through the Readline module to match one of the choices.

The call parameter behaves exactly as for 'ask' -- with the exception
that if not passed, it defaults to the caller of multiask.


on(event, &proc=nil) => Array

Adds to and/or returns the list of procs to call on an event.  The event
parameter is a symbol (or will be converted to a symbol) that is used to
identify the event.  For the list of events that are used by jab, see
EVENTS below.  Of course, you may add and signal your own events as well.
See 'signal_event' and 'filter_event'.

If proc is non-nil, it will be added to the list of procs to call for
this event.  In any case, this method returns that list.


notify(what=nil, how) => Boolean

Enables or disables a class of notifications.  The what argument will
typically be one of the following:

:all		combination of :errors, :info, :jabs, and :status
:errors		error messages
:info		informational messages
:jabs		messages from other users
:status		status updates from users

If what is not passed, the user will be asked for one of the above

If how is true, the specified notification(s) will be enabled, otherwise

If you pass what, it can be something other than one of the above, which
allows you to create a new class of notifications for you own use.  I'd
recommend avoiding :always, as that is used internally for notifications
that may not be suppressed.

Returns how.



Synonym for 'exit'.


say(what) => nil

Prints what to $stdout, after applying any color filters.


settings => nil

Prints current settings to $stdout, after applying any color filters.

The following settings are reported for XMPP:

status :state, 'message'	# current availability status
hush :type			# any suppressed notifications
when_tapped :action		# how to handle subscription requests

The following settings are reported for IRC:

domain, port, nickname
the result of sending an INFO command to the server


shove(whom=nil) => nil


Sends an :unsubscribe request to whom.  If whom is nil, the user will be
asked for it.

While there is a supported mechanism to receive responses to this
request, I have yet to see any server provide them.


Sends a PART command for channel specified by whom.


source(file=nil, ignore_fnf=nil) => Object or nil

Processes commands from file.  if file is nil, the user will be asked for
it.  Returns the result of evaluating file -- or nil if an error occurred,
the user pressed EOF, or the file does not exist and ignore_fnf is true.

The entire file is evaluated in one eval statement, so statements can
cross line boundaries (unlike within 'interact').  If an error occurs, it
is rescued and reported to the user.  As with 'interact', all commands
are evaluated within a sandboxed binding.

If ignore_fnf is true, then if the file does not exist no error occurs.
Otherwise, an Errno:ENOENT error is reported.


status(state=nil, message=nil) => Jabber::Presence or nil

Set the user's current status.  State must be one of :available, :away,
:chat, :dnd (do not disturb), or :xa (extended away), but may be a string
instead of a symbol.  If nil, the user will be asked, with the default being

The message argument is an optional text description of the status.  If
nil, the user will be asked.  Use an empty string for no message.

Returns the new Jabber::Presence resulting from the specified status, or
nil if the user pressed EOF at any prompt.

This command has no effect when using IRC.  Use "irc away [message]" or
/away at a jab prompt to set away status.


tap(whom=nil) => nil


Sends a :subscribe request to whom.  If whom is nil, the user will be
asked for it.

Most XMPP servers respond to this message with a status update if you
have successfully subscribed now or in the past, so you can use this
command to check status of an existing friend as well as to subscribe to
a new one.


Sends a JOIN command for the channel specified by whom.


unhush(what) => true

Synonym for notify(what, true).


when_tapped(action) => Symbol or nil

Specify how to handle incoming subscription requests.  Action may be one
of the following, or its string equivalent:

:ask	Ask the user on the next call to 'handle_requests'
:y	Always approve
:n	Always decline

If action is nil, the user will be asked for one of the above.  If the
user presses EOF, returns nil, otherwise the symbol.

This command has no effect when using IRC.



You may define and signal any number of events, but the folloiwng list
indicates pre-defined events that are signaled by jab.  See 'on', 
'signal_event', and 'filter_event'.

Event		When called			Arguments	Returns
-----		-----------			---------	-------
:connect	'connect' after opening		none		ignored
		the connection

:disconnect	'disconnect' before		none		ignored
		closing the connection

:identity	'interject' before output	user identity	filtered identity

:input		'ask' after user presses Enter	user input	filtered input
						(string or nil)

:interject	'interject' before output	(string)	filtered string

:jab		before displaying an incoming	body of jab	filtered body
		jab (occurs before :interject)	(string)

:prompt		'ask' before prompting user	prompt (string)	filtered prompt
		(and before applying colors)


Because each command is evaluated within a proc inside the Jab class, you
can access instance and class variables of the Jab class.  The following
variables are provided:

@@numcolors 	Number of colors supported by this terminal.  This is only set
	    	on the first call to 'get_color'.

@@me	   	The terminal-specific sequence for the termcap capability "me".
	  	This is only set on the first execution of 'color'.

@@termcap	Hash of termcap capability and command used to retrieve it.
		The default is to use tput.

@@use_readline  Whether to use the Readline module for terminal input.
	      	True by default.

@accept	    	How to handle subscription requests.  One of :ask, :y, or :n.

@client	    	Jabber::Client for the connection.

@colors	    	Array of color filters.  Each element of the Array is an Array
	    	of [Regexp, String], where Regexp is the regular expression of
	    	the match, and String is what to replace it with (using

@domain		When using IRC, the server URL.

@events		Hash of :event => [proc...], hooks to call on certain events.
		Set by 'on'.

@id_verbosity	The verbosity level for user identities.

@listener	When using IRC, the thread that receives messages from the server.

@nick		When using IRC, the user's nickname.

@notify	    	Hash of notification settings.  Each element is Symbol =>
	    	Boolean, where Symbol is one of the notification type symbols,
	    	and Boolean is true if enabled.

@pager	    	Pager command to use for 'help'.

@port		When using IRC, the port number on the host.

@presence   	Jabber::Presence from last status modification for this user.

@real_name	When using IRC protocol, the user's full name.  Must be set
		prior to connecting to take effect.  If not set when connecting,
		the value of $USER will be used to extract the full name from 

@requests   	Queue of pending subscription requests.  Each element of the
	    	Array is a Jabber::Presence from a request.

@roster	    	Jabber::Roster::Helper for the connection.

@sandbox    	Binding for command evaluation.

@statuses	Hash of user => [:status, 'message'] for previously seen status

@user	    	Currently connected username.


The following extensions to jab are provided.  See the comments at the top of
each file for usage, but in general all you need to do is source these in your
.jabrc to obtain their functionality.

notify-new	Updates a file with the number of unread jabs.

keep-prompt	Keeps the prompt visible on the last line of the terminal.
		Relies on specific termcap entries.  Also adds the current time
		to each notification.

protocol-irc	Substitutes the IRC protocol instead of Jabber.  Connects to an IRC
		server, changes the 'tap' command to joing a channel, and 'jab'
		to start talking to a channel or user.  When in iterative jab mode,
		a '/me ' at the beginning of a line will send an ACTION message,
		otherwise a '/' at the beginning of a line indicates an IRC command.
		The extension also adds an explicit 'irc' command.  See the supplied
		file dot.jabrc-irc for a usage example.

Of course you can write your own extensions just as simply as these are implemented.