This commit is contained in:
Geoffrey Frogeye 2018-08-14 10:08:59 +02:00
parent 18a7278422
commit 47cdc830a0
1 changed files with 149 additions and 44 deletions

View File

@ -19,39 +19,25 @@ import argparse
import configparser
import base64
import shutil
import argparse
import xdg.BaseDirectory
import sys
import subprocess
colorama.init()
coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s')
log = logging.getLogger()
ACCOUNTS = dict()
ALIASES = set()
log.debug("Loading config")
def generate_aliases():
for name in config.sections():
if not name.islower():
continue
section = config[name]
ALIASES.add(section["from"])
if "alternatives" in section:
for alt in section["alternatives"].split(";"):
ALIASES.add(alt)
ACCOUNTS[name] = section
# TODO XDG
configPath = os.path.join(os.path.expanduser('~'), '.config', 'mel', 'accounts.conf')
config = configparser.ConfigParser()
config.read(configPath)
# Reading config a bit
accounts = dict()
mails = set()
for name in config.sections():
if not name.islower():
continue
section = config[name]
mails.add(section["from"])
if "alternatives" in section:
for alt in section["alternatives"].split(";"):
mails.add(alt)
accounts[name] = section
log.debug("Loading database")
dbPath = os.path.realpath(os.path.expanduser(config["GENERAL"]["storage"]))
db = notmuch.Database(mode=notmuch.Database.MODE.READ_WRITE, path=dbPath)
# TODO Open read-only when needed
log.debug("Database loaded")
def get_location(msg):
path = msg.get_filename()
@ -61,6 +47,7 @@ def get_location(msg):
path = path[len(base):]
pathSplit = path.split('/')
mailbox = pathSplit[1]
assert mailbox in ACCOUNTS
state = pathSplit[-1]
folder = tuple(pathSplit[2:-1])
assert state in {'cur', 'tmp', 'new'}
@ -193,25 +180,14 @@ def retag_msg(msg):
tag_if('spam', slugFolder[0] == 'JUNK' or slugFolder[0] == 'SPAM')
tag_if('deleted', slugFolder[0] == 'TRASH')
tag_if('draft', slugFolder[0] == 'DRAFTS')
tag_if('sent', expeditor in mails)
tag_if('sent', expeditor in ALIASES)
# TODO remove unprocessed
# Save
msg.thaw()
msg.tags_to_maildir_flags()
def applyMsgs(queryStr, function, *args, useProgressbar=False, **kwargs):
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, *args, **kwargs)
def extract_email(field):
try:
sta = field.index('<')
@ -220,8 +196,25 @@ def extract_email(field):
except ValueError:
return field
def applyMsgs(queryStr, action, *args, showProgress=False, **kwargs):
log.info("Querying {}".format(queryStr))
query = notmuch.Query(db, queryStr)
query.set_sort(notmuch.Query.SORT.OLDEST_FIRST)
elements = query.search_messages()
if showProgress:
nbMsgs = query.count_messages()
iterator = progressbar.progressbar(elements, max_value=nbMsgs)
else:
iterator = elements
log.info("Executing {}".format(action))
for msg in iterator:
action(msg, *args, **kwargs)
# applyMsgs('*', print_msg)
applyMsgs('tag:inbox', print_msg)
# applyMsgs('tag:inbox', print_msg)
# applyMsgs('tag:spam', print_msg)
# applyMsgs('tag:unread', print_msg)
# applyMsgs('tag:unprocessed', print_msg)
@ -230,3 +223,115 @@ applyMsgs('tag:inbox', print_msg)
# applyMsgs('tag:unprocessed', retag_msg, useProgressbar=True)
# applyMsgs('*', retag_msg, useProgressbar=True)
if __name__ == "__main__":
# Main arguments
parser = argparse.ArgumentParser(description="Meh mail client")
selectedVerbosityLevels = ["DEBUG", "INFO", "WARNING", "ERROR", "FATAL"]
parser.add_argument('-v', '--verbosity', choices=selectedVerbosityLevels, default='WARNING', help="Verbosity of log messages")
# parser.add_argument('-n', '--dry-run', action='store_true', help="Don't do anything") # DEBUG
defaultConfigFile = os.path.join(xdg.BaseDirectory.xdg_config_home, 'mel', 'accounts.conf')
parser.add_argument('-c', '--config', default=defaultConfigFile, help="Accounts config file")
parser.set_defaults(dbmode=notmuch.Database.MODE.READ_ONLY)
parser.set_defaults(showProgress=False)
parser.set_defaults(useThreads=False)
parser.set_defaults(actionBefore=None)
parser.set_defaults(actionAfter=None)
parser.set_defaults(action=None)
subparsers = parser.add_subparsers(help="Action to execute", required=True)
## List messages
# inbox (default)
def func_inbox(args):
queryStr = 'tag:unread' if args.only_unread else 'tag:inbox'
applyMsgs(queryStr, print_msg)
parserInbox = subparsers.add_parser("inbox", help="Show unread, unsorted and flagged messages")
parserInbox.add_argument('-u', '--only-unread', action='store_true', help="Show unread messages only")
# TODO Make this more relevant
parserInbox.set_defaults(func=func_inbox)
# list folder [--recurse]
## List actions
# delete msg...
# spam msg...
# flag msg...
# ↑un* equivalents
# move dest msg...
## Read message
# read msg [--html] [--plain] [--browser]
# attach msg [id] [--save] (list if no id, xdg-open else)
## Redaction
# new account
# reply msg [--all]
## Folder management
# mkdir folder
# rmdir folder (prevent if folder isn't empty (mail/subfolder))
# (yeah that should do)
## Meta
# setup (interactive thing maybe)
# fetch (mbsync, notmuch new, retag, notify; called by greater gods)
def func_fetch(args):
# Fetch mails
log.info("Fetching mails")
mbsyncConfigPath = os.path.expanduser("~/.mbsyncrc") # TODO Better
cmd = ["mbsync", "--config", mbsyncConfigPath, "--all"]
subprocess.run(cmd)
# Index new mails
log.info("Indexing mails")
log.error("TODO Can't `notmuch new` when database is already open!")
notmuchConfigPath = os.path.expanduser("~/.notmuchrc") # TODO Better
cmd = ["notmuch", "--config", notmuchConfigPath, "new"]
log.debug(" ".join(cmd))
subprocess.run(cmd)
# Tag new mails
applyMsgs('tag:unprocessed', retag_msg, showProgress=True)
# Notify
log.info("Notifying new mails")
# TODO Maybe before retag, notify unprocessed && unread
parserFetch = subparsers.add_parser("fetch", help="Fetch mail, tag them, and run notifications")
parserFetch.set_defaults(dbmode=notmuch.Database.MODE.READ_WRITE)
parserFetch.set_defaults(func=func_fetch)
## Debug
# process (all or unprocessed)
args = parser.parse_args()
print(args)
# Installing logs
colorama.init()
coloredlogs.install(level=args.verbosity, fmt='%(levelname)s %(message)s')
log = logging.getLogger()
log.info("Loading config {}".format(args.config))
if not os.path.isfile(args.config):
log.fatal("Config file not found: {}".format(args.config))
sys.exit(1)
# TODO Create it, maybe?
config = configparser.ConfigParser()
config.read(args.config)
generate_aliases()
if args.dbmode is not None:
log.info("Loading database")
dbPath = os.path.realpath(os.path.expanduser(config["GENERAL"]["storage"]))
db = notmuch.Database(mode=args.dbmode, path=dbPath)
if args.func:
log.info("Executing function {}".format(args.func))
args.func(args)