bitlbee is a program that acts as an IRC server and lets you connect to any number of IM services (Jabber, ICQ, AIM, MSN, and Yahoo) - just using the IRC protocol to talk there. That means you can use any InternetRelayChat client with it!
bitlbee is at http://www.bitlbee.org Or, in Debian,
apt-get -u install bitlbee
Or, in OS X,
port install bitlbee
You can also use the public BitlBee server at im.bitlbee.org:6667, or any other public server from the public server list: http://www.bitlbee.org/main.php/servers.html
Lisp:bitlbee.el allows you to start and stop the bitlbee process from within Emacs. That way you can run a local bitlbee server without having to worry about inetd.
If you are using Mac OS X, the simplest way to have BitlBee start “on demand” is using launchd. Put this text in ~/Library/LaunchAgents?/mac.bitlbee.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC
"-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>mac.bitlbee</string>
<key>ProgramArguments</key>
<array>
<string>/opt/local/sbin/bitlbee</string>
<string>-I</string>
<string>-n</string>
</array>
<key>Sockets</key>
<dict>
<key>Listeners</key>
<dict>
<key>SockNodeName</key>
<string>127.0.0.1</string>
<key>SockServiceName</key>
<string>6667</string>
</dict>
</dict>
<key>inetdCompatibility</key>
<dict>
<key>Wait</key>
<false/>
</dict>
</dict>
</plist>Then logout and log back in for it to become active. – JohnWiegley
See rcircAutoAuthentication which includes a bitlbee example.
The following snippets automagically identify yourself to Bitlbee when your IRC client is a recent ERC:
(defvar bitlbee-password "geheim")
(add-hook 'erc-join-hook 'bitlbee-identify)
(defun bitlbee-identify ()
"If we're on the bitlbee server, send the identify command to the
&bitlbee channel."
(when (and (string= "localhost" erc-session-server)
(string= "&bitlbee" (buffer-name)))
(erc-message "PRIVMSG" (format "%s identify %s"
(erc-default-target)
bitlbee-password))))This snippet defines a function that connects to a local bitlbee server and identifies yourself. Then using bitlbee with Circe is simply a matter of M-x bitlbee
(defun bitlbee ()
(interactive)
(circe "localhost" "6667" "bitlbee")
(save-window-excursion
(set-buffer "localhost:6667")
(with-circe-server-buffer
(circe-server-send
(format "PRIVMSG &bitlbee :identify %s"
my-bitlbee-password)))))Note that, by default, BitlBee assumes your IRC client sends and receives text in iso8859-15. If you need a different character set for your language, you’ll have to tell BitlBee about that. Type the command “help set charset” for more information.
Using bitlbee to chat with people whose clients send everything as HTML can be a pain. Bitlbee provides settings to strip HTML, but you can also render it using w3m.
(add-to-hook 'erc-insert-modify-hook 'mah/maybe-wash-im-with-w3m)
(autoload 'w3m-region "w3m" "Render region using w3m")
(defun mah/maybe-wash-im-with-w3m ()
"Wash the current im with emacs-w3m."
(save-restriction
(with-current-buffer (current-buffer)
(let ((case-fold-search t))
(goto-char (point-min))
(when (re-search-forward "<HTML>.*</HTML>" nil t)
(print (match-string 0))
(narrow-to-region (match-beginning 0) (match-end 0))
(let ((w3m-safe-url-regexp mm-w3m-safe-url-regexp)
w3m-force-redisplay)
(w3m-region (point-min) (point-max))
(goto-char (point-max))
(delete-char -2))
(when (and mm-inline-text-html-with-w3m-keymap
(boundp 'w3m-minor-mode-map)
w3m-minor-mode-map)
(add-text-properties
(point-min) (point-max)
(list 'keymap w3m-minor-mode-map
;; Put the mark meaning this part was rendered by emacs-w3m.
'mm-inline-text-html-with-w3m t))))))))Note that the above was adapted from gnus, so some of those mm- variables may not be what you want.

Ever wondered what this 938748324 person is that sends messages to you. AFAIK bitlbee has no builtin support to query the white pages of ICQ, therefore i quickly hacked up this one:
(defun erc-cmd-ICQWHOIS (uin)
"Queries icq-user with UIN `uin', and returns the result."
(let* ((result (myerc-query-icq-user uin))
(fname (cdr (assoc 'fname result)))
(lname (cdr (assoc 'lname result)))
(nick (cdr (assoc 'nick result))))
(erc-display-message nil 'notice (current-buffer) (format "%s (%s %s)" nick fname lname))))(defun myerc-query-icq-user (&optional uin) "Queries UIN `uin' on people.icq.com, returns result table." (with-timeout (3 (ignore)) (and uin (with-current-buffer (url-retrieve-synchronously (format "http://people.icq.com/whitepages/about_me/1,,,00.html?to=%%25U&Uin=%s" uin)) (let* (;; some cleansing (result (buffer-string)) (result (replace-regexp-in-string (format " \\|\n\\|%c" 13) " " result)) (result (replace-regexp-in-string "<.+?>\\|%c" "" result)) (result (replace-regexp-in-string "\\s-+" " " result)) (result (replace-regexp-in-string ".+;Personal Details" "" result)) ;; now the parsing (fname (and (string-match "first ?name: ;\\(\\S-+\\)" result) (match-string 1 result))) (lname (and (string-match "last ?name: ;\\(\\S-+\\)" result) (match-string 1 result))) (nick (and (string-match "nickname: ;\\(\\S-+\\)" result) (match-string 1 result)))) `((fname . ,fname) (lname . ,lname) (nick . ,nick)))))))
When you enter a Jabber conference room, past messages from people no longer in the room are formatted as
<root> System message: Message from unknown participant ajk: ...
The following elisp reformats them more legibly:
(defun my-reformat-jabber-backlog ()
"Fix \"unkown participant\" backlog messages from bitlbee."
(save-excursion
(goto-char (point-min))
(if (looking-at
"^<root> System message: Message from unknown participant \\([^:]+\\):")
(replace-match "<\\1>"))))
(add-hook 'erc-insert-modify-hook 'my-reformat-jabber-backlog)BitlBee has http proxy support in the code. To use it, see the system-wide BitlBee configuration file bitlbee.conf, usually installed in (/usr/local)/etc/bitlbee/. You can’t use the runtime set command for this because proxy servers usually aren’t really a per-user thing.
By default, new conversations with buddie start in the control channel. However, most people probably prefer to have a separate window for every conversation. So you might want BitlBee to send messages directly to you instead of via the control channel. This is possible. Type the command “help set private” for more information.
I recently tried to use Bitlbee at my work in order to access to my Jabber account. Because of the presence of a firewall and an http proxy, the best way I found to achieve this is to connect to a jabber server which allows connections through port 443 (such as amessage.info, jabberes.org, etc.).
The problem I found is that I couldn’t see where I could configure the server port for my jabber account under Bitlbee. So I decided to recompile Bitlbee and to modify the protocols/jabber/jabber.c file by replacing 5222 and 5223 by 443 at the following lines :
#define DEFAULT_PORT 5222
#define DEFAULT_PORT_SSL 5223I also wanted to have crypted connections, and, once again, I couldn’t found the way to enable ssl for each account. So I also modified the jabber.c by converting ssl=0 into ssl=1 at the following lines :
(...)
static void gjab_start(gjconn gjc)
{
struct aim_user *user;
int port = -1, ssl = 1;
(...)I know it really is an ugly hack, because it supposes that you make all your connections via ssl through port 443. It works for me, but there may be a much better way to achieve this…
Here’s how to expand all t.co links by running curl --silent --head on each one of them.
;; (t.co-resolve "http://t.co/AvQV2dTP")
(defun t.co-resolve (url)
"Resolve t.co URL by launching `curl --head' and parsing the result."
(assert (string-match (concat "^" rcirc-url-regexp "$") url))
(let* ((curl (shell-command-to-string (format "curl --silent --head %s" url)))
(location (when (and (string-match "\\`HTTP/1\.1 301" curl)
(string-match (concat "^Location: "
rcirc-url-regexp) curl))
(match-string 1 curl))))
(or location url)))
;; (with-temp-buffer (insert "hallo http://t.co/AvQV2dTP hallo") (goto-char (point-min)) (t.co-url-replace nil nil) (buffer-string))
(defun t.co-url-replace (sender response)
"Call `t.co-resolve' on every matching URL."
(let ((re "http://t\\.co/[a-z0-9]+"))
(while (re-search-forward re nil t)
(replace-match (save-match-data (t.co-resolve (match-string 0)))))))
(eval-after-load 'rcirc
'(add-to-list 'rcirc-markup-text-functions
't.co-url-replace))