Product
Eingehende E-Mails wie ein Profi verarbeiten [Mailgun-API 2.0]
So weit zurückliegend wirkt unser Blog wie ein altes Familienfotoalbum. Dieser Beitrag erschien ursprünglich bereits im Jahr 2011.
Früher war der E-Mail-Versand schwierig, aber mit der Mailgun E-Mail-Versand-API haben wir SMTP und MIME hinter uns gelassen und den Vorgang zu einer trivialen und geradezu angenehmen Erfahrung gemacht.

Wenn Ihre Anwendung jedoch E-Mail-Konversationen führen muss, wie es bei Enterprise-Apps oft der Fall ist, ist der Versand nicht einmal die halbe Miete. Angenommen, Sie möchten einen E-Mail-basierten Bot entwickeln, der sich in den Workflow Ihrer Enterprise-App integrieren lässt. Bedenken Sie die Hindernisse, die Sie überwinden müssten:
Transportprobleme. Die eingehenden Nachrichten müssen Ihre App auf irgendeinem Weg erreichen. Das bedeutet, dass irgendwo ein MX-Eintrag vorhanden sein muss, der auf einen E-Mail-Server verweist, welcher in der Lage ist, eingehende E-Mails an Ihren Code weiterzuleiten. Dabei gibt es viele Stolpersteine. Einer davon ist, dass Sie wahrscheinlich korrekte Bounces für ein „ungültiges Postfach“ generieren möchten, um Absendende zu benachrichtigen, falls diese sich bei der Adresse vertippt haben.
Probleme bei der Spam-Verarbeitung. Sobald Sie E-Mails für eine bestimmte MX-Domain akzeptieren, müssen Sie sich darüber im Klaren sein, dass der Großteil der eingehenden E-Mails früher oder später Spam sein wird. Und selbst wenn Ihre E-Mail-Annahmerichtlinie Allowlist-basiert ist, ist es nicht immer wünschenswert, dass sich Ihre App mit Spam-Angriffen auseinandersetzen muss.
Probleme beim MIME-Parsing. Wir haben dies bereits angesprochen, aber das Parsen von MIME ist in den meisten Programmiersprachen eine mühsame Aufgabe. Bibliotheken für das MIME-Parsing sind nicht zahlreich und viele von ihnen weisen eine geringe Toleranz gegenüber realem Datenverkehr auf.
Nachrichteninhalt. Ihre Probleme beim Parsen gehen weit über MIME hinaus. Die meisten eingehenden E-Mails sind in der Regel Antworten auf zuvor gesendete Nachrichten, und Sie möchten meistens die riesigen zitierten Teile entfernen. Oft ist es zudem erwünscht, die Signatur einer Person aus dem Nachrichtentext zu extrahieren.
Das wirkt ziemlich abschreckend für etwas so Einfaches, wie einen Textausschnitt in eine Anwendung zu integrieren. Wäre es nicht schön, etwas Ähnliches wie Sinatra oder Flask zu haben und in Python einfach schreiben zu können:
@email_in(".*@myapp.com")rn def incoming_message(message_obj):rn # access various parts of the fully parsed incoming message:rn message_obj.bodyrn message_obj.body_without_quoted_textrn message_obj.sender_signaturern # stuff that matters:rn make_profit_from(message_obj)
Die Ruby-Community liebt Blöcke. Die Ruby-Community würde sicher gerne Folgendes schreiben:
email_in ".*@myapp.com" do |message_obj|rn # sweet profit-making code...rn end
Die Idee hinter diesen Codebeispielen ähnelt dem Routing-Mechanismus moderner MVC-Frameworks. Anstatt jedoch URLs mit Controller-Aktionen abzugleichen, möchten wir Routen definieren, die ein Empfängeradressen-Muster einer Funktion in Ihrem Code zuordnen.
Doch wie gehen Sie bei der Implementierung von email_in() angesichts der oben aufgeführten Schwierigkeiten vor?
Mailgun Routes als Lösung
Wir haben diese speziell für diesen Anwendungsfall entwickelt. Sie bieten bei Weitem den angenehmsten Weg, eine App für bidirektionales E-Mail-Messaging zu erstellen. Vertrauen Sie uns, wir sind E-Mail-Experten. Aber genug der Worte, lassen Sie uns zur Tat schreiten.
Definieren wir zunächst eine neue Route im Mailgun-Control-Panel. Diese Route gleicht die Empfangsadresse mit dem regulären Ausdruck „.*@myapp.com“ ab. Bei einer Übereinstimmung führt sie zwei Dinge aus:
- Sie parst die Nachricht und sendet sie per POST an die URL „/emailin“ Ihrer App.
- Zudem leitet sie eine Kopie der Nachricht an ein Entwicklerpostfach weiter, beispielsweise developer@myapp.com

Nun können wir Nachrichten an @myapp.com verarbeiten, indem wir den folgenden Code in die Web-App einfügen:
@app.route("/mailin", methods=["POST"]) rndef mailin(): rn # see if the message is spam:rn is_spam = request.form['X-Mailgun-SFlag'] == 'Yes'rnrn # access some of the email parsed values:rn request.form['From']rn request.form['To']rn request.form['subject']rnrn # stripped text does not include the original (quoted) message, only whatrn # a user has typed:rn request.form['stripped-text']rn request.form['stripped-signature']rnrn # enumerate through all attachments in the message and savern # them to disk with their original filenames:rn for attachment in request.files.values():rn attachment.filenamern data = attachment.stream.read()rn with open(attachment.filename, "w") as f:rn f.write(data)
Eingehende E-Mails wie ein Profi verarbeiten [Mailgun-API 2.0]
Senden wir mit Gmail eine Testnachricht an unsere Web-App und sehen uns an, was per POST übermittelt wird.
Hier ist der Screenshot der Nachricht, wie sie in GMail angezeigt wird. Beachten Sie, dass sie den eigentlichen Textkörper des Geschriebenen (stripped-text), die Signatur und den zitierten Teil enthält, welcher in den meisten Fällen irrelevant ist:

Die per POST gesendeten Daten werden unten als ausgegebener Python-MultiDict-Request dargestellt. Beachten Sie, dass Mailgun zusätzlich zu künstlichen Parametern wie „recipient“ oder „stripped-text“ auch alle MIME-Kopfzeilen per POST an die App sendet, sodass Sie vollen Zugriff auf alles haben:
POST-Parameter (im Protokoll als Python-MultiDict ausgegeben)
('From', u'Ev Kontsevoy '),rn('sender', u'ev@mailgunhq.com'),rn('To', u'Awesome Bot '),rn('attachment-count', u'1'),rn('Subject', u'Re: Your application')])rn('stripped-text', u'My application is attached.nThanks.'),rn('stripped-html', u'HTML version of stripped-text'),rn('body-html', u'[full html version of the message]'),rn('body-plain', u'[full text version of the message]'),rn('stripped-signature', u'-- nEv Kontsevoy,nCo-founder and CEO of Mailgun.net - the emailnplatform for developers.'),rn('recipient', u'bot@hello.mailgun.org'),rn('subject', u'Re: Your application'),rn('timestamp', u'1320695889'),rn('signature', u'b8869291bd72f1ad38238429c370cb13a109eab01681a31b1f4a2751df1e3379'),rn('token', u'9ysf1gfmskxxsp1zqwpwrqf2qd4ctdmi5e$k-ajx$x0h846u88'),rn('In-Reply-To', u'Message-Id-of-original-message'),rn('Date', u'Mon, 7 Nov 2011 11:58:06 -0800'),rn('Message-Id', u'message-id-goes-here'),rn('X-Originating-Ip', u'[216.156.80.78]'),rnrn# NOTE: ALL message fields are parsed and pasted. If some fields (like "Cc") arern# missing here it only means they were absent from the message.
Per POST gesendete Dateien:
('attachment-1', FileStorage: 'application.pdg')
Fassen wir zusammen. Was haben wir hier?
- Eingehender E-Mail-Datenverkehr wird mit einem regulären Ausdruck abgeglichen, der auf die Empfangsadresse der Nachricht angewendet wird.
- Übereinstimmende Nachrichten werden geparst, auf Spam geprüft und per HTTP POST an die URL Ihrer Anwendung gesendet.
- Zitierte Teile der Nachricht werden abgetrennt (stripped), die Signatur wird erkannt.
- Wenn Ihre Anwendung ausgefallen ist und nicht antwortet, werden Nachrichten in die Warteschlange gestellt, und es erfolgen bis zu drei Tage lang weitere Zustellversuche.
- Zusätzlich können übereinstimmende Nachrichten optional in einem Postfach zu Archivierungs-, Backup- oder Debugging-Zwecken gespeichert werden.
- Diese Komplexitäten spielen für Sie keine Rolle mehr. Sie können sich ganz auf das Schreiben von profitablem Code konzentrieren.
Übrigens kann die Manipulation von E-Mail-Routen programmatisch über eine API erfolgen. Dies ermöglicht es Ihnen, einen magischen Flask/Sinatra-ähnlichen @email_in()-Dekorator zu erstellen, der alles miteinander verknüpft.
Mailgun Routes können mehr als nur einen einfachen Abgleich der Empfangsadresse. Sie unterstützen Erfassungen über reguläre Ausdrücke, das „Match-if-nothing-else-matched“-Verhalten, sie verfügen über Ausführungsprioritäten – im Grunde handelt es sich um eine einfache Programmiersprache für das E-Mail-Routing.
Das waren die Grundlagen. Einfach großartig. Legen wir also los und befreien die Welt von unsinnigen „no-reply@“-E-Mails. Es ist höchste Zeit.
Herzliche Grüße
–
\nMailgunners
Edit: Beteiligen Sie sich gerne an der Diskussion auf HN