Tcl Scripting HOWTO

# Intoduction

NOTA BENE: This documentation is badly out of date for 2.x.

The libpurple Tcl interface provides a Tcl API for many useful libpurple functions. Like the perl API, the Tcl API does not provide access to every corner of libpurple exposed by the C interface. It does, however, provide a very powerful interface to many of libpurple's functions through a simple to learn and extend scripting language.

If you are not familiar with Tcl, you will probably find it somewhat different from what you are used to. Despite being somewhat unique (more akin to shell programming than other traditional scripting languages such as perl or python), it is simple to learn for beginners and experienced programmers alike. There are numerous books on the subject; we will not discuss it any further here.

# Getting Started

The only requirement placed on a purple Tcl script by libpurple is the existence of a procedure called `plugin_init`. This procedure has some limitations placed upon it; it will be parsed and evaluated before the rest of the Tcl script, so it cannot reference any other variables or procedures declared in the script. In practice this is not a problem, as the only thing this procedure should do is return a simple list containing five items: the name of the script, its version number, a summary (just a few words) of its function, a short (longer than the summary, but no more than a couple of sentences if possible) description, the author, and a URL to web page. For example:

proc plugin_init { } {
return [ list "Example Plugin" \
"1.0" \
"Example plugin registration" \
"Example of how to register a plugin for the Tcl HOWTO" \
"Ethan Blanton <eblanton@cs.purdue.edu>" \
"http://pidgin.im/" ]
}

The rest of the script will generally be registration to recieve notification of various purple (or Pidgin, or finch, or ...) signals (more about this below) and definitions of procedures to be executed when those signals occur.

# Interpreter Details

libpurple initializes and drives the Tcl event loop (similar to Tk), meaning that commands like `fileevent` and `after` are available and do not require `vwait` etc. The `vwait` actually seems to be somewhat broken due to a bug somewhere in the Tcl/Glib event loop glue, and it should not be used for now.

The purple-specific functions are provided in a statically-linked package called `purple`; this means that if you spawn a child interpreter and wish to use the purple-specific functions, you will need to execute `load {} purple` in that interpreter.

# purple Internal Procedures and Variables

All of the information provided for your use by purple will be in the `::purple` namespace. This means that in order to access it you will either have to import the purple namespace (e.g. via the command `namespace import purple::*`) or reference it explicitly. The following descriptions will reference it explicitly for clarity.

- Variables

purple::version

This contains the version of the libpurple library which loaded the script.

- Commands

purple::account alias account
purple::account connect account
purple::account connection account
purple::account disconnect account
purple::account find username protocol
purple::account handle
purple::account isconnected account
purple::account list ?option?
purple::account protocol account
purple::account username account

The `purple::account` command consists of a set of subcommands pertaining to purple accounts.

`alias` returns the alias for the account `account`. If there is no alias for the given account, it returns the empty string.

The subcommand `connect` connects the named account if it is not connected, and does nothing if it is. In either case, it returns the `gc` for the account.

`connection` returns the `gc` of the given account if it is connected, or 0 if it is not. This `gc` is the gc used by purple::connection and other functions.

`disconnect` disconnects the given `account` if it is connected, or does nothing if it is.

`find` finds an account by its `username` and `protocol` (as returned by `purple::account username` and `purple::account protocol`) and returns the account if found, or 0 otherwise.

`handle` returns the instance handle required to connect to account signals. (See `purple::signal connect`).

The `isconnected` query returns true if the given account is connected and false otherwise.

The `list` subcommand returns a list of all of the accounts known to libpurple. The elements of this lists are accounts appropriate for the `account` argument of the other subcommands. The `-all` option (default) returns all accounts, while the `-online` option returns only those accounts which are online.

The `protocol` subcommand returns the protocol ID (e.g. "prpl-aim") for the given account.

The `username` subcommand returns the username for the account `account`.

purple::buddy alias buddy
purple::buddy handle
purple::buddy info ( buddy | account username )
purple::buddy list

`purple::buddy` is a set of commands for retrieving information about buddies and manipulating the buddy list. For the purposes of Tcl, a "buddy" is currently a list of several elements, the first of which being the type. The currently recognized types are "group", "buddy", and "chat". A group node looks like:

{ group name { buddies } }

A buddy node is:

{ buddy name account }

And a chat node is:

{ chat alias account }

The `alias` subcommand returns the alias for the given buddy if it exists, or the empty string if it does not.

`handle` returns the blist handle for the purposes of connecting signals to buddy list events. (See `purple::signal connect`).

`info` causes the purple-using UI to display the info dialog for the given buddy. Since it is possible to request user info for a buddy not in your buddy list, you may also specify a buddy by his or her username and the account through which you wish to retrieve info.

`list` returns a list of `group` structures, filled out with buddies and chats as described above.

purple::connection account gc
purple::connection displayname gc
purple::connection handle
purple::connection list
purple::connection state

`purple::connection` is a collection of subcommands pertaining to account connections.

`account` returns the purple account associated with `gc`. This account is the same account used by `purple::account` and other commands.

`displayname` returns the display name (duh) of `gc` as reported by `purple_connection_get_display_name(gc)`.

`handle` returns the purple connections instance handle. (See `purple::signal connect`).

`list` returns a list of all known connections. The elements of this list are appropriate as `gc` arguments to the other `purple::connection` subcommands or other commands requiring a gc.

`state` returns the PurpleConnectionState of this account as one of the strings "connected", "disconnected", or "connecting".

purple::conv_send account who text

`purple::conv` is simply a convenience wrapper for `purple::send_im` and `purple::conversation write`. It sends the IM, determines the from and to arguments for `purple::conversation write`, and prints the text sent to the conversation as one would expect. For the curious, you may view the source for it by typing `info body purple::conv_send` at a Purple Commander prompt.

Note that an error in either `purple::send_im` or `purple::conversation write` will not be caught by this procedure, and will be propagated to the caller.

purple::conversation find ?-account account? name
purple::conversation handle
purple::conversation list
purple::conversation new ?-chat? ?-im? account name
purple::conversation write conversation style from to text

`purple::conversation` provides an API for dealing with conversations. Given that libpurple clients are instant messenger programs, you'll probably spend a lot of time here.

The command `find` attempts to find an existing conversation with username `name`. If the `-account` option is given, it refines its search to include only conversations on that account.

`handle` returns the conversations instance handle for the purposes of signal connection. (See `purple::signal connect`).

`list` returns a list of all currently open conversations.

The `new` subcommand can be used to create a new conversation with a specified user on a specified account if one does not exist, or retrieve the existing conversation if it does. The `-chat` and `-im` options specify whether the created conversation should be a chat or a standard IM, respectively.

`write` is used to write to the specified conversation. The `style` argument specifies how the text should be printed – as text coming from the purple user (style `send`), being sent to the purple user (style `recv`), or as a system message (such as "so-and-so has signed off", style `system`). From is the name to whom the text should be attributed – you probably want to check for aliases here, lest you confuse the user. `text` is the text to print.

purple::core handle
purple::core quit

This command exposes functionality provided by the purple core API.

`purple::core handle` returns a handle to the purple core for signal connection. (See `purple::signal connect`).

`quit` exits the libpurple client cleanly, and should be used in preference to the tcl `exit` command. (Note that `exit` has not been removed, however.)

purple::debug level category message

Equivalent to the C purple_debug function, this command outputs debugging information to the libpurple UI's debug window (or, typically, stdout if that UI is invoked with -d|–debug). The valid levels are, in increasing level of severity, `-misc`, `-info`, `-warning`, and, or `-error`. `category` is a short (a few characters ... for instance, "tcl" or "tcl plugin") "topic" type name for this message, and `message` is the text of the message. In the style of Tcl puts (and differing from purple_debug), no trailing \n is required. (However, embedded newlines may be generated with \n).

purple::notify ?type? title primary secondary

Also a direct equivalent to a C function, purple_notify, this command causes libpurple to present the provided notification information to the user via some appropriate UI method. The `type` argument, if present, must be one of `-error`, `-warning`, or `-info`. The following three arguments' absolute meanings may vary with the purple UI being used, but `title` should generally be the title of the window, and `primary` and `secondary` text within that window; in the Pidgin UI, `primary` is slightly larger than `secondary` and displayed in a boldface font.

purple::send_im gc who text

This sends an IM in the fashion of serv_send_im. `gc` is the GC of the connection on which you wish to send (as returned by most event handlers), `who` is the nick of the buddy to which you wish to send, and `text` is the text of the message.

purple::signal connect instance signal args proc
purple::signal disconnect instance signal

`purple::signal` is a set of subcommands for dealing with purple signals.

The `connect` subcommand registers the procedure `proc` as a handler for the signal `signal` on the instance `instance`. `instance` should be an instance handle as returned by one of the `handle` commands from the various parts of libpurple. `args` and @ proc are as in the Tcl proc command; note that the number of arguments in `args` must match the number of arguments emitted by the signal exactly, although you need not use them all. The procedure `proc` may be either a simple command or a procedure in curly brackets. Note that only one procedure may be associated with each signal; an attempt to connect a second procedure to the same signal will remove the existing binding and replace it with the new procedure. `purple::signal connect` returns 0 on success and 1 on failure.

`disconnect` removes any existing signal handler for the named signal and instance.

purple::unload

This unloads the current plugin. Note that preferences will not be updated (yet).

# Signals

Check the signals documentation for the meaning of these signals; this is intended to be a list only of their arguments. Signal callbacks will be made in their own namespace, and arguments to those signal callbacks will live in the namespace `event` underneath that namespace. To briefly illustrate, the signal `receiving-im-msg` is provided with three arguments; the account on which the IM was received, the name of the buddy sending the IM, and the text of the IM. These arguments live in the variables `event::account`, `event::sender`, and `event::buffer`, respectively. Therefore a callback which notifies the user of an incoming IM containing the word 'shizzle' might look like this:

purple::signal connect [purple::conversation handle] receiving-im-msg {
if {[ string match "*shizzle*" $event::buffer ]} {
purple::notify -info "tcl plugin" "Fo' shizzle" \
"$event::sender is down with the shizzle"
}
}

Note that for some signals (notably `receiving-im-msg`, `sending-im-msg`, and their chat counterparts), changes to the event arguments will change the message itself from libpurple's vantage. For those signals whose return value is meaningful, returning a value from the Tcl event will return that value as it would in C.