This is rather niche, but I thought of sharing it anyway for those who use Fastmail. This script allows to open .eml files and message:// links with Fastmail - rather than Mail. The problem is only that Fastmail doesn’t show you the mail when you search based on Message-Id (it rather shows you a message list with the message found which you then have to click).
This code does the following
- Extract Message-ID from .eml file or message:// handler
- Get the Fastmail JMAP
id
for the “Message-ID” found - Open the URL Fastmail + jmap_id
Use keyring.set_password("jmap","johndoe@fastmail.com","s3cretpassw0rd")
to set your password.
Use Platypus to create a handler for .eml files and message:// links and set that as the default handler, so launching a .eml file from DT will automatically launch this script. As the script takes 3-4 seconds to produce a result, choose the “Progress bar” option in Platypus so you’ll get a nice loading bar during waiting to stare at
#!/usr/bin/env python3
import json
import os
import requests
import sys
import keyring
import urllib.parse
import email
class TinyJMAPClient:
"""The tiniest JMAP client you can imagine."""
def __init__(self, hostname, username, password):
"""Initialize using a hostname, username and password"""
assert len(hostname) > 0
assert len(username) > 0
assert len(password) > 0
self.hostname = hostname
self.username = username
self.password = password
self.session = None
self.api_url = None
self.account_id = None
def get_session(self):
"""Return the JMAP Session Resource as a Python dict"""
if self.session:
return self.session
r = requests.get(
"https://" + self.hostname + "/.well-known/jmap",
auth=(self.username, self.password),
)
r.raise_for_status()
self.session = session = r.json()
self.api_url = session["apiUrl"]
return session
def get_account_id(self):
"""Return the accountId for the account matching self.username"""
if self.account_id:
return self.account_id
session = self.get_session()
account_id = session["primaryAccounts"]["urn:ietf:params:jmap:mail"]
self.account_id = account_id
return account_id
def make_jmap_call(self, call):
"""Make a JMAP POST request to the API, returning the reponse as a
Python data structure."""
res = requests.post(
self.api_url,
auth=(self.username, self.password),
headers={"Content-Type": "application/json"},
data=json.dumps(call),
)
res.raise_for_status()
return res.json()
def main():
arg = sys.argv[1]
if arg.endswith(".eml"):
with(open(arg)) as eml:
msg = email.message_from_file(eml)
message_id = msg['message-id']
else:
if arg.startswith("message://"):
message_id = arg[10:]
else:
message_id = arg
message_id = urllib.parse.unquote(message_id)
username = "johndoe@fastmail.com"
client = TinyJMAPClient(hostname="jmap.fastmail.com",username=username,password=keyring.get_password("jmap",username))
account_id = client.get_account_id()
get_res = client.make_jmap_call(
{
"using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
"methodCalls": [
[
"Email/query",
{
"accountId": account_id,
"filter": {"header": ["Message-id", message_id] },
"limit": 1,
},
"a"
],
],
}
)
jmap_id = get_res["methodResponses"][0][1]["ids"][0]
os.system("open https://beta.fastmail.com/mail/show/" + jmap_id)
if __name__ == "__main__":
main()