dotfiles/office/.local/bin/mutt2task
Marty Oehme aefce1c498
office: Integrate neomutt and taskwarrior
The beginning of what I hope can be a useful integration: send mails to
taskwarrior as tasks and open the corresponding mail from tasks in
taskwarrior.

To make a task out of an e-mail, in neomutt, simply press `t` when the
mail is selected or opened. It will create an automatic task in
taskwarrior with the description "Reply to [mail] by [sender]" and tag
it as mail.
If you press `T` instead, you can give the task your own description and
tags.

In taskwarrior, you can simply `t open <taskid>` on a task that came
from neomutt to show the message content on the command line (using
notmuch). This is still a bit rudimentary and I would like an improved
display, but it works for now.
2022-12-08 21:31:15 +01:00

100 lines
3.1 KiB
Python
Executable file

#!/usr/bin/env python
"""
Creates a new task with corresponding description and tags.
Description and tags can be supplied via commandline through `-d` and `-t` options,
or interactively by using the options without supplying a string for them. E.g.:
`mutt2task` => task: Reply to <subject> from <sender>
`mutt2task -d "hello" -t one two` => task: hello +one +two
`mutt2task -d -t` => asks you interactively for description and tags
"""
import argparse
import email
import sys
import re
from tasklib import TaskWarrior, Task
def get_args():
parser = argparse.ArgumentParser(
description="Creates a task with given message and annotates the task with message id"
)
parser.add_argument(
"-d",
"--description",
nargs="?",
const=True,
default=None,
help="Supply a description for the task. Use the option as a flag to supply description interactively.",
)
parser.add_argument(
"-t",
"--tags",
nargs="*",
help="Supply any number of tags for the task. Use the option as a flag to supply tags interactively.",
)
parser.add_argument(
"-c",
"--clean",
action="store_true",
help="Remove subject annoyances like RE FWD on a best-effort basis.",
)
return parser.parse_args()
args = get_args()
def clean_subject(subject):
to_remove = ["fwd", "re", "fw", "aw", "wg", "sv", "vs", "ref", "tr"]
for abbrev in to_remove:
patt = re.compile(r"^{}(?: |:)\s*".format(abbrev), re.IGNORECASE)
subject = patt.sub("", subject)
return subject
def get_task_description(message):
subject = clean_subject(message["subject"]) if args.clean else message["subject"]
description = 'Reply to "{}" from {}'.format(
subject, message["from"].split(" <")[0]
)
# automatically set description if one provided
if args.description and isinstance(args.description, str):
description = args.description
# otherwise let user enter one if option has been passed
elif args.description:
inp = input("Enter task description or press enter for default: ")
description = inp if inp else description
return description
def get_task_tags():
tags = ["mail"]
if args.tags and len(args.tags) > 0:
tags = sum([tag.split(" ") for tag in args.tags], [])
elif args.tags is not None and len(args.tags) == 0:
inp = input("Enter task tags or press enter for default: ")
tags = inp.split(" ") if inp else tags
return tags
def main():
try:
message = email.message_from_file(sys.stdin)
if not message:
print("Something went wrong, cannot parse mail.")
sys.exit(1)
tw = TaskWarrior("~/.local/share/task", create=False)
sys.stdin = open("/dev/tty") # make user able to add input again
task = Task(tw, description=get_task_description(message), tags=get_task_tags())
task.save()
task.add_annotation(message['message-id'])
except Exception as e:
print(e)
sys.exit(2)
if __name__ == "__main__":
main()