AFEW(1) afew AFEW(1) (NAME) afew - notmuch afew notmuch : o o notmuch . notmuch new (post sync hook) offlineimap post-new notmuch . maildir (killed threads) (spam) . : afew alot ( notmuch) ;) o : (QUICK START) : o afew o o post-new notmuch afew (Install) / : $ sudo aptitude install notmuch python-notmuch dbacl $ git clone git://github.com/teythoon/afew.git $ cd afew $ python setup.py install --prefix=~/.local ~/.local/bin PATH . ~/.bashrc : if [ -d ~/.local/bin ]; then PATH=$PATH:~/.local/bin fi << >> . (Initial Config) ~/.notmuch-config : [new] tags=new ~/.config/afew/config : # This is the default filter chain [SpamFilter] [KillThreadsFilter] [ListMailsFilter] [ArchiveSentMailsFilter] [InboxFilter] post-new notmuch afew : $ notmuchdir=path/to/maildir/.notmuch $ mkdir -p "$notmuchdir/hooks" $ printf > "$notmuchdir/hooks/post-new" '#!/usr/bin/env sh\n$HOME/.local/bin/afew --tag --new\n' $ chmod u+x "$notmuchdir/hooks/post-new" (Next Steps) : o o o afew --tag --all afew o afew (INSTALLATION) (Requirements) afew +3.6 notmuch . / : $ sudo aptitude install notmuch python-notmuch python-dev python-setuptools : notmuch Homebrew macOS $ brew install --with-python3 notmuch brew python3 notmuch . (Unprivileged Install) afew (virtualenv) setup.py pip. # create and activate virtualenv $ python -m venv --system-site-packages .venv $ source .venv/bin/activate # install via pip from PyPI: $ pip install afew # or install from source: $ python setup.py install --prefix=~/.local .venv/bin/afew PATH ( ~/bin/) : $ ln -snr .venv/bin/afew ~/.bin/afew (Building documentation) Sphinx : # build docs into build/sphinx/{html,man} $ python setup.py build_sphinx -b html,man (COMMAND LINE USAGE) afew . (action) . : tag . << >> . watch move-mails maildir (Initial tagging) : $ afew --tag --new # or to tag *all* messages $ afew --tag --all ~/.offlineimaprc : postsynchook = ionice -c 3 chrt --idle 0 /bin/sh -c "notmuch new && afew --tag --new" afew . (Simulation) --dry-run --tag --sync-tags notmuch . -vv . (Move Mode) afew --move-mails . --dry-run --verbose . afew ( ) maildir . . . maildir . notmuch afew --move-mails presynchook offlineimap . afew notmuch new . afew -m --notmuch-args=--no-hooks pre-new notmuch . << >> . (Commandline help) : $ afew --help Usage: afew [options] [--] [query] Options: -h, --help show this help message and exit Actions: Please specify exactly one action. -t, --tag run the tag filters -w, --watch continuously monitor the mailbox for new files -m, --move-mails move mail files between maildir folders Query modifiers: Please specify either --all or --new or a query string. -a, --all operate on all messages -n, --new operate on all new messages General options: -C NOTMUCH_CONFIG, --notmuch-config=NOTMUCH_CONFIG path to the notmuch configuration file [default: $NOTMUCH_CONFIG or ~/.notmuch-config] -e ENABLE_FILTERS, --enable-filters=ENABLE_FILTERS filter classes to use, separated by ',' [default: filters specified in afew's config] -d, --dry-run don't change the db [default: False] -R REFERENCE_SET_SIZE, --reference-set-size=REFERENCE_SET_SIZE size of the reference set [default: 1000] -T DAYS, --reference-set-timeframe=DAYS do not use mails older than DAYS days [default: 30] -v, --verbose be more verbose, can be given multiple times (CONFIGURATION) (Configuration File) afew ~/.config/afew/config . INI . . NotMuch (NotMuch Config) afew notmuch new . ~/.notmuch-config : [new] tags=new afew notmuch notmuch . notmuch afew notmuch MAILDIR $HOME/mail . afew notmuch $HOME/ ( 0.28 notmuch ). (Filter Configuration) notmuch tag . : [SpamFilter] [KillThreadsFilter] [ListMailsFilter] [ArchiveSentMailsFilter] sent_tag = '' [InboxFilter] <<>> . . . : message (verbosity) . query notmuch . new afew --new . tags . + - . (;) . tags_blacklist tags . . deer antelope@deer.com : [Filter.1] query = 'antelope@deer.com' tags = +deer message = Wild animals ahoy ( InboxFilter) new InboxFilter (Inbox) : [Filter.2] query = from'pointyheaded@boss.com' tags = -new;+boss message = Message from above (Full Sample Config) . notmuch : # immediately archive all messages from "me" notmuch tag -new -- tag:new and from:me@example.com # delete all messages from a spammer: notmuch tag +deleted -- tag:new and from:spam@spam.com # tag all message from notmuch mailing list notmuch tag +notmuch -- tag:new and to:notmuch@notmuchmail.org # finally, retag all "new" messages "inbox" and "unread" notmuch tag +inbox +unread -new -- tag:new () afew : [ArchiveSentMailsFilter] [Filter.1] message = Delete all messages from spammer query = from:spam@spam.com tags = +deleted;-new [Filter.2] message = Tag all messages from the notmuch mailing list query = to:notmuch@notmuchmail.org tags = +notmuch [InboxFilter] tag:new afew --new . : o ArchiveSentMailsFilter sent_tag ( '' sent ). . o InboxFilter unread . maildir . (More Filter Examples) : [Filter.1] query = sicsa-students@sicsa.ac.uk tags = +sicsa message = sicsa [Filter.2] query = from:foosoc.ed@gmail.com OR from:GT Silber OR from:lizzie.brough@eusa.ed.ac.uk tags = +soc;+foo message = foosoc [Filter.3] query = folder:gmail/G+ tags = +G+ message = gmail spam # skip inbox [Filter.6] query = to:notmuch@notmuchmail.org AND (subject:emacs OR subject:elisp OR "(defun" OR "(setq" OR PATCH) tags = -new message = notmuch emacs stuff # Assuming the following workflow: all messages for projects or releases should be tagged # as "project/A", "project/B" respectively "release/1.0.1" or "release/1.2.0". # # In most cases replies to messages retain their context: the project, the release(s), .. # # The following config will propagate all project/... or release/... tags from a thread # to all new messages. [PropagateTagsByRegexInThreadFilter.1] propagate_tags = project/.* # do not tag spam filter = not is:spam [PropagateTagsByRegexInThreadFilter.2] propagate_tags = release/.* (FILTERS) ( ) : [SpamFilter] [KillThreadsFilter] [ListMailsFilter] [ArchiveSentMailsFilter] [InboxFilter] . message query / tags . ArchiveSentMailsFilter SentMailsFilter : o new inbox InboxFilter . DKIMValidityFilter DKIM DKIM dkim-ok dkim-fail . DMARCReportInspectionFilter DMARC ZIP . XML . : SPF "dmarc-spf-fail" "dmarc-spf-ok" . DKIM "dmarc-dkim-fail" "dmarc-dkim-ok" . FolderNameFilter . procmail sieve . o folder_explicit_list = o . . o ( ). o . o 'Sent' . o folder_blacklist = o . . o . o 'INBOX' 'inbox' . o folder_transforms = o . 'folder:tag' . (:) . o . o 'Junk' 'spam' / (draft) (sent) : folder_transforms = Junk:spam Drafts:draft Sent:sent o folder_lowercases = true o o maildir_separator = o maildir . o '.' . o maildir (collapsed dirs) . maildir : [...] /path/to/maildir/devel.afew/[cur|new|tmp]/... /path/to/maildir/devel.alot/[cur|new|tmp]/... /path/to/maildir/devel.notmuch/[cur|new|tmp]/... [...] afew 'devel' 'afew' . '/' : [...] /path/to/maildir/devel/afew/[cur|new|tmp]/... /path/to/maildir/devel/alot/[cur|new|tmp]/... /path/to/maildir/devel/notmuch/[cur|new|tmp]/... [...] : maildir_separator = / HeaderMatchingFilter . . : o header = o pattern = o tags = {} . : [HeaderMatchingFilter.1] header = X-Spam-Flag pattern = YES tags = +spam [HeaderMatchingFilter.2] header = List-Id pattern = <(?P.*)> tags = +lists;+{list_id} [HeaderMatchingFilter.3] header = X-Redmine-Project pattern = (?P.+) tags = +redmine;+{project} SpamFilter ListMailsFilter HeaderMatchingFilter . InboxFilter new inbox (killed) (spam) . ( notmuch new ). KillThreadsFilter killed killed . . ListMailsFilter List-Id lists lists/ . MeFilter Notmuch (primary_email other_email) . to-me me_tag . SentMailsFilter : o sent_tag = o . o . o 'sent' . . o to_transforms = o To/Cc/Bcc . <'user_part@domain_part>:tags' . (:) . ':tags' 'user_part' . 'tags' . o List-Id . SpamFilter : o spam_tag = o . o 'spam' . o 'junk' 'scum' . . X-Spam-Flag . (Customizing filters) : SpamFilter : 1. [SpamFilter.0] # note the index message = meh SpamFilter . SpamFilter . 2. ... [ShitFilter(SpamFilter)] message = I hatez teh spam! [ShitFilter.0] [ShitFilter.1] message = Me hatez it too. . (entry points) . setup.py afew . afew . (MOVE MODE) (Configuration Section) : [MailMover] folders = INBOX Junk rename = False max_age = 15 # rules INBOX = 'tag:spam':Junk 'NOT tag:inbox':Archive Junk = 'NOT tag:spam AND tag:inbox':INBOX 'NOT tag:spam':Archive . (Rules) ( ). : folders = INBOX Junk "Sent Mail" : = ['':]+ . . maildir . ( ) . folders . INBOX = 'tag:spam':Junk maildir INBOX ( ) spam Junk . notmuch . : Junk = 'NOT tag:spam AND tag:inbox':INBOX 'NOT tag:spam':Archive Junk spam inbox INBOX . spam Archive . (Max Age) max_age . max_age = 15 afew . (Rename) IMAP mbsync . mbsync . rename UID mbsync "Maildir error: duplicate UID 1234" "UID 567 is beyond highest assigned UID 89" . afew UID mbsync . mbsync UID UID . offlineimap . rename = True (Limitations) (1) . INBOX = 'NOT tag:inbox':Archive Junk = 'NOT tag:spam':INBOX INBOX . Junk inbox Archive . spam inbox INBOX ;) (2) (1:1) . . . AFEW (EXTENDING AFEW) ~/.config/afew/ afew (import) . Filter register_filter . : from afew.filters.BaseFilter import Filter from afew.FilterRegistry import register_filter PROJECT_MAPPING = { 'fabric': 'deployment', 'oldname': 'new-name', } @register_filter class RedmineFilter(Filter): message = 'Create tag based on redmine project' query = 'NOT tag:redmine' def handle_message(self, message): project = message.get_header('X-Redmine-Project') if project in PROJECT_MAPPING: project = PROJECT_MAPPING[project] self.add_tags(message, 'redmine', project) message query Filter . message . query . handle_message() . . notmuch : afew : header() filename() get_thread(). Filter add_tags() remove_tags() <<>> . : [RedmineFilter] (IMPLEMENTATION) (Database Manager) alot alot.db.DBManager . class afew.Database.Database notmuch. add_message(path, sync_maildir_flags=False, new_mail_handler=None) (index) notmuch . (Parameters) o path (str ) -- o sync_maildir_flags (bool ) -- True notmuch maildir o new_mail_handler ( notmuch.Message ) -- (Raises) notmuch.NotmuchError (Returns) notmuch.Message close() notmuch . do_query(query) notmuch . (Parameters) query (str ) -- (Returns) (Return type) notmuch.Query get_messages(query, full_thread=False) . (Parameters) o query (str ) -- Database.do_query() o full_thread (bool ) -- (Returns) (iterator) notmuch.Message remove_message(path) notmuch . (Parameters) path (str ) -- walk_replies(message) . (Parameters) message (notmuch.Message) -- (Returns) (iterator) notmuch.Message walk_thread(thread) . (Parameters) thread (notmuch.Thread) -- (Returns) (iterator) notmuch.Message (Filter) class afew.filters.BaseFilter.Filter(database, **kwargs) flush_changes() notmuch () . (Configuration management) (Miscellaneous utility functions) o (Index) o (Module Index) o (Search Page) (AUTHOR) Justus Winter (COPYRIGHT) afewmail project 4.0.2 July 22, 2026 AFEW(1)