home of the madduck/ docs/
Irssi productivity hacks with chanact

I am a heavy IRC user, and the following is a priceless tip how to allow you to join dozens of channels, keep the IRC window open at all times, and still prevent getting distracted by happenings on those channels that you don't actually care about.

First, let me introduce the most basic feature of chanact, a very useful plugin for irssi, my IRC client of choice. You can find chanact.pl also in Debian's irssi-scripts package, so to enable it, install that package and run:

mkdir -p ~/.irssi/scripts/autorun
ln -s /usr/share/irssi/scripts/chanact.pl ~/.irssi/scripts/autorun

and then run /script load chanact from any running irssi instances.

Channel aliases

I have been using the irssi chanact script for many years to assign one-letter aliases to my channels — I could never understand how people kept track of the fact that window 17 was e.g. #debian-devel. In my case, that's just window d, I can switch to it with Meta-d and if there's activity, there'll be a d in the status bar. Obviously, this will limit you to the number of available characters, but between Unicode and windows 1–20 available for temporary use (see my use of chanact_renumber_start further down), this should be enough for everyone.

This is trivially implemented with autosendcmd, like so in ~/.irssi/config:

channels = (
  […]
  {
    name = "#debian-devel";
    chatnet = "oftc";
    autojoin = "Yes";
    autosendcmd = "/window_alias d";
  },
  […]
);

I also suggest to explicitly add keybindings to the configuration file to get defined behaviour if you /reload.

keyboard = (
  […]
  {
    key = "meta-d";
    id = "change_window";
    data = "d:oftc/#debian-devel";
  },
  […]
);

chanact itself exports numerous settings, and here are mine:

settings = {
  […]
  "perl/core/scripts" = {
    […]
    chanact_header = "";
    chanact_separator = "";
    chanact_abbreviate_names = "0";
    chanact_autorenumber = "yes";
    chanact_renumber_start = "21";
    chanact_display_alias = "$H$N$S";
    chanact_chop_status = "yes";
    chanact_show_names = "no";
    chanact_show_mode = "0";
    chanact_show_alias = "yes";
    […]
  };
  […]
};

I leave it up to you to figure out what these do since you are likely to set them to your own taste anyway. Type /set chanact into irssi to get a list of all settings, and check the script itself for documentation about them.

Lastly, because that autosendcmd hook sometimes gets called again and again (e.g. if you /part and /join the channel, or there's a netsplit), I learnt to refrain from using /save, which I disabled as follows:

aliases = {
  […]
  save = "echo edit the config file instead (or use //save)";
};

I also put my irssi configuration into a Git repository to stay in control of what goes into the configuration, and to be able to inspect the changes easily should I chose to circumvent the save protection by running //save. But that may just be me.

Per-window activity filtering

With the above settings, irssi displays a series of one-letter aliases in the status bar to indicate, which channels have activity; those that contain activity with highlights you defined will be bold (depending on your theme, the sb_act_* abstracts determine that).

The problem is that a non-empty list distracts and might lead you to switch to a channel to figure out what the activity is; after all, it's just a Meta-* key combination away.

chanact allows you to define an activity filter, and together with activity_hide_targets, one can already control to a certain degree which activity level is considered high enough for you to take note. The following levels exist:

0: show all channels
1: hide channels without activity
2: hide channels with only join/part/etc messages
3: hide channels with text messages
4: hide all channels (now why would you want to do that)

However, these levels are all-or-nothing, and that wasn't good enough for me, so I reported a feature wishlist and found out that I wasn't the only one with similar needs, or even an implementation.

But it seemed like we couldn't find one solution to settle on, and I really wanted the feature, so I went ahead and touch the script (PERL!!!) myself. The result turned out quite nice, but it was O(n²), so the author rewrote it with a bit of nicer Perl.

What counts is the final result, and I love it. Note that you will need chanact 0.5.12, or Debian's irssi-scripts version 20090810, which includes a patched 0.5.11.

I identified three types of channels in terms of the activity level I cared about:

In all three cases, I still wanted to see highlights, but those seem to be handled somewhat differently anyway, so I didn't have to worry about them.

The following three settings achieved what I want:

settings = {
  […]
  "perl/core/scripts" = {
    […]
    chanact_filter = "3";
    chanact_filter_windowlist_level = "2";
    chanact_filter_windowlist = "@QUERIES #vcs-pkg #debian.ch,1";
  };
};

The effects of these three are as follows:

So in the above, I would get to see activity for #vcs-pkg and all queries, and I would get to see joins and parts for #debian.ch on top of the normal activity.

The only shortcoming is that the window list takes window names, and those need not be unique: if you are joined to #debian-devel on multiple networks, I don't know what will happen. I also did not find a trivial way to fix this.

Do not disturb

Two other aliases conveniently allow me to go into and out of do-not-disturb mode, such that not even highlights reach me, unless they are in one of the channels (and depending on the setting, @QUERIES) I watch:

aliases = {
  […]
  dnd = "set chanact_filter 4";
  avail = "set chanact_filter 3";
};

This is convenient, because I can turn on /dnd most of the time, but if I return with /avail, the highlights all show up and I can go through them.

Quick modifications to the filter list

The last change I made is a gross hack abusing the hide script, so that I can quickly show and hide activity for certain channels.

settings = {
  […]
  "perl/core/scripts" = {
    […]
    showc = "eval set activity_hide_targets $chanact_filter_windowlist; hide target add $0; eval eval set chanact_filter_windowlist \\$activity_hide_targets; set -default activity_hide_targets";
    hidec = "eval set activity_hide_targets $chanact_filter_windowlist; hide target remove $0; eval eval set chanact_filter_windowlist \\$activity_hide_targets; set -default activity_hide_targets";
    viewc = "set chanact_filter_windowlist";
  };
};

The problem is that /hide adds to hide and removes to show, which is exactly the inverse of how the window list works. Thus, the above hack needs to reverse that, and abuses the (no longer necessary) activity_hide_targets setting for that.

I told you it was gross!