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 Or, in Debian,

 apt-get -u install bitlbee

Or, in OS X,

 port install bitlbee

Public server

You can also use the public BitlBee server at, or any other public server from the public server list:

Running Bitlbee as an Inferior Process

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"?>
            "-//Apple//DTD PLIST 1.0//EN"
  <plist version="1.0">

Then logout and log back in for it to become active. – JohnWiegley

Auto-authenticating to BitlBee


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" 

If you prefer to authenticate without storing your password in a visible place in your Emacs startup files though, it’s probably better to use the following hook function. This one looks up the current server name in your .authinfo or .netrc file:

  (defun bitlbee-netrc-identify ()
    "Auto-identify for Bitlbee channels using authinfo or netrc.
    The entries that we look for in netrc or authinfo files have
    their 'port' set to 'bitlbee', their 'login' or 'user' set to
    the current nickname and 'server' set to the current IRC
    server's name.  A sample value that works for authenticating
    as user 'keramida' on server 'localhost' is:
    machine localhost port bitlbee login keramida password supersecret"
    (when (string= (buffer-name) "&bitlbee")
      (let* ((secret (plist-get (nth 0 (auth-source-search :max 1
							   :host erc-server
							   :user (erc-current-nick)
							   :port "bitlbee"))
	     (password (if (functionp secret)
			   (funcall secret)
	(erc-message "PRIVMSG" (concat (erc-default-target) " " "identify" " " password) nil))))
  ;; Enable the netrc authentication function for &biblbee channels.
  (add-hook 'erc-join-hook 'bitlbee-netrc-identify)


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 ()
   (circe "localhost" "6667" "bitlbee")
     (set-buffer "localhost:6667")
        (format "PRIVMSG &bitlbee :identify %s"

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.

More ERC hacks


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."
      (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-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)
	       (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.

The ICQ white pages

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, returns result table."
	  (with-timeout (3 (ignore))
	    (and uin
	             (url-retrieve-synchronously (format ",,,00.html?to=%%25U&Uin=%s" uin))
	           (let* (;; some cleansing
	                  (result (buffer-string))
	                  (result (replace-regexp-in-string (format "&nbsp\\|\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)))))))

Jabber conference room backlog

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 in ERC:

    (defun my-reformat-jabber-backlog ()
      "Fix \"unkown participant\" backlog messages from bitlbee."
        (goto-char (point-min))
        (if (looking-at
    	 "^<root> Message from unknown participant \\([^:]+\\):")
    	(replace-match "<\\1>"))))
    (add-hook 'erc-insert-modify-hook 'my-reformat-jabber-backlog)

or in rcirc:

  (defun my-rcirc-reformat-bitlbee-backlog (sender response)
    (when (string= response "PRIVMSG")
      (goto-char (point-min))
      (while (re-search-forward
              "<root> Message from unknown participant \\([^:]+\\):"
              nil t)
        (replace-match "<\\1>"))))
  (add-to-list 'rcirc-markup-text-functions 'my-rcirc-reformat-bitlbee-backlog)

Desktop notifications of messages on systems with D-Bus

The following code creates a desktop notification via D-Bus whenever a new message is received; the notification doesn’t time out, and is instead cleared by sending a reply to that message, or by being closed manually.

Tested on Emacs 24.3 manually compiled for Debian Wheezy.

  (require 'notifications)
  ;; Association list, pairing buffer names with notification IDs.
  (setq erc-notification-id-alist '())
  ;; Function for use by erc-new-message-notify.
  (defun erc-notification-closed (id reason)
    "Callback function, for removing notification entry if user closed notification manually."
    (dolist (entry erc-notification-id-alist)
      (if (equal id (cdr entry))
          (setq erc-notification-id-alist
                 (assoc (car entry) erc-notification-id-alist)
  ;; Create notification when ERC message received.
  (defun erc-new-message-notify (message)
    "Notify user of new ERC message."
    (let ((this-buffer (buffer-name (current-buffer))))
      (if (not (string-match "^[#&]" this-buffer))
          (if (not (assoc this-buffer erc-notification-id-alist))
              (setq erc-notification-id-alist
                     `(,this-buffer ,@(notifications-notify :timeout 0 :on-close 'erc-notification-closed :title "New message in chat" :body (buffer-name (current-buffer))))
  (add-hook 'erc-insert-pre-hook 'erc-new-message-notify)
  ;; On ERC buffer modification by user, clear notification message.
  (defun erc-close-notification ()
    "Close ERC notification bubbles related to the current chat."
    (let ((this-buffer (buffer-name (current-buffer))))
      (if (assoc this-buffer erc-notification-id-alist)
          (notifications-close-notification (cdr (assoc this-buffer erc-notification-id-alist)))
          (setq erc-notification-id-alist
                 (assoc this-buffer erc-notification-id-alist)
  (add-hook 'erc-send-post-hook 'erc-close-notification)

HTTP proxy support

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.

Automatically opening new query buffers for new IMs?

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.

Using Bitlbee behind a firewall

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,, 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 5223

I 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…

At least in BitlBee 1.0.1, another way to get SSL-encrypted Jabber is to add the account like account add password I used this to access Google Talk which IIRC requires SSL. ~Ilkka
BitlBee 1.2: use account set

Twitter's URL shortener for rcirc

Here’s how to expand all links by running curl --silent --head on each one of them.

;; ( "")

(defun (url)
  "Resolve 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 hallo") (goto-char (point-min)) ( nil nil) (buffer-string))

(defun (sender response)
  "Call `' on every matching URL."
  (let ((re "http://t\\.co/[a-z0-9]+"))
    (while (re-search-forward re nil t)
      (replace-match (save-match-data ( (match-string 0)))))))

(eval-after-load 'rcirc
  '(add-to-list 'rcirc-markup-text-functions