Freenode cloaking and ERC
IRC clients often provide a way for their users to automatically
identify themselves to NickServ, and ERC is no exception.
To enable erc-services-mode
in your ~/.ercrc.el
, the following should do the
trick:
(require 'erc-services nil t)
(erc-services-mode 1)
(setq erc-prompt-for-nickserv-password nil)
Aside:
You’ll also need to configure erc-nickserv-passwords
with your
nick and password information.
IRC clients also usually provide a way to automatically join
channels when you connect; ERC enables its erc-autojoin-mode
by default.
You tell ERC which channels to auto-join by configuring erc-autojoin-channels-alist
.
Suppose you want to auto-join #emacs on freenode. You’d set erc-autojoin-channels-alist
like so:
(setq erc-autojoin-channels-alist '((".*freenode.net" "#emacs")))
Auto-identification and auto-joining are both nice features, but
it turns out they can interact in an unfortunate way, when you
use IRC hostmask
cloaking. Hostmask cloaking is when the IRC
server disguises the host that you’re connected to IRC
from. Most cloaks get installed when you identify with NickServ
. For example, when I identify with
freenode’s NickServ
, I receive
this notice:
-kubrick.freenode.net- NickServ set your hostname to "unaffiliated/hober"
OK, so what's the big deal? The problem is that, when using
erc-services-mode
and erc-autojoin-mode
at the same
time, ERC auto-joins channels before your hostmask
cloak is installed. People in those channels see your uncloaked
host information when you join, thus completely obviating the
point of cloaking in the first place.
The fix is to ensure that we only auto-join channels
after NickServ
has installed
our hostmask cloak. Here’s how to do that.
First, disable erc-autojoin-mode
in ~/.ercrc.el
:
(erc-autojoin-mode 0)
We know NickServ
’s installed our
cloak when we receive the notice I quoted above. ERC provides a
hook for processing notices, erc-server-NOTICE-functions
,
which we can use.
(add-hook 'erc-server-NOTICE-functions 'ted-post-cloak-autojoin)
Of course, now we have to write ted-post-cloak-autojoin
.
Now we need to understand how ted-post-cloak-autojoin
will be called from
erc-server-NOTICE-functions
.
Like most ERC callbacks, members of erc-server-NOTICE-functions
are
passed two parameters: proc
, an object representing the
network connection, and parsed
, which contains the
message received from the server.
(defun ted-post-cloak-autojoin (proc parsed)
"Autojoin iff NickServ tells us to."
...)
We want to auto-join when we receive one of these cloak notices.
We can inspect parsed
to
see if it’s what we’re looking for:
(defun ted-post-cloak-autojoin (proc parsed)
"Autojoin iff NickServ tells us to."
(when (and (string-equal "irc.freenode.net"
(erc-response.sender parsed))
(string-match ".*NickServ set your hostname to.*"
(erc-response.contents parsed)))
...))
To actually perform the auto-join, we can call the function
erc-autojoin-channels
, which takes
two arguments, server
and
nick
. But where do we get
values for server
and
nick
from?
In Emacs Lisp, process objects (like our proc
) have associated buffers
(called process buffers). ERC stores session-specific
information in buffer-local variables within proc
’s buffer. We can use
with-current-buffer
to temporarily
switch to proc
’s
buffer.
A value for server
can be
found in the variable erc-session-server
, and the
function erc-current-nick
returns
your current nick on that server.
(with-current-buffer (process-buffer proc)
...
(erc-autojoin-channels erc-session-server (erc-current-nick))
...)
There’s one last issue to note: erc-server-NOTICE-functions
’s
docstring
says if the function returns non-nil, stop processing the
hook.
We don’t want to prevent other hook functions
from running, so we should always return nil.
Putting it all together, here’s what I’ve come up with:
(defun ted-post-cloak-autojoin (proc parsed)
"Autojoin iff NickServ tells us to."
(with-current-buffer (process-buffer proc)
(when (and (string-equal "irc.freenode.net"
(erc-response.sender parsed))
(string-match ".*NickServ set your hostname to.*"
(erc-response.contents parsed)))
(erc-autojoin-channels erc-session-server (erc-current-nick))
nil)))
There you go. Now auto-join isn’t triggered until we’re hiding behind our hostmask cloak, and hopefully you've learned something useful about ERC customization and Emacs Lisp too!