aboutsummaryrefslogtreecommitdiff
path: root/templates/.bin/mutt-notmuch-py
blob: 40c7a76bbbad7bbb406338c438e71f7c7752b52a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#!/usr/bin/env python
"""
mutt-notmuch-py

This is a Gmail-only version of the original mutt-notmuch script.

It will interactively ask you for a search query and then symlink the matching
messages to $HOME/.cache/mutt_results.

Add this to your muttrc.

macro index / "<enter-command>unset wait_key<enter><shell-escape>mutt-notmuch-py<enter><change-folder-readonly>~/.cache/mutt_results<enter>" \
          "search mail (using notmuch)"

This script overrides the $HOME/.cache/mutt_results each time you run a query.

Install this by adding this file somewhere on your PATH.

Tested on OSX Lion, OSX El Capitan, Fedora 23, and Arch Linux.

(c) 2012-2016 --- Honza Pokorny
Licensed under BSD
"""

from __future__ import print_function
import os
import hashlib

try:
    from shlex import quote
except ImportError:
    from pipes import quote

import sys

from mailbox import Maildir
from optparse import OptionParser
from collections import defaultdict
import readline

VERSION = '1.3.0'

try:
    from commands import getoutput
except ImportError:
    from subprocess import getoutput

try:
    input = raw_input
except NameError:
    pass


def digest(filename):
    with open(filename, 'rb') as f:
        return hashlib.sha1(f.read()).hexdigest()


def pick_all_mail(messages):
    for m in messages:
        if 'All Mail' in m:
            return m


def empty_dir(directory):
    box = Maildir(directory)
    box.clear()


def command(cmd):
    return getoutput(cmd)


def normalize(path):
    if path:
      # Use expanduser() so that os.symlink() won't get weirded out by
      # tildes.
      return os.path.expanduser(path).rstrip('/')


def main(dest_box, options):
    is_gmail = options.gmail
    filter_path = options.base_path
    history_path = normalize(options.history_path)
    if history_path and os.path.exists(history_path):
        readline.read_history_file(history_path)
    query = input('Query: ')
    if history_path:
      readline.write_history_file(history_path)

    command('mkdir -p %s/cur' % dest_box)
    command('mkdir -p %s/new' % dest_box)

    empty_dir(dest_box)

    files = command('notmuch search --output=files {}'.format(quote(query))).split('\n')

    data = defaultdict(list)
    messages = []

    for f in files:
        if not f:
            continue

        if filter_path is not None and filter_path not in f:
            continue

        try:
            sha = digest(f)
            data[sha].append(f)
        except IOError:
            print('File %s does not exist' % f)

    for sha in data:
        if is_gmail and len(data[sha]) > 1:
            messages.append(pick_all_mail(data[sha]))
        else:
            messages.append(data[sha][0])

    for m in messages:
        if not m:
            continue

        target = os.path.join(dest_box, 'cur', os.path.basename(m))
        if not os.path.exists(target):
            os.symlink(m, target)


if __name__ == '__main__':
    p = OptionParser("usage: %prog [OPTIONS] [RESULTDIR]")
    p.add_option('-g', '--gmail', dest='gmail',
                 action='store_true', default=True,
                 help='gmail-specific behavior')
    p.add_option('-G', '--not-gmail', dest='gmail',
                 action='store_false',
                 help='Normal, non-gmail-specific behavior')
    p.add_option('-p', '--base-path', dest='base_path',
                 help='Only include messages in given base path')
    p.add_option('--history-path', dest='history_path',
                 help='Save/restore query history to given path')
    p.add_option('-v', '--version', dest='version', action='store_true',
                 help='Show version')
    (options, args) = p.parse_args()

    if args:
        dest = args[0]
    else:
        dest = '~/.cache/mutt_results'

    if options.version:
        print(VERSION)
        sys.exit(0)

    try:
        main(normalize(dest), options)
    except KeyboardInterrupt:
        pass