This page has some examples from my Gnus configuration. It does not contain any kind of introductory material or basic configuration. See official manual and EmacsWiki Gnus category for much information about that.

Index

Notification about new news

Here is some code I use in .gnus to integrate the desktop notification code with Gnus to notify me about new email in groups.

Code:

;; Notification of new mail using libnotify over D-Bus
(require 'notify)

;; Define mail notification groups here:
(setq notify-gnus-groups
      '(("INBOX"       "nnimap+oyviste:INBOX"
                       "nnimap+oyvinst:INBOX"
                       "nnimap+gmail:INBOX")
        ("svn-commit"  "nnimap+oyviste:INBOX.svn-commit")
        ("vortex-core" "nnimap+oyviste:INBOX.vortex-core")
        ("systemfeil"  "nnimap+oyviste:INBOX.sok-feil"
                       "nnimap+oyviste:INBOX.vortex-feil")
        ("Twitter"     "nnrss:#itkonf - Twitter Search")
        )
      )

;; Notify about unread news in notify-groups. Number of unreads is the sum of unreads in all Gnus groups
;; in a given notify group.
(defun notify-gnus-groups-unread()
  (cond ((boundp 'notify-gnus-groups)
         (when (not (boundp 'notify-gnus-unread-count)) (setq notify-gnus-unread-count nil))
         (let ((groupdefs notify-gnus-groups))
           (while groupdefs
             (let* ((groupdef (car groupdefs))
                    (notifygroup (car groupdef))
                    (groups (cdr groupdef))
                    (unreadbefore (or (cdr (assoc notifygroup notify-gnus-unread-count)) 0))
                    (unreadnow 0))
               (mapc (lambda(val)
                       (setq unreadnow (+ unreadnow (or (gnus-group-unread val) 0)))) groups)
               ; Notify if more unread messages than before in current notify-group
               (when (and (> unreadnow 0) (> unreadnow unreadbefore))
                 (notify-show-message "Gnews" (if (= 1 unreadnow) (format "1 unread message\n[%s]" notifygroup)
                                        (format "%d unread messages\n[%s]" unreadnow notifygroup)) 'mail)
                 )
               ; Update with new unread count
               (if (assoc notifygroup notify-gnus-unread-count)
                     (setcdr (assoc notifygroup notify-gnus-unread-count) unreadnow)
                 (setq notify-gnus-unread-count (cons (cons notifygroup unreadnow) notify-gnus-unread-count))
                 )
               (setq groupdefs (cdr groupdefs))
               ))))))
(add-hook 'gnus-after-getting-new-news-hook 'notify-gnus-groups-unread t)

Automatic BBDB-integration from Google GMail contacts

I use BBDB as addressbook mainly for completion of addresses when composing emails in Gnus/Message. I maintain and update all my contacts in GMail and wanted them available in my local BBDB addressbook automatically. This code, from my .gnus file, sets up periodic update of BBDB from GMail using using google-contacts.el.

;; Automatic BBDB-integration from Google GMail contacts.
(require 'google-contacts)
(setq google-contacts-passwd-use-auth-source t) ; requires machine name "gmail.com" to be setup in an auth source.
(setq google-contacts-system-group-names
      '((friends . "Venner")
        (coworkers . "Kolleger")
        (family . "Familie")))
(setq google-contacts-update-running nil)
(defun google-contacts-update-bbdb()
  (interactive)
  (when (and (nm-is-connected) (not google-contacts-update-running))
    (unwind-protect
        (progn
          (setq google-contacts-update-running t)
          (gnus-message 5 "Updating contacts from Google")
          (google-contacts-merge-with-bbdb (google-contacts-retrieve)))
      (setq google-contacts-update-running nil))))
(gnus-demon-add-handler 'google-contacts-update-bbdb 120 t)

BBDB will be updated every two hours from GMail. Periodic calling is accomplished using gnus-demon. BBDB can be updated explicitly with "M-x google-contacts-update-bbdb", since the function is interactive.

Gnus as mailto: URL handler

Using the Emacs-server together with emacsclient, you can get a nice frame popup with new message composition in a running Emacs instance by using a very simple shell script as your mailto:-handler:

Code:

#!/bin/sh
# Use Gnus as mailto: handler
url="$1"

type emacsclient 1>/dev/null 2>&1 || { echo "Error: emacsclient command is required."; exit 255; }

exec emacsclient -a "" -n -c -e "(mailto-url-gnus \"$url\" t t)"

The mailto-url-gnus function used in this script is available in mailto-url-gnus.el (Only works for Gnus+Message-mode combo.)

Gnus integration with NetworkManager

The following code is an example of using nm.el to set Gnus in plugged or unplugged state depending on network availability.

Code:


;; Plug or unplug Gnus agent automatically depending on network availability.
;; Requires Emacs23 with D-Bus bindings and NetworkManager.
;; Tiny nm.el library (NetworkManager integration).
(require 'nm)
(nm-enable)
(defun imap-nuke-server-processes()
 "Brutally kill running IMAP server background processes. Useful
when Gnus hangs on network outs or changes."
  (interactive)
  (let ((sm (if gnus-select-method
                (cons gnus-select-method gnus-secondary-select-methods)
              gnus-secondary-select-methods)))
    (while sm
      (let ((method (car (car sm)))
            (vserv (nth 1 (car sm))))
        (when (and (eq 'nnimap method)
                   (buffer-local-value 'imap-process (get-buffer (nnimap-get-server-buffer vserv))))
          (gnus-message 6 "Killing IMAP process for server %s" vserv)
          (delete-process (buffer-local-value 'imap-process (get-buffer (nnimap-get-server-buffer vserv))))))
      (setq sm (cdr sm)))))

(defun gnus-nm-agent-unplug()
  "Kill IMAP server processes and unplug Gnus agent."
  (when (and (fboundp 'gnus-alive-p) (gnus-alive-p))
    (gnus-message 6 "Network is disconnected, unplugging Gnus agent.")
    (with-current-buffer gnus-group-buffer
      (imap-nuke-server-processes)        ; optional, help prevent hangs in IMAP processes when network has gone down.
      (gnus-agent-toggle-plugged nil))))

(defun gnus-nm-agent-plug() 
  "Plug Gnus agent."
  (when (and (fboundp 'gnus-alive-p) (gnus-alive-p))
    (gnus-message 6 "Network is connected, plugging Gnus agent.")
    (with-current-buffer gnus-group-buffer
      (gnus-agent-toggle-plugged t))))

(defun gnus-nm-enable()
  (add-hook 'nm-connected-hook 'gnus-nm-agent-plug)
  (add-hook 'nm-disconnected-hook 'gnus-nm-agent-unplug))

(defun gnus-nm-disable()
  (remove-hook 'nm-connected-hook 'gnus-nm-agent-plug)
  (remove-hook 'nm-disconnected-hook 'gnus-nm-agent-unplug))

;; Add hooks for enabling/disabling integration on startup/shutdown:
(add-hook 'gnus-started-hook 'gnus-nm-enable)
(add-hook 'gnus-exit-gnus-hook 'gnus-nm-disable)

Workaround: nnrss-backend quirks

The nnrss-backend for reading RSS-feeds in Gnus (bundled with Emacs 23) did not really work too well. Lots of RSS posts were constantly being duplicated, caused the identifying posts as hash of all the contets. Additionally, it did not support Atom-feeds natively (Thanks to EmacsWiki for providing a solution to that). Here is some code I use in .gnus to fix these things. Also, check out GnusRSS EmacsWiki page.

Emacs Lisp code:

;; RSS setup
;; Support ATOM feeds in mm-insert-url (convert them to RSS, http://www.emacswiki.org/emacs/GnusRss).
(require 'mm-url)
(defadvice mm-url-insert (after DE-convert-atom-to-rss () )
  "Converts atom to RSS by calling xsltproc."
  (when (re-search-forward "xmlns=[\"']http://www.w3.org/.*/Atom[\"']"
               nil t)
    (goto-char (point-min))
    (message "Converting Atom to RSS... ")
    (call-process-region (point-min) (point-max)
             "xsltproc"
             t t nil
             (expand-file-name "~/.el/atom2rss.xsl") "-")
    (goto-char (point-min))
    (message "Converting Atom to RSS... done")))
(ad-activate 'mm-url-insert)
(require 'nnrss)

(setq nnrss-use-local t) ; feeds are updated by download script which runs regularly.

;; Update asynchronous download script file
(setq nnrss-download-script-file (expand-file-name "rss-download.sh" gnus-directory))
(defun nnrss-update-download-script()
  "Update RSS download script"
  (interactive)
  (with-temp-buffer
    (nnrss-generate-download-script)
    (write-file nnrss-download-script-file nil)))

(defun nnrss-run-download-script() 
  "Run RSS download script"
  (interactive)
  (gnus-message 5 (concat "Running download script: " nnrss-download-script-file ".."))
  (shell-command nnrss-download-script-file))

;; Re-define nnrss-generate-download-script to something better.
;; Do conversion of Atom to RSS for all ATOM feeds in the download script.
(defun nnrss-generate-download-script ()
  "Generate a download script in the current buffer.
It is useful when `(setq nnrss-use-local t)'."
  (interactive)
  (insert "#!/bin/sh\n")
  (insert "# Generated on " (format-time-string "%Y-%m-%d %H:%M %Z") "\n")
  (insert "ATOM2RSS='" (expand-file-name "~/.el/atom2rss.xsl") "'\n")
  (insert "RSSDIR='" (expand-file-name nnrss-directory) "'\n\n")
  (dolist (elem nnrss-group-alist)
    (let ((url (nth 1 elem))
          (filename (nnrss-translate-file-chars (concat (car elem) ".xml"))))
      (insert "wget -t 3 -T 60 -q -O \"$RSSDIR/feed.tmp\" '" url "'\n")
      (insert "if [ $? -eq 0 ]; then\n")
      (insert "  touch \"$RSSDIR/feed.tmp\"\n")
      (insert "  if grep -qE \"xmlns=[\\\"']http://www.w3.org/.*/Atom[\\\"']\" \"$RSSDIR/feed.tmp\"; then\n")
      (insert "    xsltproc \"$ATOM2RSS\" \"$RSSDIR/feed.tmp\" > \"$RSSDIR\"/'" filename "'\n")
      (insert "  else\n")
      (insert "    mv \"$RSSDIR/feed.tmp\" \"$RSSDIR\"/'" filename "'\n")
      (insert "  fi\n")
      (insert "fi\n")
      ))
  (insert "\n" "rm -f \"$RSSDIR/feed.tmp\"\n")
  (insert "exit 0" "\n"))

;; WORKAROUND Redefine nnrss-make-hash-index to something more appropriate..
;; Only hash certain fields, exclude all others (since they tend to change way
;; too often, causing too many article "duplicates" in RSS feeds).
(defvar nnrss-article-hash-fields '(title link guid pubDate author)
  "List of fields to use as basis for generating an RSS feed item hash value.
The fields are symbols.

Common fields include:
title, link, guid, pubDate, description and author

In theory, if you never want a an article to appear more than
once (first publication), then the guid field alone should be
good enough (if the feed provides it, most do).

Be careful with including description if you don't like duplicates.
Some RSS-feeds update the description with number of comments and
other data that frequently change after the time of publication.
That will result in duplicate articles, if you include it in hashing.")
;; Re-defining function from nnrss.el:
(defun nnrss-make-hash-index (item)
  "Make a hash value of the given RSS feed item, based on the fields
set in `nnrss-article-hash-fields'."
  (setq item (gnus-remove-if (lambda(field)
                               (cond 
                                ((not (listp field)))
                                ((not (memq (car field) nnrss-article-hash-fields)))
                                ((eq 'link (car field)) ; only hash plain link field
                                                        ; (discard links in Atom xmlns for instance).
                                 (or (not (= 3 (length field)))
                                     (not (eq nil (nth 1 field)))
                                     (not (stringp (nth 2 field)))))
                                 nil)) item))
  (let ((itemstring (gnus-prin1-to-string item)))
    (gnus-message 9 "nnrss: Making hash index of %s" itemstring)
  (md5 itemstring nil nil nnrss-file-coding-system)))

gnus-demon interrupting long running IMAP operations

I had problems with gnus-demon interrupting and aborting long running IMAP operations if the gnus-demon-scan-news function was being invoked while an IMAP operation was running. My solution to this is to disable gnus-demon while these operations are running. List all functions you want protected from unfortunate interruption using the macro gnus-inhibit-demon-for-funcs, as shown below.

Code:

;; WORKAROUND Prevent gnus-demon from running handlers if we're doing long
;; running IMAP-operations. This is necessary because a call to
;; `gnus-demon-scan-news' while doing IMAP ops will disrupt the whole
;; operation :(. That sucks, so we must work around it.
(setq gnus-inhibit-demon-adv-inhibit-count 0) ; track recursive advice-inhibits
                                              ; and only un-inhibit when outer
                                              ; advised func finally returns.
(defmacro gnus-inhibit-demon-for-funcs (&rest funcs)
  (let (forms)
  (while funcs
    (let ((func (car funcs)))
      (setq forms (cons (list 'defadvice func (list 'before (make-symbol (concat (symbol-name func) "-DE-BEFORE")) 'activate)
                              (list 'gnus-message 7 (concat "Inhibiting gnus-demon for function " (symbol-name func)))
                              '(setq gnus-inhibit-demon-adv-inhibit-count (1+ gnus-inhibit-demon-adv-inhibit-count))
                              '(setq gnus-inhibit-demon t))
                        forms))
      (setq forms (cons (list 'defadvice func (list 'after (make-symbol (concat (symbol-name func) "-DE-AFTER")) 'activate 'protect)
                              '(setq gnus-inhibit-demon-adv-inhibit-count (1- gnus-inhibit-demon-adv-inhibit-count))
                              '(when (= 0 gnus-inhibit-demon-adv-inhibit-count)
                                 (setq gnus-inhibit-demon nil)))
                        forms))
      )
    (setq funcs (cdr funcs)))
  (when forms (cons 'progn forms))))
;; List all outer-level (typically interactive) functions which can take a while to 
;; complete under certain conditions, and that must *not* be interrupted by gnus-demon:
(gnus-inhibit-demon-for-funcs gnus-summary-exit
                              gnus-summary-move-article
                              gnus-summary-copy-article
                              gnus-summary-delete-article
                              gnus-summary-respool-article
                              gnus-group-select-group
                              gnus-summary-rescan-group
                              gnus-agent-fetch-session
                              gnus-summary-limit-to-headers
                              gnus-cache-enter-article
                              nnrss-run-download-script)