1 Return-Path: <bgamari.foss@gmail.com>
\r
2 X-Original-To: notmuch@notmuchmail.org
\r
3 Delivered-To: notmuch@notmuchmail.org
\r
4 Received: from localhost (localhost [127.0.0.1])
\r
5 by olra.theworths.org (Postfix) with ESMTP id BC2EF431FB6
\r
6 for <notmuch@notmuchmail.org>; Wed, 23 Feb 2011 06:23:06 -0800 (PST)
\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org
\r
11 X-Spam-Status: No, score=-0.799 tagged_above=-999 required=5
\r
12 tests=[DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1,
\r
13 FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled
\r
14 Received: from olra.theworths.org ([127.0.0.1])
\r
15 by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)
\r
16 with ESMTP id ODwLvKJv1aog for <notmuch@notmuchmail.org>;
\r
17 Wed, 23 Feb 2011 06:23:03 -0800 (PST)
\r
18 Received: from mail-qw0-f53.google.com (mail-qw0-f53.google.com
\r
19 [209.85.216.53]) (using TLSv1 with cipher RC4-SHA (128/128 bits))
\r
20 (No client certificate requested)
\r
21 by olra.theworths.org (Postfix) with ESMTPS id D4753431FB5
\r
22 for <notmuch@notmuchmail.org>; Wed, 23 Feb 2011 06:23:02 -0800 (PST)
\r
23 Received: by qwc9 with SMTP id 9so3634960qwc.26
\r
24 for <notmuch@notmuchmail.org>; Wed, 23 Feb 2011 06:23:00 -0800 (PST)
\r
25 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma;
\r
26 h=domainkey-signature:from:to:subject:user-agent:date:message-id
\r
27 :mime-version:content-type;
\r
28 bh=XNXQJ/4qiywr7H3pAoW8UzYwekyO85KZWLvkHnrlqJk=;
\r
29 b=Se1aB/X9IBSehMQnTJOTIN4lSTF68cLaw0HSaMoc4DaTZhdMDafvAP1VvAzCoZdOmv
\r
30 24+yaju5oq3u548esdb6MQS2KyIP4LHZOeI2JfKDXTg1JOgldS6Th8qQ4HvWnXP2Xngm
\r
31 U2XKa6Wyi1Vgi7WniNrw95q61zH04lZIU9olc=
\r
32 DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma;
\r
33 h=from:to:subject:user-agent:date:message-id:mime-version
\r
35 b=QPYHPHchycWoFKl79YakJTET3RucUNX6+2repdRdOI8UtFY70NIRJ21783CGUVjz0m
\r
36 KHKxDRHRYAqKCli98h0hpBWnM269QjUqmFuyVJA2+LnxVhhCAWvVxFBEs9srQ+qgYIKg
\r
37 AOCNr28kk8Zr7rFikajK+XYWw4vA88MHtnUOs=
\r
38 Received: by 10.229.229.139 with SMTP id ji11mr3041165qcb.174.1298470980042;
\r
39 Wed, 23 Feb 2011 06:23:00 -0800 (PST)
\r
40 Received: from localhost (gamari.physics.umass.edu [128.119.56.223])
\r
41 by mx.google.com with ESMTPS id g28sm5479260qck.37.2011.02.23.06.22.59
\r
42 (version=TLSv1/SSLv3 cipher=OTHER);
\r
43 Wed, 23 Feb 2011 06:22:59 -0800 (PST)
\r
44 From: Ben Gamari <bgamari.foss@gmail.com>
\r
45 To: notmuch <notmuch@notmuchmail.org>
\r
46 Subject: My mail configuration
\r
47 User-Agent: Notmuch/0.5-64-gdd23272 (http://notmuchmail.org) Emacs/23.1.1
\r
48 (x86_64-pc-linux-gnu)
\r
49 Date: Wed, 23 Feb 2011 09:22:57 -0500
\r
50 Message-ID: <87tyfu3k5a.fsf@gmail.com>
\r
52 Content-Type: text/plain; charset=us-ascii
\r
53 X-BeenThere: notmuch@notmuchmail.org
\r
54 X-Mailman-Version: 2.1.13
\r
56 List-Id: "Use and development of the notmuch mail system."
\r
57 <notmuch.notmuchmail.org>
\r
58 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
\r
59 <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
\r
60 List-Archive: <http://notmuchmail.org/pipermail/notmuch>
\r
61 List-Post: <mailto:notmuch@notmuchmail.org>
\r
62 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
\r
63 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
\r
64 <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
\r
65 X-List-Received-Date: Wed, 23 Feb 2011 14:23:06 -0000
\r
67 Here is my mail sorting script that has been slowly evolving for almost
\r
68 a year now. It uses the Python bindings, along with Bogofilter for spam
\r
69 filtering. There is also an update-spam script which brings the
\r
70 Bogofilter database in to synchronization with the notmuch tags. On this
\r
71 note, if someone wants to implement the ability to hide certain tags
\r
72 (say, those matching /\..+/) in the emacs interface it would be greatly
\r
73 appreciated. I have notmuch configured such that all new mail starts
\r
74 with just the "new" tag. The sorting script then takes it from
\r
75 there. Hope this will give folks some ideas.
\r
82 ===File ~/.env/mail/sort_mail.py============================
\r
86 # Be careful about using Query.count_messages(), it's technically an estimate
\r
87 # and is not guarranteed to be correct
\r
93 logging.basicConfig(level=logging.DEBUG)
\r
96 start_time = time.time()
\r
98 def sf_list(name, tag):
\r
99 #_tags.append( ('to:%s@lists.sourceforge.net' % name, ['list', tag]) )
\r
100 #_tags.append( ('to:%s@lists.sf.net' % name, ['list', tag]) )
\r
101 _tags.append( ('to:%s' % name, ['list', tag]) )
\r
103 def kernel_list(name, tag):
\r
104 #_tags.append( ('to:%s@vger.kernel.org' % name, ['list', tag]) )
\r
105 _tags.append( ('to:%s' % name, ['list', tag]) )
\r
107 def fdo_list(name, tag):
\r
108 #_tags.append( ('to:%s@lists.freedesktop.org' % name, ['list', tag]) )
\r
109 _tags.append( ('to:%s' % name, ['list', tag]) )
\r
111 def _list(name, tag):
\r
112 _tags.append( ('to:%s' % name, ['list', tag]) )
\r
114 def tag(filter, *tags):
\r
115 _tags.append( (filter, tags) )
\r
117 kernel_list('linux-kernel', 'lkml')
\r
118 kernel_list('mm-commits', 'mm-commits')
\r
119 kernel_list('linux-omap', 'linux-omap')
\r
120 kernel_list('linux-next', 'linux-next')
\r
121 kernel_list('linux-wireless', 'linux-wireless')
\r
122 kernel_list('linux-btrfs', 'btrfs')
\r
123 _list('linux-pm', 'linux-pm')
\r
124 _list('linux-arm-kernel', 'linux-arm')
\r
125 sf_list('oprofile-list', 'oprofile')
\r
126 sf_list('spi-devel-general', 'spi-devel')
\r
127 sf_list('linux1394-devel', 'ieee1394')
\r
129 sf_list('ipw3945-devel', 'ipw')
\r
130 _list('hostap@lists.shmoo.com', 'hostap')
\r
131 _list('ath9k-devel@', 'ath9k')
\r
132 _list('vim-dev@vim.org', 'vim')
\r
133 _list('vim_dev', 'vim')
\r
135 fdo_list('intel-gfx', 'intel-gfx')
\r
136 fdo_list('xorg', 'xorg')
\r
137 fdo_list('hal', 'hal')
\r
138 fdo_list('compiz', 'compiz')
\r
139 sf_list('dri-devel', 'dri')
\r
140 sf_list('dri-users', 'dri')
\r
141 sf_list('mesa3d-dev', 'mesa')
\r
142 fdo_list('mesa-dev', 'mesa')
\r
144 fdo_list('devkit-devel', 'devkit')
\r
145 sf_list('matplotlib-users', 'matplotlib')
\r
146 sf_list('matplotlib-devel', 'matplotlib')
\r
147 _list('notmuch@notmuchmail.org', 'notmuch')
\r
148 _list('eigen@lists.tuxfamily.org', 'eigen')
\r
149 _list('launchpad-users@lists.launchpad.net', 'launchpad')
\r
150 _list('boost@lists.boost.org', 'boost')
\r
151 _list('debian-python@lists.debian.org', 'debian-python')
\r
153 _list('geda-user@', 'geda')
\r
155 _list('openembedded-devel@lists.openembedded.org', 'openembedded')
\r
156 _list('beagleboard@googlegroups.com', 'beagleboard')
\r
157 _list('angstrom-distro-devel@linuxtogo.org', 'angstrom')
\r
158 _list('angstrom-distro-users@linuxtogo.org', 'angstrom')
\r
160 _list('mono-devel-list@lists.ximian.com', 'mono')
\r
161 _list('mono-list@', 'mono')
\r
162 _list('ubuntu-devel-discuss@lists.ubuntu.com', 'ubuntu-devel')
\r
163 _list('git@vger.kernel.org', 'git')
\r
164 _list('sup-talk@rubyforge.org', 'sup')
\r
165 _list('thrust-users@googlegroups.com', 'thrust')
\r
166 _list('golang-nuts@googlegroups.com', 'go')
\r
167 _list('numpy-discussion@scipy.org', 'numpy')
\r
168 _list('scipy-user@scipy.org', 'scipy')
\r
170 _list('rsync@lists.samba.org', 'rsync')
\r
171 tag('from:samba-bugs', 'bugs', 'rsync', 'list')
\r
173 _list('containers@', 'containers')
\r
175 tag('from:bugzilla', 'bugs', 'list')
\r
177 # Tags that aren't for lists
\r
178 tag('from:Facebook', 'facebook')
\r
179 tag('to:gdh@gdhour.com', 'gdh')
\r
181 tag('to:bgamari@gmail.com', 'gmail')
\r
182 tag('to:bgamari.foss@gmail.com', 'foss')
\r
183 tag('from:Ben Gamari', 'sent')
\r
184 tag('from:bgamari.foss', 'sent')
\r
186 from sort_junk import sort_junk
\r
187 from notmuch_utils import *
\r
189 db = notmuch.Database(mode=notmuch.Database.MODE.READ_WRITE)
\r
191 # Freeze new messages
\r
192 q_new = notmuch.Query(db, 'tag:new')
\r
194 for msg in q_new.search_messages():
\r
198 # Take care of basics
\r
199 tag_search(db, 'tag:new', '+unread', '+unseen')
\r
201 # Take care of feeds
\r
202 tag_search(db, 'folder:feeds', '+feeds', '-new')
\r
204 # Run through Bogofilter
\r
208 for filter, tags in _tags:
\r
209 tag_search(db, '%s and tag:new' % filter, *tags)
\r
211 # Ignore things I sent
\r
212 tag_search(db, 'tag:new and tag:sent', '-unseen', '-new', '-unread', '+watch')
\r
215 for msg in q_new.search_messages():
\r
216 q = notmuch.Query(db, 'tag:watch and thread:%s' % msg.get_thread_id())
\r
217 if len(q.search_messages()) > 0:
\r
218 logging.debug('watching %s' % msg.get_message_id())
\r
219 msg.add_tag('watch')
\r
221 # Watched items should go to inbox
\r
222 tag_search(db, 'tag:new and tag:watch', '+inbox', '-new')
\r
224 # Ignore threads that I've already seen
\r
225 q = notmuch.Query(db, 'tag:new and tag:list')
\r
226 for msg in q.search_messages():
\r
227 q2 = notmuch.Query(db, 'thread:%s and not tag:unseen' % msg.get_thread_id())
\r
228 if len(q2.search_messages()) > 0:
\r
229 msg.remove_tag('unseen')
\r
230 msg.remove_tag('new')
\r
232 # Remove new from sorted list items
\r
233 tag_search(db, 'tag:new and tag:list', '-new')
\r
235 # Tag remaining new items for inbox
\r
236 tag_search(db, 'tag:new', '+inbox', '-new')
\r
238 # Thaw new messages
\r
239 for msg in q_new.search_messages():
\r
242 end_time = time.time()
\r
243 logging.info('Sorted %d messages in %1.2f seconds' % (n_msgs, end_time - start_time))
\r
245 ============================================================
\r
248 ===File ~/.env/mail/sort_junk.py============================
\r
253 from subprocess import PIPE
\r
257 def sort_junk(query):
\r
258 spam_re = re.compile('X-Bogosity:\s*Spam')
\r
259 spamicity_re = re.compile('spamicity=(\d\.\d+)')
\r
260 bf = subprocess.Popen(['bogofilter', '-bv'], stdin=PIPE, stdout=PIPE)
\r
261 for msg in query.search_messages():
\r
262 bf.stdin.write(msg.get_filename() + '\n')
\r
263 l = bf.stdout.readline()
\r
264 if re.search(spam_re, l):
\r
265 logging.debug('Message %s marked as junk' % msg.get_message_id())
\r
266 msg.add_tag('junk')
\r
269 if __name__ == '__main__':
\r
271 db = notmuch.Database(mode=notmuch.Database.MODE.READ_WRITE)
\r
272 query = notmuch.Query(db, ' '.join(sys.argv[1:]))
\r
275 ============================================================
\r
278 ===File ~/.env/mail/update-junk=============================
\r
282 from notmuch_utils import *
\r
284 from time import time
\r
287 logging.basicConfig(level=logging.INFO)
\r
289 db = notmuch.Database(mode=notmuch.Database.MODE.READ_WRITE)
\r
290 if '--clean' in sys.argv:
\r
291 import shutil, os.path
\r
292 shutil.rmtree(os.path.expanduser('~/.bogofilter'))
\r
293 tag_search(db, 'tag:.bf_spam', '-.bf_spam')
\r
294 tag_search(db, 'tag:.bf_ham', '-.bf_ham')
\r
296 def do_update(search, tag_func, bf_args):
\r
297 start_time = time()
\r
298 p = subprocess.Popen(['bogofilter', bf_args], stdin=subprocess.PIPE)
\r
299 q = notmuch.Query(db, search)
\r
301 for msg in q.search_messages():
\r
302 p.stdin.write('%s\n' % msg.get_filename())
\r
307 return (n, time()-start_time)
\r
309 logging.info('Registering spam')
\r
310 n,t = do_update('tag:junk and not tag:.bf_spam', lambda msg: msg.add_tag('.bf_spam'), '-sb')
\r
311 logging.info('Registered %d spam in %1.2f seconds' % (n,t))
\r
313 logging.info('Unregistering spam')
\r
314 n,t = do_update('not tag:junk and tag:.bf_spam', lambda msg: msg.remove_tag('.bf_spam'), '-Sb')
\r
315 logging.info('Unregistered %d spam in %1.2f seconds' % (n,t))
\r
317 # Only consider messages that have been read as ham
\r
318 logging.info('Registering ham')
\r
319 n,t = do_update('not tag:junk and not tag:unread and not tag:.bf_ham', lambda msg: msg.add_tag('.bf_ham'), '-nb')
\r
320 logging.info('Registered %d ham in %1.2f seconds' % (n,t))
\r
322 logging.info('Unregistering ham')
\r
323 n,t = do_update('tag:junk and tag:.bf_ham', lambda msg: msg.remove_tag('.bf_ham'), '-Nb')
\r
324 logging.info('Unregistered %d ham in %1.2f seconds' % (n,t))
\r
326 ============================================================
\r