mel2
This commit is contained in:
parent
18a7278422
commit
47cdc830a0
181
scripts/mel
181
scripts/mel
|
@ -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")
|
||||
|
||||
# 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()
|
||||
def generate_aliases():
|
||||
for name in config.sections():
|
||||
if not name.islower():
|
||||
continue
|
||||
section = config[name]
|
||||
mails.add(section["from"])
|
||||
ALIASES.add(section["from"])
|
||||
if "alternatives" in section:
|
||||
for alt in section["alternatives"].split(";"):
|
||||
mails.add(alt)
|
||||
accounts[name] = section
|
||||
ALIASES.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)
|
||||
|
||||
|
|
Loading…
Reference in a new issue