vm-auto-archive-messages'
You can use VM's `vm-auto-folder-alist' to automatically
send messages to a default destination. This is described in the
previous section. Use the
`A' key (bound to
`vm-auto-archive-messages') to automatically save
messages to their folders.
If you'd like this to happen
automatically when new mail is received, you can add the
`vm-auto-archive-messages' function to
`vm-arrived-messages-hook'.
If you wanted new mail, upon arrival, to be automatically moved to
the folders defined in `vm-auto-folder-alist' and then
deleted, you could do this:
(setq vm-delete-after-archiving t) (defun my-vm-arrived-messages-hook () (vm-auto-archive-messages) (vm-expunge-folder)) (add-hook 'vm-arrived-messages-hook 'my-vm-arrived-messages-hook)
Use the code below to automatically generate the folder name to archive the message based on the user name in the From: address. The code will usually match any message. So if you want some messages to go to other folders, put those alist entries ahead of the comment in the code below.
(setq vm-auto-folder-alist
;; code to make auto-folder default to the user name of the sender
'(("From"
("<\\([^ \t\n\f@%()<>]+\\)[@%]\\([^ \t\n\f<>()]+\\)>" .
(buffer-substring (match-beginning 1) (match-end 1)))
("<\\([^>]+\\)>" .
(buffer-substring (match-beginning 1) (match-end 1)))
("\\([^ \t\n\f@%()<>]+\\)\\([@%]\\([^ \t\n\f<>()]+\\)\\)?" .
(buffer-substring (match-beginning 1) (match-end 1)))
))
)
Here's my code for auto-deleting spam.
;;; Auto-delete spam mail.
;;; Just set up a virtual folder spec for the auto-delete folder, like this:
;;;
;;; Where it says (inbox) below, you should put ("/the/path/to/your/inbox")
;;; Or use backqoute and the value of vm-primary-inbox (1998 June 20 SOM)
;;;
;;; (setq vm-virtual-folder-alist
;;; (list
;;; '("auto-delete"
;;; ((inbox)
;;; (and (or (unread) (new))
;;; (or (author "AlphaCONNECT")
;;; (author "credit@usa.net")
;;; (and (author "mailer-daemon@rational.com")
;;; (subject "Account no longer active"))
;;; ))))))
(defun vm-auto-delete ()
"*Mark for deletion any message matched by the
auto-delete virtual folder specification."
(interactive)
(condition-case c
(if (and (boundp 'vm-virtual-folder-alist)
(assoc "auto-delete" vm-virtual-folder-alist))
(let (n)
(save-window-excursion
(save-excursion
(vm-apply-virtual-folder "auto-delete")
(setq n (length vm-message-list))
(if (> n 0)
(progn
(vm-goto-message 1)
(vm-delete-message n)))
(vm-quit)))
(if (> n 0)
(message "%d messages marked for deletion." n)
(message "No messages marked for deletion."))))
(error (message "vm-auto-delete error"))))
(add-hook 'vm-arrived-messages-hook 'vm-auto-delete)
See the FAQ for using Procmail with VM.
As I mentioned in my posting to bug-vm on Tuesday I've recently started working with virtual folders in VM, and am generally very impressed, but have found some features lacking, namely the poor integration of virtual folders with real folders (i.e. incorporation of new mail isn't dynamic, etc.) and some user-interface features are less than ideal (a [virtual? both?] folders summary window would be great, as would dynamic updating of the "Virtual:Visit" menu (and the "Folder:Visit" one) to show the number of messages in each folder -- it's too easy to ignore mail now!).
I've since refined my implementation of workarounds to one of the features I've found lacking: namely I've implemented a hack to automatically incorporate new mail into virtual folders when new mail is incorporated into their underlying buffers and to automatically quit from virtual folders when their underlying folder is closed. These hacks are far from perfect yet -- I assume only one primary "INBOX", and that all virtual folders are derived from this one real folder.
I enclose the code here in hopes it will be helpful to someone, and in hopes that it will encourage other users to think about these features and perhaps someone will implement these features in a more integrated way.
I've been reading mail using these functions for the past few days
and I don't know how I ever managed with the jumble before!
(I.e. after I start VM I then immediately hit 'G' to get
new mail and open all the virtual folders, then I read mail almost
exclusively from the virtual folders, and I switch to INBOX and
'q' before I go to work to close everything at home so I
can read from there, and vice versa when coming home; and if I need
to read something new quick I use 'g' and read it
directly from the INBOX folder.
(defvar my-vm-virtual-leftovers-folder "General Delivery"
"The name of the virtual folder containing all the messages which are not
members of other virtual folders. (It's created after all the others are
created by my-vm-visit-all-virtual-folders.)")
(defun my-vm-quit-all-virtual-folders ()
"Quit from all of the virtual folders in vm-virtual-folder-alist."
(interactive)
(let ((vfolder vm-virtual-folder-alist))
(while vfolder
(save-excursion
(condition-case error-data
(let ((vm-confirm-quit nil))
(set-buffer (concat "(" (car (car vfolder)) ")"))
(vm-quit))
(error nil))
(setq vfolder (cdr vfolder))))))
(defun my-vm-visit-all-virtual-folders ()
"Visit all of the virtual folders in vm-virtual-folder-alist."
(interactive)
(let ((vfolder vm-virtual-folder-alist))
(while vfolder
(let ((vfname (car (car vfolder))))
(if (not (string-equal vfname my-vm-virtual-leftovers-folder))
(vm-visit-virtual-folder vfname))
(setq vfolder (cdr vfolder)))))
(vm-visit-virtual-folder my-vm-virtual-leftovers-folder))
(defun my-vm-resync-all-virtual-folders ()
"Revisit all virtual folders to update them."
(interactive)
(save-excursion
(my-vm-quit-all-virtual-folders)
(my-vm-visit-all-virtual-folders)))
(defun my-vm-get-new-mail ()
"Local version of vm-get-new-mail that updates all virtual folders in
vm-virtual-folder-alist."
(interactive)
(save-excursion
(my-vm-quit-all-virtual-folders)
(set-buffer (vm-get-file-buffer vm-primary-inbox))
(vm-get-new-mail)
(my-vm-visit-all-virtual-folders)))
;; This overrides the normal binding of 'G' to vm-sort-messages....
(define-key vm-mode-map "G" 'my-vm-get-new-mail)
(defun my-vm-quit ()
"Local version of vm-quit that quits all virtual buffers if quitting
vm-primary-inbox."
(interactive)
(vm-select-folder-buffer)
(if (eq major-mode 'vm-virtual-mode)
(vm-quit)
(if (string-equal (buffer-file-name)
(expand-file-name vm-primary-inbox))
(my-vm-quit-all-virtual-folders))
(vm-quit)))
(define-key vm-mode-map "q" 'my-vm-quit)
;; This is the list of virtual folders created by my-vm-visit-all-virtual-folders
;;
;; XXX It would be nice to use vm-primary-inbox in some way instead of "INBOX"
;; XXX Wouldn't it?
;;
;; WARNING: these definitions are based on the assumption that VM has
;; been patched to ignore case when comparing selector values to data.
;;
(setq vm-virtual-folder-alist
;; note the magic of creating the first element of the alist with
;; a symbol that needs to be eval'ed at assignment time
(append (list (list (symbol-value 'my-vm-virtual-leftovers-folder)
(list (list "INBOX")
'(not (virtual-folder-member)))))
'(("automake"
(("INBOX")
(sender-or-recipient "automake@")))
("cvs"
(("INBOX")
(or (sender-or-recipient "bug-cvs@")
(sender-or-recipient "info-cvs@"))))
("gnu-emacs"
(("INBOX")
(or (sender-or-recipient "bug-gnu-emacs@")
(sender-or-recipient "gnu-emacs-sources@")
(sender-or-recipient "info-gnu-emacs@")
(sender-or-recipient "gnu-emacs-bug@"))))
("VM"
(("INBOX")
(or (sender-or-recipient "bug-vm@")
(sender-or-recipient "info-vm@"))))
("exim"
(("INBOX")
(or (sender-or-recipient "exim-users@")
(sender-or-recipient "exim-announce@"))))
("ipfilter"
(("INBOX")
(sender-or-recipient "ipfilter@")))
("nanog"
(("INBOX")
(or (sender-or-recipient "nanog@")
(sender-or-recipient "nanog-announce@"))))
("smail3"
(("INBOX")
(sender-or-recipient "smail3")))
("vmailer"
(("INBOX")
(sender-or-recipient "vmailer-testers@")))
("other-lists"
(("INBOX")
(or (sender-or-recipient "ssh@")
(sender-or-recipient "stk@"))))
("ssh"
(("INBOX")
(sender-or-recipient "ssh@")))
("current-users"
(("INBOX")
(sender-or-recipient "current-users@")))
("netbsd-bugs"
(("INBOX")
(or (sender-or-recipient "gnats-bugs@gnats.netbsd.org")
(sender-or-recipient "netbsd-bugs@"))))
("netbsd-other"
(("INBOX")
(or (sender-or-recipient "netbsd-announce@")
(sender-or-recipient "netbsd-help@")
(sender-or-recipient "netbsd-ports@")
(sender-or-recipient "netbsd-users@"))))
("source-changes"
(("INBOX")
(or (sender-or-recipient "source-changes@")
(sender-or-recipient "source@netbsd.org"))))
("netbsd-tech"
(("INBOX")
(or (sender-or-recipient "tech-install@")
(sender-or-recipient "tech-kern@")
(sender-or-recipient "tech-net@")
(sender-or-recipient "tech-ports@")
(sender-or-recipient "tech-security@")
(sender-or-recipient "tech-toolchain@")
(sender-or-recipient "tech-userlevel@"))))
("port-alpha"
(("INBOX")
(sender-or-recipient "port-alpha@")))
("port-arm32"
(("INBOX")
(sender-or-recipient "port-arm32@")))
("port-i386"
(("INBOX")
(sender-or-recipient "port-i386@")))
("port-pmax"
(("INBOX")
(sender-or-recipient "port-pmax@")))
("port-powerpc"
(("INBOX")
(sender-or-recipient "port-powerpc@")))
("port-sparc"
(("INBOX")
(sender-or-recipient "port-sparc@")))
("port-sun3"
(("INBOX")
(sender-or-recipient "port-sun3@"))))))
I am posting a new way to auto-archive mail. I think it beats how VM
does auto-archiving, and for those of you who have taken the trouble
to figure out how do do it, well, here's an alternative. More
importantly, for those who thought that it was too much trouble to do
it before, well now is your chance. It is almost identical to VM's
`vm-auto-archive-messages', but different in that it
uses virtual folders to sort things out.
If you have defined your very own virtual folder selectors in
`vm-virtual-folder-alist', then you know how easy VM
makes it, and you're already more than half-way there. For example,
here's a piece of mine...
(setq vm-virtual-folder-alist
'(("anneh"
(("INBOX")
(and (author "anneh")
(header "jobslist"))))
("spam"
(("INBOX")
(or (not (or (recipient "cadet\\|dave")
(author "cadet\\|dave\\|mit\.edu\\|anneh")
(header "xemacs\.org\\|dismal-users")
(subject "\\bmit\\b")
(text "\\bmit\\b")))
(header "^X-advertisement"))))))
well, now I can use those selectors (like "spam") to archive my messages. Here's how easy it is...
1) add this somewhere in you .vm file:
(defun db-vm-auto-archive-messages (arg)
"Auto archive messages using applied virtual folders.
The prefix arg is meaningless here, unlike in `vm-auto-archive-messages'
where it does the following:
Prefix arg means to ask user for confirmation before saving each message.
Here, you will not be prompted before saving at all.
When invoked on marked messages (via vm-next-command-uses-marks),
only marked messages are checked against vm-auto-folder-alist.
Archiving depends on the value of `db-vm-auto-folder-alist':
given each entry in `db-vm-auto-folder-alist', this function will
archive messages in the virtual folder which is the car of the entry
into the folder which is the cdr of the entry."
(interactive "P")
(save-excursion
(save-window-excursion
(flet ((bbdb/vm-update-record (&rest args) nil))
(let ((use-marked (eq last-command 'vm-next-command-uses-marks))
(vm-startup-with-summary nil) ; for efficiency
(last-command 'vm-next-command-uses-marks))
(vm-select-folder-buffer)
(vm-check-for-killed-summary)
(vm-error-if-folder-empty)
(if use-marked
(vm-create-virtual-folder 'marked)
(vm-create-virtual-folder 'unfiled))
(loop for entry in db-vm-auto-folder-alist
for selector = (if (stringp (car entry))
(car entry)
(symbol-name (car entry)))
for folder = (if (cdr entry)
(concat (file-truename (or vm-folder-directory
default-directory))
(cdr entry)))
do (save-excursion
(vm-apply-virtual-folder selector)
(condition-case nil
(progn
(vm-mark-all-messages)
(if folder
(vm-save-message folder)
(call-interactively 'vm-delete-message)))
(error nil))
(vm-quit))))
(vm-quit))))) ; quit unfiled/marked folder
(defvar db-vm-auto-folder-alist nil
"*Alist of virtual folder selectors and folder names for auto archiving.
\(see `db-vm-auto-archive-messages' for details\)
For each entry in the alist, the car must be a symbol or string matching a
virtual folder selector defined in `vm-virtual-folder-alist'.
The cdr can be any folder name, such as \"spam\" \(understood to be in
`vm-folder-directory', otherwise in `default-directory' if
`vm-folder-directory' is nil\). If the cdr is `nil', then the messages
will be deleted.
An example of how to use this variable is:
\(setq db-vm-auto-folder-alist '\(\(spam . nil\)
\(jobfind . \"MAILING-LISTS\"\)
\(spousefind . \"MAILING-LISTS\"\)
\(mom . \"MOM\"\)\)\)")
(define-key vm-mode-map "A" 'db-vm-auto-archive-messages)
2) read the docs on how to use
`db-vm-auto-archive-messages'. Here's an example of
what I might put for `db-vm-auto-folder-alist':
(setq db-vm-auto-folder-alist '((spam . nil)
(anneh . "ANNEH")))
and that's it. for lot's of people, this won't be very hard to do at all, since you've already gone through the trouble of writing your virtual folder selectors, and if you haven't, you'll find that they're quite easy and fun to write.
VM in itself has no provisions explicitly for spam filtering, but the vm-auto-folder-alist can be used for simple spam filtering, e.g.:
(setq vm-auto-folder-alist
'(("^Subject: "
("viagra\\|mortgage\\|enlarge" . "spam")
("weight.*loss" . "spam"))
("^X_Mailer: "
("BulkMailer" . "spam")
("Extractor Pro" . "spam"))))
Auto-folders can also be used with externally (on the mail server) run spam filters, such as SpamAssassin or bogofilter using the auto-folders to select the tagged mails, e.g.:
(setq vm-auto-folder-alist
'(("^X-Spam-Status: " ("POSSIBLE SPAM" . "spam"))
("^X-Spam-Flag: " ("YES" . "spam"))
("^X-Bogosity: " ("Yes," . "spam"))))
These spam filters can also be run locally on your own machine, giving you better control over them. In this case, you also may want to try one of the VM-interfaces to these spamfilters: