Lou Plummer beschreibt in seinem Artikel „How to Send an Email to Your Obsidian Vault”, wie er mit Hilfe von IFTTT, Dropbox und Hazel E‑Mails an sein Obsidian Vault „sendet“. Dazu nutzt er eine spezielle Mailbox in IFTTT, in der eingehende Mails einen Workflow auslösen, der den Inhalt der Mail in der persönlichen Dropbox als Textdatei speichert. Von dort werden die E‑Mails dann, in einem nächsten Schritt, lokal von Hazel nach Obsidian kopiert.
Der Artikel inspirierte mich dazu, eine eigene Lösung zu entwickeln. E‑Mails bieten eine praktische Möglichkeit, Notizen zu erfassen, insbesondere wenn z.B. am Arbeitsplatz der Zugang zum persönlichen Obsidian Vault nicht verfügbar ist. Ich habe mir schon vor Jahren einen speziellen E‑Mail-Account erstellt, den ich zum Übermitteln von Links, Notizen und Erinnerungen nutze. Lous Artikel brachte mich nun auf die Idee, diesen Mail-Account direkt mit meinem Obsidian Vault zu koppeln. Zusätzlich erschien mir dieser Workflow auch als eine gute Herausforderung, um meine Python-Kenntnisse zu erweitern.
.
Hier sind die Anforderungen so einen solchen Workflow:
- Er soll lokal auf meinem Hauptcomputer laufen, in meinem Fall auf meinem MacBook.
- Er soll die E‑Mails des speziellen Mail-Accounts direkt herunterladen und in Obsidian als Markdown-Dateien speichern.
- Es sollen nur die ungelesenen E‑Mails heruntergeladen werden, die anschließend den Status „gelesen“ erhalten.
- Die Konfiguration, also die Zugangsdaten zum Mail-Server und der Pfad zum Obsidian-Ordner, soll in einer externen Konfigurationsdatei gespeichert werden.
Warnhinweis: Beim automatischen Konvertieren einer E‑Mail in eine Markdown-Datei, die dann in Obsidian aufgerufen wird, besteht theoretisch die Möglichkeit, dass Code ausgeführt wird. Nach meinem aktuellen Kenntnisstand ist dieses Risiko jedoch gering. Ich bin offen für Hinweise, falls jemand andere Erfahrungen gemacht hat. Zudem möchte man vermeiden, Spam in seinem Obsidian Vault zu speichern. Für meinen speziellen E‑Mail-Account ist dies unwahrscheinlich, da er nur mir bekannt ist und bisher keinen Spam erhalten hat. Dennoch könnten Filter implementiert werden, um nur E‑Mails von bestimmten Absendern oder mit bestimmten Schlüsselwörtern im Betreff nach Obsidian weiterzuleiten. Derzeit verzichte ich jedoch auf solche Maßnahmen.
Vorbereitung des Projekt-Setups
Da meine Anforderungen den Einsatz externer Python-Bibliotheken erfordern, empfiehlt es sich, das Skript in einer virtuellen Python-Umgebung zu erstellen und auszuführen.
Der erste Schritt besteht darin, einen Projektordner zu erstellen:
mkdir /Users/leif/Documents/Projects/mail2obsidian cd /Users/leif/Documents/Projects/mail2obsidian
Anschließend wird die virtuelle Umgebung erstellt und aktiviert:
python3 -m venv env source env/bin/activate
Nach der Aktivierung der Umgebung ändert sich der Eingabe-Prompt im Terminal, um anzuzeigen, dass die Umgebung aktiv ist, z.B. so:
(env) > $
Einrichten der benötigten Bibliotheken
Zunächst benötige ich eine Bibliothek, die den Zugriff auf den E‑Mail-Account ermöglicht. Nach eingehender Recherche habe ich mich für IMAPClient entschieden. Zum Lesen der externen Konfigurationsdatei verwende ich PyYAML, und für die Konvertierung der E‑Mails nutze ich markdownify. Alle diese Bibliotheken werden in die aktive Python-Umgebung installiert:
pip install imapclient pyyaml markdownify
Erstellen der Konfigurationsdatei
Ich habe mich für eine externe Konfigurationsdatei entschieden, um sicherzustellen, dass beim Veröffentlichen des Source-Codes keine privaten Daten im Code enthalten sind. Zudem ist es praktisch, diese Daten an einem zentralen Ort abzulegen, der leicht an neue Anforderungen angepasst werden kann.
Derzeit werden in der Datei die Zugangsdaten zum Mail-Server und der Dateipfad zum Ordner in Obsidian, in dem die Mails gespeichert werden, abgelegt. Als Datenstruktur habe ich das sogenanntes „geschachteltes Dictionary” gewählt, mit zwei Hauptschlüsseln, denen die eigentlichen Schlüssel-Wert-Paare folgen. Diese Datenstruktur lässt sich mit der PyYAML
-Bibliothek leicht verarbeiten und in die Python-Datenstruktur umsetzen.
email: server: "imap.example.com" user: "your-email@example.com" password: "your-password" output: path: "/Users/username/Documents/ObsidianVault/98 emails"
Für Windows oder Linux muss der Dateipfad entsprechend angepasst werden, damit das Skript auch auf diesen Plattformen funktioniert.
Vorstellung des mail2obsidian .py Scriptes
Da das Abrufen der E‑Mails vom Server mit der Bibliothek meine Fähigkeiten überstieg, habe ich den Code zusammen mit ChatGPT entwickelt. Da ich dabei auch etwas lernen wollte, ließ ich mir die einzelnen Stellen ausführlich erklären. Diese Erklärungen, die mehr Iterationen als die Code-Entwicklung benötigten, fasse ich an dieser Stelle mit eigenen Worten zusammen. Wem dies etwas zu langatmig ist, kann direkt zum gesamten Skript-Code springen.
1. Laden der Konfiguration aus einer YAML-Datei
with open('config.yaml', 'r') as file: config = yaml.safe_load(file) EMAIL = config['email']['user'] PASSWORD = config['email']['password'] IMAP_SERVER = config['email']['server'] OUTPUT_PATH = config['output']['path']
Wie erwähnt, ist die Dateistruktur der Konfigurationsdatei ein „geschachteltes Dictionary”, das es auch in der Programmiersprache Python gibt. In den ersten zwei Zeilen wird die Datei eingelesen und die Daten mit dem Befehl yaml.safe_load(file)
in der Variablen config
gespeichert, wobei die Datenstruktur erhalten bleibt.
Der Zugriff auf die Werte erfolgt dann über den Hauptschlüssel, email
oder output
und anschließend über den jeweiligen Schlüssel.
Mit dem with
-Statement wird ein Kontextmanager erstellt, der sicherstellt, dass die Datei nach dem Verlassen des Code-Blocks ordnungsgemäß geschlossen wird. Der Kontextmanager übernimmt die Verwaltung von Ressourcen, wie das Öffnen und Schließen von Dateien, und sorgt dafür, dass diese korrekt freigegeben werden, auch wenn innerhalb des Blocks eine Ausnahme auftritt.
2. Sicherstellen, dass der Ausgabeordner existiert
os.makedirs(OUTPUT_PATH, exist_ok=True)
Dieser Aufruf stellt sicher, dass der Pfad für das Speichern der Markdown-Datei existiert. Dabei wird versucht, das Verzeichnis zu erstellen. Normalerweise würde der Befehl einen Fehler melden, falls das Verzeichnis bereits vorhanden ist. Mit dem Zusatz exist_ok=True
wird die Fehlermeldung jedoch unterdrückt, und das vorhandene Verzeichnis bleibt unverändert.
3. Verbindung zum E‑Mail-Server herstellen
with imapclient.IMAPClient(IMAP_SERVER) as server: server.login(EMAIL, PASSWORD) server.select_folder('INBOX')
In diesem Block wird die Verbindung zum Mail-Server aufgebaut und der Fokus auf die INBOX gesetzt. Falls der Server ein anderes Postfach für neue Mails nutzt, muss der Name entsprechend angepasst werden, was in der Regel kein Problem darstellt.
Auch hier wird mit einem with
-Statement ein Kontextmanager erstellt, der die Ressourcenverwaltung übernimmt. Er sorgt dafür, dass die Verbindung zum Mail-Server während der folgenden Verarbeitungsschritte aufrechterhalten bleibt.
4. Ungelesene E‑Mails suchen
messages = server.search(['UNSEEN'])
Mit diesem Aufruf wird eine Liste aller IDs (UIDs) der ungelesenen E‑Mails aus der INBOX erstellt. Diese Liste wird im nächsten Schritt verwendet, um die E‑Mails nacheinander zu verarbeiten.
5. Schleife für die Verarbeitung der E‑Mails
for uid, message_data in server.fetch(messages, 'RFC822').items():
Um die for
-Schleife zu verstehen, benötigte ich etwas länger, da ich bisher nicht mit einem solchen Konstrukt gearbeitet hatte. Die Iteration in der Schleife wird nicht explizit in der for
-Schleife definiert, sondern ergibt sich aus dem Ergebnis des Aufrufs:
server.fetch(messages, 'RFC822')
Die Variable messages
enthält die Liste der IDs der ungelesenen E‑Mails, z.B.:
messages = [101, 102, 103] # IDs ungelesener E-Mail-Server
Der Aufruf server.fetch(messages, 'RFC822')
ruft für jede ID in messages
die E‑Mail-Daten ab, wobei das Format ‘RFC822’ verlangt wird, und gibt diese als Dictionary zurück:
{ 101: {b'RFC822': b'Raw Email Data for UID 101'}, 102: {b'RFC822': b'Raw Email Data for UID 102'}, 103: {b'RFC822': b'Raw Email Data for UID 103'} }
Hierbei ist die ID 101 der Schlüssel zur ersten ungelesenen E‑Mail, und der Wert {b’RFC822’: b’Raw Email Data for UID 101′} repräsentiert den message_body
im RAW-Format. Dieser ist nicht so klar lesbar wie im Beispiel und kann kodiert sein, z.B. mit Base64 besteht aus mindestens zwei Bereichen, dem E‑Mail-Header Daten und dem E‑Mail-Body.
Das .items()
am Ende des Aufrufs erzeugt eine Abfolge von Schlüssel-Wert-Paaren, durch die die for
-Schleife iteriert. Dabei wird die ID in der Variablen uid
und der E‑Mail-Body in message_data
für die weitere Verarbeitung gespeichert.
6. Umwandeln der E‑Mail-Daten in ein lesbares Format
email_message = email.message_from_bytes(message_data[b'RFC822'])
In der Schleife wird die E‑Mail in der Variablen email_message
gespeichert. Die Methode message_from_bytes(message_data[b'RFC822'])
wandelt die rohen E‑Mail-Daten in ein Python-E-Mail-Objekt um. Dieses Objekt bietet eine klare Struktur, mit der das Skript einfach auf die benötigten Informationen zugreifen kann. Diese Struktur besteht aus den folgenden Bereichen:
Header-Bereich:
Der Header-Bereich besteht aus Schlüssel-Wert-Paaren. In vielen E‑Mail-Programmen kann man sich diese Header-Daten anzeigen lassen, z.B. auf dem Mac im Mail-Programm unter dem Menüpunkt Darstellung → E-Mail → Alle Header
. In dem Skript werde ich nur, den Betreff, den Absender und das Datum auswerten:
Subject
(Betreff)From
(Absender)To
(Empfänger)Date
(Datum der E‑Mail)- Weitere Felder wie
CC
,BCC
,Reply-To
Auf diese Felder kann relativ einfach zugegriffen werden, da sie in einem E‑Mail-Objekt strukturiert vorliegen. Allerdings ist es oft notwendig, die Felder zu dekodieren, da E‑Mails nicht nur druckbare ASCII-Zeichen enthalten.
Payload (Inhalt):
Die Payload enthält den eigentlichen Inhalt der E‑Mail. Sie kann aus einem oder mehreren „Parts” bestehen, die wiederum dekodiert sein können:
- Text/plain: Der Klartext-Inhalt.
- Text/html: HTML-Version der Nachricht.
- Anhänge: Dateien, Bilder oder andere eingebettete Inhalte, die oft kodiert sind, z.B. mit Base64, um sie korrekt in das E‑Mail-Format einzubinden
7. Verarbeitung des Header-Bereichs
subject, encoding = decode_header(email_message['Subject'])[0] if isinstance(subject, bytes): subject = subject.decode(encoding if encoding else 'utf-8')
Im ersten Schritt wird aus dem Header der Betreff, also das Subject
, extrahiert. Aus dem Betreff soll später der Dateiname erstellt werden. Wie bereits erwähnt, können der Header und das Subject kodiert sein, daher werden sie zunächst in UTF‑8 dekodiert. Das Ergebnis wird in der Variablen subject
gespeichert.
Zusätzlich werden der Absender der E‑Mail und das Datum für die Notiz-Properties ausgelesen und in den Variablen sender
und send_date
gespeichert.
sender = email_message.get('From', 'Unknown sender') date_header = email_message.get('Date', 'Unknown date') try: send_date = parsedate_to_datetime(date_header).strftime('%d.%m.%Y %H:%M:%S') if date_header != 'Unknown date' else date_header except Exception: send_date = "Invalid date"
Das Datum wird dabei in ein bei uns gebräuchliches Format umgewandelt. Falls diese Daten nicht ausgelesen werden können, wird ein Standardwert in den Variablen gespeichert.
8. Erstellen eines sicheren Dateinamens
filename = f"{subject}.md" filename = ( filename.replace('/', '_') .replace('\\', '_') .replace(':', '_') .replace('*', '_') .replace('?', '_') .replace('"', '_') .replace('<', '_') .replace('>', '_') .replace('|', '_') )
Da im Betreff einer E‑Mail Zeichen vorkommen können, die in Dateinamen problematisch sind, werden diese Zeichen in der Variablen filename
durch ein _
ersetzt, nachdem ihr der Wert von subject
zugeordnet wurde.
Ich benutze bei der Zuordnung zur Variablen filename
einen f‑String, der es mir ermöglicht, die Variable subject
direkt in geschweifte Klammern {}
einzufügen, um den Dateinamen zu erstellen.
Zum Ersetzen der kritischen Zeichen verwende ich ein Konstrukt namens Method Chaining, das es mir ermöglicht, die replace
-Methode mit den unterschiedlichen Zeichen hintereinander anzuwenden.
9. Initialisierung des Notiz-Variablen
# Initialize email content with YAML frontmatter email_content = f"""--- subject: "{subject}" from: "{sender}" send_date: "{send_date}" --- """
Die Variable email_content
wird genutzt, um den Inhalt der Notiz zusammenzubauen. Dabei wird die Variable bei der Initialisierung mit den Property-Daten belegt. Hierzu wird, wie schon beim Notiznamen, ein f‑string genutzt, der die Einbettung von Python-Ausdrücken ermöglicht, deren Ergebnisse direkt in den String eingesetzt werden. Der Frontmatter-Block wird jeweils von ---
umschlossen.
10. Verarbeitung der E‑Mail-Payload
# Extract email body for part in email_message.walk(): if part.get_content_type() == "text/html": # Found HTML content; convert to Markdown and stop searching charset = part.get_content_charset() or 'utf-8' payload = part.get_payload(decode=True) if payload: html_content = payload.decode(charset, errors='replace') email_content += md(html_content) # Convert HTML to Markdown break # Prioritize HTML, so we stop here elif part.get_content_type() == "text/plain" and email_content.strip() == f"---\n{subject}\n---": # Only use if no HTML was found charset = part.get_content_charset() or 'utf-8' payload = part.get_payload(decode=True) if payload: email_content += payload.decode(charset, errors='replace') # If email_content is still empty, add a default message if email_content.strip() == f"---\n{subject}\n---": email_content += "No content available."
Wie oben erwähnt, befindet sich der Inhalt der Mail in der sogenannten Payload und muss von dort extrahiert werden. Da diese Payload aus mehreren Teilen (Parts) besteht, durchläuft diese Schleife die einzelnen Teile. Der erste if
-Block if part.get_content_type() == "text/html":
überprüft, ob der aktuelle Teil HTML-Inhalt ist. Wenn dies der Fall ist, wird mit der Methode get_content_charset()
im Ausdruck charset = part.get_content_charset() or 'utf-8'
versucht, die Kodierung des Inhalts zu ermitteln. Wenn keine Kodierung ermittelt werden kann, wird standardmäßig utf-8
verwendet. Mit payload = part.get_payload(decode=True)
wird nun der eigentliche Inhalt dekodiert. Durch das Setzen von decode=True
wird sichergestellt, dass der Inhalt als Rohbytes zurückgegeben wird, nachdem etwaige Transferkodierungen wie base64
entfernt worden sind. Damit ist dieser Inhalt jedoch immer noch kein lesbarer String, sondern ein sogenannter Byte-String. Ein Beispiel für so einen Byte-String wäre b"M\xc3\xbcller Stra\xc3\x9fe"
, der utf-8
dekodiert Müller Straße
ergibt.
Bevor die Konvertierung dieses Byte-Strings des HTML-Inhalts in Markdown erfolgt, wird zunächst sicherheitshalber geprüft, ob der Inhalt tatsächlich dekodiert wurde (payload
ist nicht None
oder leer). Auf diese Weise wird vermieden, dass weitere Verarbeitungsschritte auf einem nicht vorhandenen oder leeren Inhalt stattfinden, was zu Fehlern führen könnte.
11. Konvertierung und Speichern des HTML-Inhaltes
html_content = payload.decode(charset, errors='replace') email_content += md(html_content) # Convert HTML to Markdown break # Prioritize HTML, so we stop here
In der ersten Zeile wird der Inhalt nun endgültig in lesbares HTML konvertiert, wobei eventuelle nicht in utf-8
vorhandene Zeichen (errors='replace'
) durch ein Ersatzzeichen wie �
ersetzt werden. Dies stellt sicher, dass der Dekodierungsvorgang nicht abbricht und der String weiterhin verarbeitet werden kann, auch wenn einige Zeichen nicht korrekt dargestellt werden.
Mit email_content += md(html_content)
wird der HTML-Inhalt in Markdown konvertiert und an die Variable email_content
hinter den bereits zuvor gespeicherten Frontmatter angehängt (+=
).
Das anschließende break
beendet die Schleife. Damit wird der elif
-Teil der Bedingung nur verarbeitet, wenn die Mail keinen HTML-Part enthält.
12. Text-Inhalt verarbeiten und Speichern
elif part.get_content_type() == "text/plain" and email_content.strip() == f"---\n{subject}\n---": # Only use if no HTML was found charset = part.get_content_charset() or 'utf-8' payload = part.get_payload(decode=True) if payload: email_content += payload.decode(charset, errors='replace')
Dieser Teil der Bedingung wird nur verarbeitet, wenn kein HTML-Part gefunden wurde und an die Variable email_content
angehängt werden soll. Daher wird geprüft, ob die Variable email_content
ausschließlich den Inhalt des Property-Bereichs der Notiz enthält und ob es einen Text-Part (text/plain
) gibt. Die strip
-Methode entfernt dabei alle vorangestellten und nachgestellten Leerzeichen oder Zeilenumbrüche, um eine genaue Übereinstimmung zu gewährleisten.
Die Verarbeitung des Textes erfolgt dann ähnlich wie beim HTML-Part, nur dass die dekodierte Payload direkt, also ohne Konvertierung, an die Variable email_content
angehängt wird.
Abschluss
if email_content.strip() == f"---\n{subject}\n---": email_content += "No content available." # Save the email content to a Markdown file file_path = os.path.join(OUTPUT_PATH, filename) with open(file_path, 'w', encoding='utf-8') as f: f.write(email_content) # Mark the email as read server.add_flags(uid, '\\Seen') print("Unread emails have been converted to Markdown files.")
‘
Am Ende des Skriptes wird noch einmal geprüft, ob an die Variable email_content
überhaupt E‑Mail-Inhalt angehängt wurde. Wenn nicht, wird ein Hinweis, dass kein E‑Mail-Inhalt gelesen werden konnte, zu den Property-Daten in die Variable hinzugefügt.
Danach wird der Dateipfad zum Obsidian Vault zusammen mit dem Dateinamen der Notiz in die Variable file_path
geschrieben. Anschließend wird diese Datei erstellt und der Inhalt von email_content
hineingeschrieben. Am Ende wird die Mail auf dem Server mit dem Flag \\Seen
, also als gelesen, versehen, womit die Schleife mit der Verarbeitung einer Mail aus der Liste messages
beendet ist. Diese Schleife wird so oft durchlaufen, bis die Liste abgearbeitet ist.
Am Ende wird eine Meldung in die Konsole geschrieben.
Der gesamte Skript-Code
import imapclient import email from email.header import decode_header from email.utils import parsedate_to_datetime import os from markdownify import markdownify as md import yaml # Lädt die Konfiguration aus der YAML Datei with open('config.yaml', 'r') as file: config = yaml.safe_load(file) EMAIL = config['email']['user'] PASSWORD = config['email']['password'] IMAP_SERVER = config['email']['server'] OUTPUT_PATH = config['output']['path'] # Sicherstellen, dass der Ausgabeordner existiert os.makedirs(OUTPUT_PATH, exist_ok=True) # Verbindung zum E-Mail-Server herstellen with imapclient.IMAPClient(IMAP_SERVER) as server: server.login(EMAIL, PASSWORD) server.select_folder('INBOX') # Ungelesene E-Mails suchen messages = server.search(['UNSEEN']) # E-Mails abrufen und verarbeiten for uid, message_data in server.fetch(messages, 'RFC822').items(): email_message = email.message_from_bytes(message_data[b'RFC822']) # E-Mail-Betreff dekodieren subject, encoding = decode_header(email_message['Subject'])[0] if isinstance(subject, bytes): subject = subject.decode(encoding if encoding else 'utf-8') # Extrahiert und decodiert subject, send-date und sender sender = email_message.get('From', 'Unknown sender') date_header = email_message.get('Date', 'Unknown date') try: send_date = parsedate_to_datetime(date_header).strftime('%d.%m.%Y %H:%M:%S') if date_header != 'Unknown date' else date_header except Exception: send_date = "Invalid date" # Sicheren Dateinamen erstellen filename = f"{subject}.md" filename = ( filename.replace('/', '_') .replace('\\', '_') .replace(':', '_') .replace('*', '_') .replace('?', '_') .replace('"', '_') .replace('<', '_') .replace('>', '_') .replace('|', '_') ) # Initialisiert Variable für den E-Mail Inhalt mit Frontmatter YAML email_content = f"""--- subject: "{subject}" from: "{sender}" send_date: "{send_date}" --- """ # E-Mail-Text extrahieren for part in email_message.walk(): if part.get_content_type() == "text/html": # Bei gefundenen HTML-Part; Konvertiert zu Markdown and stoppt da Suchen nach weiteren Parts charset = part.get_content_charset() or 'utf-8' payload = part.get_payload(decode=True) if payload: html_content = payload.decode(charset, errors='replace') email_content += md(html_content) # Konvertiert das HTML to Markdown break # Wenn HTML-Inhalt gefunden wird, wird die Schleife verlassen, dadurch wird HTML-Inhalt prioriziert elif part.get_content_type() == "text/plain" and email_content.strip() == f"---\n{subject}\n---": # Wird nur drucchlaufen, wenn kein HTML-Part gefunden wurde charset = part.get_content_charset() or 'utf-8' payload = part.get_payload(decode=True) if payload: email_content += payload.decode(charset, errors='replace') # Fall weder HTML noch Text gefunden wird, wird ein Hinweis in die Variabel geschrieben if email_content.strip() == f"---\n{subject}\n---": email_content += "No content available." # Mail-Inhalt wird in die Notiz-Datei geschrieben und am definierten Ort gespeichert. file_path = os.path.join(OUTPUT_PATH, filename) with open(file_path, 'w', encoding='utf-8') as f: f.write(email_content) # Markiert die verarbeitete Mail als gelesen server.add_flags(uid, '\\Seen') print("Unread emails have been converted to Markdown files.")
Zusammenfassung und Ausblick
Dieses Skript ist als Ausgangspunkt für weitere Ideen gedacht und ist sicherlich nicht “fertig”. Ich habe bereits eine erste funktionierende Version als Reaktion auf Lous Beitrag vor einigen Wochen auf meinem englischen Blog veröffentlicht, in der beispielsweise noch nicht die Metadaten in das Frontmatter geschrieben wurden. Durch das Schreiben an diesem Beitrag habe ich auch wiederum weitere Ideen für Erweiterungen bekommen.
Derzeit ist das Skript so konzipiert, dass es manuell aufgerufen wird. Das macht für mich auch Sinn, da ich mir nicht ständig E‑Mails schicke und den Workflow bei Bedarf dann einfach manuell aufrufe. Die Ausführung des Skriptes kann jedoch auch automatisiert erfolgen. Auf dem Mac wäre der launchd
-Service der geeignete Ort, um dieses Skript automatisch, z.B. bei jedem Login, zu starten.
Eine weitere Idee, die ich bereits im Text erwähnt habe, besteht darin, nur E‑Mails von bestimmten Absendern oder mit definierten Stichwörtern im Betreff in den Obsidian Vault zu übernehmen. Mit diesem Ansatz könnte auch ein E‑Mail-Account integriert werden, der für andere Zwecke genutzt wird.
Momentan werden Bilder und andere Anhänge ignoriert. Auch die Konvertierung des HTML-Parts in Markdown ist nicht ganz optimal, insbesondere wenn man beispielsweise Teile von einer Webseite in die E‑Mail kopiert. Es könnte sich lohnen, die Konvertierung zu verbessern, möglicherweise durch die Verwendung einer anderen Konvertierungsbibliothek, um bessere Ergebnisse zu erzielen.
Es gibt also noch viel Potenzial, diesen Workflow zu erweitern und zu verbessern. Ich würde mich freuen, wenn diese Idee aufgegriffen und weiterentwickelt wird. Ideen und Hinweise sind gerne in den Kommentaren willkommen.
Schreibe einen Kommentar