A Little GUI for Your CLI

I've tried pretty much every IRC client available for both Linux and OS X. (I use both platforms during my day job.) No matter how many times I try to find a GUI application that meets my needs, I always turn back to Irssi. It works so well, and I can access it from anywhere (Figure 1). Thanks to my Raspberry Pi colocation in Austria, I always can stay logged in and never miss a message. Unfortunately, the one thing I wish Irssi had is a pop-up notification when someone sends me a message or mentions me in a channel. So, I decided to give Irssi a little GUI. It was fun, and as it turns out, it works very well.

Figure 1. I actually had to scroll up a bit to find something harmless enough to post in the magazine!

If you're running Irssi locally on the Linux machine you're sitting in front of, this process is much simpler. Since my Irssi-connected machine is on another continent, the process is a little more complex, but also a lot more fun. In order to get local GUI notifications for remote mentions of my user name, I need to accomplish a few things:

  1. I have to get Irssi to generate some sort of event and/or log when I'm mentioned in an IRC channel or query.

  2. I have to parse that information and transfer it to my local machine over the Internet in real time.

  3. I need to display a GUI pop-up on my local machine when the remote events occur, again in real time.

  4. I need to be able to disconnect and reconnect to a screen session and have the GUI notifications follow me, regardless of what computer I'm actually on. (In my case, this means Linux or OS X.)

Thankfully, Linux supplies all the tools I need, and with a little bit of scripting, I can get the system working.

Step 1: Irssi Events

I'm obviously not the first person to want a GUI notification system for Irssi, and thankfully, Thorsten Leemhuis has shared his Irssi plugin for everyone to use. You can download the plugin at http://www.leemhuis.info/files/fnotify/fnotify, and since it's released under the GPL, you can share it as you see fit.

In order to use the script, save it as fnotify.pl in your ~/.irssi/scripts/ folder where Irssi is running. For me, that's on my remote server in Austria. Once saved, you can load the script by typing:


/script load fnotify.pl

from inside the Irssi application. If you want to make fnotify.pl be loaded automatically every time Irssi starts (my recommendation), create a symbolic link into the autorun folder inside the scripts folder. To do that, type:


mkdir ~/.irssi/scripts/autorun (if it doesn't exist already)
ln -s ~/.irssi/scripts/fnotify.pl 
 ↪~/.irssi/scripts/autorun/fnotify.pl

Then fnotify should be loaded every time Irssi starts. To make sure it's working correctly, have someone mention you in an IRC channel, and check to see that the message is written to the file ~/.irssi/fnotify.

Once you're certain the plugin is working, and mentions of your name are written to the fnotify file, it's time to get that information to your local computer. But first, you need to set up the local computer for GUI pop-up messages, so you have somewhere to send the information when you transfer it.

A Local GUI Notification

This part of the equation is fairly simple. Most Linux distributions come with a notification system of some sort. I prefer a GNOME environment, so I choose to use libnotify, or more specifically the notify-send command that creates a GUI pop-up when invoked from the command line or from a script. KDE users can use the kdialog program for a similar effect, and OS X users will want to check out Terminal Notifier, hosted at https://github.com/alloy/terminal-notifier.

From your Linux command line, test notify-send by typing:


notify-send "Test Title" "Sample message..."

That should bring up a libnotify box with your title and message (Figure 2). If you're using KDE (or OS X), the command will be similar, but you'll need to check for the exact syntax. It's also possible to tweak the program to get custom icons, change the duration of the pop-up and even change the system urgency for the notification. I actually use a Pidgin icon for the notification box, only because I've used Pidgin for so long, the icon makes me think chat.

Figure 2. The pop-up system is really pretty slick.

Remote fnotify, Local libnotify

This is the part of the equation that stumped me for a long time. I'd done remote X tunneling over SSH, but since Irssi didn't have a native GUI pop-up, there was no X stuff to tunnel. I also knew that if I SSH'd to my remote server, any commands I ran would execute on the remote server. Thankfully, with enough googling and head-scratching, I learned something: it's possible to run a remote program other than a shell over SSH! I had never considered that as a possibility, but it certainly makes sense.

Knowing that I could run a program over SSH on a remote server and have its output appear on my local machine, it turned into a simple scripting matter to get the fnotify text piped in to libnotify. I should point out that it's much easier to do remote SSH stuff if you have a keypair set up to avoid the need to type in a password. It's possible to run the commands interactively, but you lose a lot of the automation aspect. I have a simple tutorial video for setting up SSH keypairs here: https://www.youtube.com/watch?v=R65HTJeObkI.

Here is my script; I'll explain it afterward:


#!/bin/bash
# Location: /usr/local/bin/irc_notify
(ssh spowers@my.remote.server.com \
-o PermitLocalCommand=no -o ServerAliveInterval=30 \
"> .irssi/fnotify; tail -f .irssi/fnotify" |
while read title message; do
notify-send -u critical -i pidgin "${title}" "${message}";
done)

I must confess again that the code above has been scrounged from other people's work and tweaked by me a bit. I certainly don't claim to be the owner of the concept. The script assumes keypairs are installed, but it probably will work interactively if there are no keypairs set up.

Here's what the script does:

  1. The script launches SSH with a couple options. I added the ServerAliveInterval=30 option because in my case, my SSH connection would time out after a while. That took away the whole point of the script, so that option is to keep the connection alive.

  2. Instead of running a shell, which is the default, I specified what program SSH is supposed to run on the remote machine. I actually have it do two things: first, clear the fnotify file in .irssi, and second, run tail -f on the same file. I clear the file so it doesn't get too long, but if you want to keep a record of all of your messages, you can leave that first part off—it won't affect how the script works.

  3. tail -f is a command that stays running and prints any new entries to the specified file to the command line. It's normally used for watching log files, but in this case, it's perfect. Every time someone mentions my user name, tail -f prints it to the screen.

  4. Notice at the end of that line is the pipe symbol (|)—that sends the output to the program following it, instead of printing it to the screen. In this case, it pipes the output of tail -f into a while loop that reads the text into two different variables. The command read title message assigns the first word in the output (usually the name of the person messaging me) to the variable $title and the rest of the message to the variable $message.

  5. The while loop then runs notify-send with the variables from above, and starts the loop over, waiting for another output from the remote SSH'd tail command. To end the program, you can press ^C. (But I actually automate that too; more on that a bit later.)

The script itself is simple, but it can be confusing to figure out what portions are occurring remotely and what portions are occurring locally. Once I learned that the SSH program can run a program other than the shell on a remote system, the scripting concept started to make sense. Hopefully, it does to you too.

Putting It All Together

You now have a system that will notify you when someone mentions your name in a running session of Irssi. I'll assume you run Irssi in a screen or tmux session, so you can connect and disconnect as need be. Because I connect from various machines, that's how I do it. To make things simple, however, I've written a second script that establishes the notification SSH connection and then SSHes me into the server. Then, when I disconnect from the interactive SSH session, it kills off the notification connection too. I call the script from above irc_notify, and the script below simply irc. When I want to use IRC, I type irc from the command line, and the notification stuff happens automatically. Here's that "irc" script:


#!/bin/bash
# Location: /usr/local/bin/irc
#
# Kill any existing fnotify sessions that might be 
# running to avoid duplicate notifications
kill $(ps aux | grep [f]notify | awk '{print $2}') > 
 ↪/dev/null &2>1
irc_notify &
ssh spowers@my.remote.server.com
# Kill all current fnotify sessions, so notifications 
# don't pop up while I'm not connected to IRC
kill $(ps aux | grep [f]notify | awk '{print $2}')

The script isn't really anything complex, but the kill lines are a little tricky. I run ps aux and then grep for fnotify to make sure I have the correct SSH session. Unfortunately, when you grep for a particular word, you also get the actual grep process. Using the [f]notify regex, only the SSH process is returned. Then, the second field is the process ID (PID), so I use awk to get just that information and kill the process by number.

I have that command twice: once before launching the interactive SSH session to make sure the notification script isn't already running, and then once after I disconnect to clean up after myself. Ideally, the first kill statement never will find anything to kill off, since my script cleans up after itself, but sometimes things go wrong. I like to make sure I don't get duplicate notifications due to multiple fnotify scripts running.

Cross Platform, and Beyond!

I regularly use the scripts on both Linux and OS X. I have no idea if there is something that would do graphical pop-ups on Windows, but I think the scripting part would work using Cygwin. I haven't had a need to test such a thing on Windows, but I'd be interested in hearing about it if you get it working.

I love using Irssi for chatting via IRC, and now that I have a GUI notification system for it, I'll probably start using Bitlbee for other IM protocols, making Irssi truly my one-stop shop for chatting needs. I hope this scripting lesson inspires you to do far more than I've accomplished—with Linux, the only limiting factor is your imagination!

Shawn is Associate Editor here at Linux Journal, and has been around Linux since the beginning. He has a passion for open source, and he loves to teach. He also drinks too much coffee, which often shows in his writing.

Load Disqus comments