142 lines
3.6 KiB
Python
Executable file
142 lines
3.6 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
|
|
"""
|
|
Meh mail client
|
|
"""
|
|
|
|
import notmuch
|
|
import logging
|
|
import coloredlogs
|
|
import colorama
|
|
import datetime
|
|
import os
|
|
import progressbar
|
|
import time
|
|
|
|
colorama.init()
|
|
coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s')
|
|
log = logging.getLogger()
|
|
|
|
log.debug("Loading database")
|
|
|
|
db = notmuch.Database(mode=notmuch.Database.MODE.READ_WRITE)
|
|
# TODO Open read-only when needed
|
|
|
|
log.debug("Database loaded")
|
|
|
|
def get_location(msg):
|
|
path = msg.get_filename()
|
|
path = os.path.dirname(path)
|
|
base = db.get_path()
|
|
assert path.startswith(base)
|
|
path = path[len(base):]
|
|
_, mailbox, folder, state = path.split('/')
|
|
assert state in {'cur', 'tmp', 'new'}
|
|
return (mailbox, folder, state)
|
|
|
|
MAILBOX_COLORS = dict()
|
|
|
|
def get_mailbox_color(mailbox):
|
|
if mailbox not in MAILBOX_COLORS:
|
|
colorfile = os.path.join(db.get_path(), mailbox, 'color')
|
|
assert os.path.isfile(colorfile)
|
|
with open(colorfile, 'r') as f:
|
|
colorStr = f.read()
|
|
colorStr = colorStr[1:] if colorStr[0] == '#' else colorStr
|
|
R = int(colorStr[0:2], 16)
|
|
G = int(colorStr[2:4], 16)
|
|
B = int(colorStr[4:6], 16)
|
|
MAILBOX_COLORS[mailbox] = '\x1b[38;2;{};{};{}m'.format(R, G, B)
|
|
return MAILBOX_COLORS[mailbox]
|
|
|
|
def format_date(date):
|
|
now = datetime.datetime.now()
|
|
midnight = datetime.datetime(year=now.year, month=now.month, day=now.day)
|
|
if date > midnight:
|
|
return date.strftime('%H:%M:%S')
|
|
else:
|
|
return date.strftime('%d/%m/%y')
|
|
|
|
|
|
def print_msg(msg):
|
|
line = ""
|
|
tags = set(msg.get_tags())
|
|
mailbox, folder, state = get_location(msg)
|
|
line += get_mailbox_color(mailbox)
|
|
|
|
# Date
|
|
date = datetime.datetime.fromtimestamp(msg.get_date())
|
|
line += format_date(date)
|
|
|
|
# Icons
|
|
line += " "
|
|
def tags2col1(tag1, tag2, both, first, second, none):
|
|
nonlocal line
|
|
if tag1 in tags:
|
|
if tag2 in tags:
|
|
line += both
|
|
else:
|
|
line += first
|
|
else:
|
|
if tag2 in tags:
|
|
line += second
|
|
else:
|
|
line += none
|
|
|
|
tags2col1('spam', 'draft', '??', '💥', '📝', ' ')
|
|
tags2col1('attachment', 'encrypted', '🔐', '📎', '🔑', ' ')
|
|
tags2col1('unread', 'flagged', '🏁', '🏳 ', '🏴', ' ')
|
|
tags2col1('sent', 'replied', '?', '↑', '↪', ' ')
|
|
|
|
# TODO To: / From:
|
|
|
|
# Subject
|
|
line += " "
|
|
line += msg.get_header("subject")
|
|
line += colorama.Style.RESET_ALL
|
|
print(line)
|
|
|
|
|
|
def retag_msg(msg):
|
|
msg.freeze()
|
|
mailbox, folder, state = get_location(msg)
|
|
|
|
# Search-friendly folder name
|
|
if folder.startswith('INBOX.'):
|
|
folder = folder[6:]
|
|
folder = folder.upper()
|
|
|
|
msg.remove_all_tags()
|
|
if folder.startswith('JUNK') or folder.startswith('SPAM'):
|
|
msg.add_tag('spam')
|
|
if folder.startswith('DRAFT'):
|
|
msg.add_tag('draft')
|
|
if folder.startswith('INBOX'):
|
|
msg.add_tag('inbox')
|
|
|
|
# TODO 'sent' tag
|
|
|
|
# Save
|
|
msg.thaw()
|
|
msg.tags_to_maildir_flags()
|
|
|
|
|
|
def applyMsgs(queryStr, function, useProgressbar=False):
|
|
query = notmuch.Query(db, queryStr)
|
|
query.set_sort(notmuch.Query.SORT.OLDEST_FIRST)
|
|
msgs = query.search_messages()
|
|
if useProgressbar:
|
|
nbMsgs = query.count_messages()
|
|
iterator = progressbar.progressbar(msgs, max_value=nbMsgs)
|
|
else:
|
|
iterator = msgs
|
|
for msg in iterator:
|
|
function(msg)
|
|
|
|
applyMsgs('tag:inbox', print_msg)
|
|
# applyMsgs('tag:spam', print_msg)
|
|
# applyMsgs('tag:unread', print_msg)
|
|
# applyMsgs('from:geoffrey@frogeye.fr', print_msg)
|
|
|
|
# applyMsgs('*', retag_msg, useProgressbar=True)
|