Faster mail with Emacs

I spent a few minutes getting offlineimap to synchronize my Gmail messages with dovecot, an IMAP server on my laptop. I also set up Gnus to work with the messages. Now I’m having fun speeding through my inbox. I don’t know why, but my text-based terminal seems a lot zippier and a lot easier to work with than Gmail… =)

I’m glad I’m back to doing my mail in Emacs!

Gnus multi-pane tricks, or I heart Planet Emacsen

The tips Joseph Miklojcik shared for reading e-mail in Gnus included this _really_ nifty thing that I hadn’t come across when I set up my Gnus before: multi-pane reading.

You know how modern news readers have a folder pane, a summary pane
and a preview pane?

Well, you can have that too.

(gnus-add-configuration
 '(article
   (horizontal 1.0
        (vertical 60 (group 1.0))
        (vertical 1.0
    (summary 0.20 point)
    (article 1.0)))))

(gnus-add-configuration
 '(summary
   (horizontal 1.0
        (vertical 60 (group 1.0))
        (vertical 1.0 (summary 1.0 point)))))

This is good stuff. =)

On Technorati: ,

Random Emacs symbol: sacha/gnus-add-subject-to-bbdb-record – Function: Add datestamped subject note for each person this message has been sent to.

Contact report

I started tracking e-mail sent on 2006.09.01 with a
nifty piece of Emacs Lisp code I wrote just for the
purpose. Now I have two months of interesting data which include not
only e-mail but also the occasional in-person contact or phone call
that I remember to note. It’s not complete – e-mail’s the only thing
that gets automatically tracked – but it does give me interesting
information. Here’s the contact report for your amusement:

Contact report

It’s sorted by overall frequency and then by regular frequency.
Warning! Parentheses follow.

(defun sacha/count-matches (regexp string)
  (let ((count 0)
        (start 0))
    (while (string-match regexp string start)
      (setq start (match-end 0)
            count (1+ count)))
    count))

(defun sacha/bbdb-contact-report-as-alist (&rest regexps)
  "Creates a list of (name count-regexp1 count-regexp2 count-regexp3)..."
  (setq regexps (reverse regexps))
  (delq nil
        (mapcar
         (lambda (rec)
           (when (bbdb-record-name (car rec))
             (let ((reg regexps)
                   (notes (bbdb-record-notes (car rec)))
                   list)
               (while reg
                 (setq list (cons (sacha/count-matches (car reg) notes)
                                  list))
                 (setq reg (cdr reg)))
               (cons (sacha/planner-bbdb-annotation-from-bbdb rec)
                     list))))
         bbdb-records)))

(defun sacha/bbdb-alist-sort-by-total (alist)
  "Sort ALIST by total contact."
  (sort alist 'sacha/bbdb-contact-sort-predicate))

(defun sacha/bbdb-contact-sort-predicate (a b)
  (and a b
       (let ((count-a (apply '+ (cdr a)))
             (count-b (apply '+ (cdr b))))
         (or
          (> count-a count-b)
          (and (= count-a count-b)
               ;; If equal, look at the subtotal of the rest
               (sacha/bbdb-contact-sort-predicate (cdr a) (cdr b)))))))

(defun sacha/bbdb-kill-contact-barchart (alist)
  "Kill a barchart with the contact report for ALIST."
  (kill-new
   (mapconcat
    (lambda (entry)
      (concat
       (car entry)
       " | "
       (mapconcat (lambda (count)
                    (if (= count 0)
                        " "
                      (make-string count ?-)))
                  (cdr entry)
                  " | ")))
    alist
    "\n")))

;; Usage: (sacha/bbdb-kill-contact-barchart
;;         (sacha/bbdb-alist-sort-by-total
;;          (sacha/bbdb-contact-report-as-alist "2006.09" "2006.10")))
;; Then yank (paste) this into another buffer

On Technorati: , , , ,

Random Emacs symbol: standard-display-cyrillic-translit – Command: Display a cyrillic buffer using a transliteration.

Keeping track of the age of messages

I can get pretty bad at responding to e-mail. This is an experiment to
see whether the negative reinforcement of seeing just how old a
message is will help me be more responsive. Either that, or I can
strive for a Mean Time Between Responses of whatever… ;)

Hmm, maybe I should combine this with my blog and start distinguishing
between E-mail to and Reply to…

(defadvice gnus-post-news (around sacha/gnus-track-message-age activate)
  "Insert a header showing how old a message is, to shame me into replying faster."
  ;; Before you post the news, figure out how old it is
  (let (days)
    (when article-buffer
      (setq days
            (- (time-to-days (current-time))
               (time-to-days
                (gnus-date-get-time
                 (mail-header-date
                  (gnus-summary-article-header
                   (gnus-summary-article-number))))))))
    ad-do-it
    (when days
      (goto-char (point-min))
      (when (re-search-forward "--text follows this line--" nil t)
        (forward-line 1)
        (insert "In reply to a message sent by "
                (mail-header-from message-reply-headers)
                " "
                (cond
                 ((= days 0) "today")
                 ((= days 1) "yesterday")
                 (t (format "%d days ago" days)))
                ": \n\n")))))
(setq message-citation-line-function nil)

On Technorati: , ,

Random Emacs symbol: tramp-perl-directory-files-and-attributes – Variable: Perl script implementing `directory-files-attributes’ as Lisp `read’able

Emacs Gnus hack: Prioritize based on the number of recipients

Ever found yourself confronted with an inbox overflowing with general
messages that you can ignore and messages that you and only you can
act on? Here’s something to help you sort the wheat from the chaff.

This indicates how personal messages are so you can immediately see which messages are just for you and which are part of a long Cc. Stephen Perelgut showed me the feature in Lotus Notes and I wanted to steal it sometime, so I did it while waiting for the Instant Rails archive.

To use it, add %ur to your gnus-summary-line-format.

(add-to-list 'nnmail-extra-headers 'To)
(add-to-list 'nnmail-extra-headers 'Cc)
(defvar sacha/gnus-count-recipients-threshold 5
  "*Number of recipients to consider as large.")

(defun sacha/gnus-count-recipients (header)
  "Given a Gnus message header, returns priority mark.
If I am the only recipient, return \"!\".
If I am one of a few recipients, but I'm listed in To:, return \"*\".
If I am one of a few recipients, return \"/\".
If I am one of many recipients, return \".\".
Else, return \" \"."
  (let* ((to (or (cdr (assoc 'To (mail-header-extra header))) ""))
         (cc (or (cdr (assoc 'Cc (mail-header-extra header))) "")))
    (cond
     ((string-match gnus-ignored-from-addresses to)
      (let ((len (length (bbdb-split to ","))))
        (cond
         ((= len 1) "!")
         ((< len sacha/gnus-count-recipients-threshold) "*")
         (t "/"))))
     ((string-match gnus-ignored-from-addresses
                    (concat to ", " cc))
      (if (< (length (bbdb-split (concat to ", " cc) ","))
             sacha/gnus-count-recipients-threshold)
          "/"
        "."))
     (t " "))))

(defalias 'gnus-user-format-function-r 'sacha/gnus-count-recipients)

Random Emacs symbol: dired-listing-switches - Variable: *Switches passed to `ls' for Dired. MUST contain the `l' option.

On Technorati: , , ,

More Emacs goodness: Refresh your memory when you e-mail using notes from BBDB

Inspired by an e-mail-based customer relationship management system briefly described by Daniel Charles of digital ketchup at Shoeless Joe’s last Friday, I decided to hack together a system that would allow me to see the notes from my contact database (aptly named the Big Brother Database, or BBDB) when I write e-mail using the Gnus mail client in Emacs.

The first thing I needed to build, of course, was something that
removed my notes from outgoing messages. People really don’t need to
see the kinds of notes I keep on them. ;) Well, they’re fairly
innocuous notes: how we met and what they’re interested in, usually,
although sometimes I’ll have notes on people’s food preferences or
shoe sizes. I’ve recently started keeping track of the subjects of
e-mail I send them, too.

(defun sacha/gnus-remove-notes ()
  "Remove everything from --- NOTES --- to the signature."
  (goto-char (point-min))
  (when (re-search-forward "^--- NOTES ---" nil t)
    (let ((start (match-beginning 0))
          (end (and (re-search-forward "^--- END NOTES ---") (match-end 0))))
      (delete-region start end))))
(add-hook 'message-send-hook 'sacha/gnus-remove-notes)

Then it was easy to write another function that composed individual
messages to all the people currently displayed in the BBDB buffer,
adding notes to each message.

(defun sacha/gnus-send-message-to-all (subject)
  "Compose message to everyone, with notes."
  (interactive "MSubject: ")
  (let ((records bbdb-records))
    (while records
      (when (bbdb-record-net (caar records))
        (bbdb-send-mail (caar records) subject)
        (when (bbdb-record-notes (caar records))
          (save-excursion
            (insert "\n--- NOTES ---\n"
                    (bbdb-record-notes (caar records))
                    "\n--- END NOTES ---\n"))))
      (setq records (cdr records)))))

I use BBDB to display only the people I want to e-mail, then I call
M-x sacha/gnus-send-message-to-all and specify a message subject. This
creates a gazillion message buffers which I can then edit. If I feel
particularly paranoid, I can remove the notes section myself with C-c
C-z (message-kill-to-signature), but sacha/gnus-remove-notes does it
as long as it’s in message-send-hook.

This code works particularly well with these other customizations:

It supersedes More Emacs fun: Composing mail to everyone with notes.

On Technorati: , , , , ,

../emacs/dotgnus.el