The name really does lend itself to abuse. Regardless, I leveraged some previous lua code to create a nice little email widget that checks my email account for new mail and, if so, creates a menu of the mailboxes which have new mail that I can select and launches mutt
with that folder open.
Code and explanation after the jump.
This is the module that my awesome rc.lua
file includes:
-- note that 'awful' is already in the global namespace
local naughty = require("naughty")
local imaplib = require("imap4")
local string = require("string")
local table = require("table")
-- create table so we can use this file as a Module
local M = {}
-- mailhost and user info
local mailhost = "mailserver"
local mailuser = "somebody"
local mailpw = "secret"
-- we don't care if new mail is in these folders
local mb_filter = { Trash = 1,
spam = 1,
spam2mail = 1,
Sent = 1,
Junk = 1
}
-------------------------------------------------------------------------------
--
local function chk_result(r)
if r:getTaggedResult() ~= 'OK' then
imap:shutdown()
return nil
end
return r
end
--------------------------------------------------------------------------------
--
local function connect(host, username, password)
local imap = imaplib.IMAP4:new(host)
-- make sure server supports IMAP4rev1
local r = chk_result(imap:CAPABILITY())
if not r then return nil end
-- now check for IMAP4Rev1 compatibility
local capability = r:getUntaggedContent('CAPABILITY')[1]
if not capability:find('IMAP4rev1') then
imap:shutdown()
return nil
end
-- bail if we can't use STARTTLS
if not (capability:find('STARTTLS') and
chk_result(imap:STARTTLS()) and
chk_result(imap:LOGIN(username, password))) then
return nil
end
return imap
end
--------------------------------------------------------------------------------
--
local function getMailboxes(imap)
local r = chk_result(imap:LIST('""', '*'))
if not r then
return nil
else
return r:getUntaggedContent('LIST')
end
end
-------------------------------------------------------------------------------
--
local function mailChecker(imap)
local mailboxes = getMailboxes(imap)
if not mailboxes then return nil end
local mail = {} -- table to hold mailbox names with new mail count
for i,v in ipairs(mailboxes) do
local mb = v:match([[.* %"(.-)%"$]])
if mb_filter[mb] == nil then
local rs = chk_result(imap:STATUS(mb, 'UNSEEN'))
local mbstat = rs:getUntaggedContent('STATUS')[1]
local nm_cnt = mbstat:match([[%(UNSEEN (%d+)%)$]])
if nm_cnt ~= '0' then
mail[mb] = nm_cnt
end
end
end
return mail
end
-------------------------------------------------------------------------------
--
local email_cmd = "x-terminal-emulator --title mutt -e sh -c 'sleep 0.5s; mutt -f ='"
local function showMail()
-- quick function to check if a 'dict' style table is empty, lua's '#'
-- operator doesn't work on dict type tables
local function empty(t)
local len = 0
for k,v in pairs(t) do
len = len + 1
end
return len
end
-- establish server connection
local imap = connect(mailhost, mailuser, mailpw)
if not imap then return end
-- perform the check
local newmail = mailChecker(imap)
if not newmail then return end -- some kind of problem
-- this logs us out and shuts down the network connection
imap:shutdown()
imap = nil
-- everything OK to now- if the table is length 0 then no new mail
if not empty(newmail) then
local mailmenu = {}
for mb, cnt in pairs(newmail) do
table.insert(mailmenu, {string.format("%s:\t%u", mb, cnt),
function ()
awful.util.spawn(email_cmd..mb)
end
}
)
end
local mm = awful.menu.new({ items = mailmenu })
mm:show({ keygrabber = true })
else
naughty.notify({ text = "No New Mail",
title = "MailChecker",
timeout = 2
}
)
end
end
-- create the widget with the mouse button binding
local mail_w = widget({ type = "imagebox" })
mail_w.image = image("/path/to/icon.png")
mail_w:buttons(awful.util.table.join(awful.button({}, 1, showMail)))
-- now populate namespace table
M.widget = mail_w
return M
The key to this module is an IMAP4 client library I wrote awhile back. The module can be found here. It can be installed to a system directory like /usr/local/share/lua/5.1/
or right into the same directory of the source code that is using it. Basically, install it into the lua path for your system. When in doubt, the same directory as the rest of your code should work.
Staying out of the IMAP specific stuff, the idea is after establishing a connection with the server, the code queries the server for a list of mailboxes. It then requests a status of each of those mailboxes, while ignoring the mb_filter
ones, and builds a table containing the mailbox name and newmail count for that mailbox.
Once the table is built, we check to see if it’s empty. If not, then build a popup menu using the mailbox names and mail count to create each menu item. The email_cmd
string uses mutt's
option to launch into a particular folder, so the mailbox name is just appended to the command for each entry. Then it’s just a matter of showing it. Note I show the menu with keygrabber = true
so I can just hit escape if I’m not interested in dealing with the new mail.
If no new email is present, then a popup notification is used to inform the user that there isn’t any mail. Sorry, no one loves you… 🙁
The widget creation is simple, consisting of 3 lines of code. I chose to make it an imagebox
widget.
To use the code, simply put it into a file like mail.lua
and put the file in the ~/.config/awesome
directory. Then in whatever module the wibox is managed use a line like this:
local mail = require("mail")
Finally, simply locate mail.widget
somewhere within the wibox layout. I put mine just before the layout box widget, for whatever that’s worth.
Thinking about it, it would be simple to create a keybinding that executes the same code. No pointing required. Hmmmm….
2 replies on “An Awesome Mail Widget”
I have just gotten mutt running almost where I want it to be. As I was stumbling around, I have just moved from that painfully slow Unity to using Awesome, and loving it (some more crap I am going to fine tune!).
So what is is the trick to get the mutt widget to work if I am using it with my local imapoffline maildir? And then after that I need to figure out how to get get mail notification running under Awesome…
I’m not familiar with offlineimap, so I’m afraid I’m not much help to you. This widget is something I concocted and simply queries all the mailboxes on an IMAP server for new mail, then builds a popup of the IMAP folders with that mail. I don’t use the offline syncing features of IMAP.