Webhook APIを使って添付ファイル付きメールを処理する(Django編)

Customers Mail Cloudではプログラム側からデータを取得したり、メールを送信するWeb APIの他に、Customers Mail Cloudでメールを受信した時にイベントを伝えてくれるWebhook APIが用意されています。

Webhook APIを使うことで、自前でメールサーバを立てずにメール受信のタイミングでシステムを起動させられるようになります。メールサーバを安定して動作させ続けるのはメンテナンスコストが大きいですが、Customers Mail Cloudを使うことで簡単にメールと連携したシステムが作れるようになるでしょう。

今回はPythonとDjangoを使ってメールを処理する流れを紹介します。前回は添付ファイルを処理できないJSON形式での紹介でしたが、今回は添付ファイルも処理する形にしています。

フォーマットはJSONとマルチパートフォームデータ

Webhookの形式として、JSONとマルチパートフォームデータ(multipart/form-data)が選択できます。この二つの違いは、添付ファイルがあるかどうかです。JSONの場合、添付ファイルは送られてきません。メールに添付ファイルがついてくる可能性がある場合は、後者を選択してください。

f:id:moongift:20200227212807p:plain
Webhook設定ダイアログ

今回はマルチパートフォームデータにおけるWebhook処理について紹介します。

REST Frameworkの利用

今回はフォームを使わずにマルチパートフォームを受け取るので、REST Frameworkを利用します。

pip install rest_framework

サーバ側のコード

Djangoで作ったサーバ側のコードは次のようになります。まずviews.pyで必要なライブラリを読み込みます。

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.parsers import MultiPartParser
from django.core.files.storage import default_storage  
from django.core.files.base import ContentFile
from django.conf import settings

さらに INSTALLED_APPS にrest_frameworkを追加します。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
]

最後にMEDIA_ROOTを定義して、添付ファイルが保存されるフォルダを作成しておきます。

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

次にWebhookを受け取るクラスを定義します。JSONではないのでパースする必要はありませんが、REST FrameworkのMultiPartParserを指定する必要があります。

class WebhookAPI(APIView):
    parser_classes = [MultiPartParser]
    def post(self, request):
        json_message = request.data
        print(f'件名: {json_message["subject"]}')
        print(f'本文(テキスト): {json_message["text"]}')
        print(f'本文(HTML): {json_message["html"]}')
        print(f'FROM: {json_message["envelope-from"]}')
        print(f'TO: {json_message["envelope-to"]}')
        # ファイルの出力
        files = request.FILES
        for key in files:
            file = files[key]
            path = default_storage.save(f'{settings.MEDIA_ROOT}/{file.name}', ContentFile(file.read()))
        return Response({'succeeded': True})

このWebhookAPIをurls.pyで読み込んで、URLを定義します。

from .views import WebhookAPI

urlpatterns = [
    path('admin/', admin.site.urls),
    path('webhook', WebhookAPI.as_view()),
]

受信結果

結果は次のようになります。

件名: Webhookのテスト
本文(テキスト): Webhookのテスト用メールです。
本文(HTML): <div dir="ltr">Webhookのテスト用メールです。<div><br clear="all"><div><div dir="ltr" class="gmail_signature">...</div>
FROM: info@smtps.jp
TO: user@smtps.jp

そして添付ファイルがsettings.pyで指定したディレクトリ以下に保存されます。

f:id:moongift:20211125150944j:plain

以下はJSONですが、メールに関する具体的なデータ構造は次のようになっています。

{
    "filter": "info@smtps.jp",
    "headers": [
      :
    ],
    "subject": "Webhookのテスト",
    "envelope-to": "user@smtps.jp",
    "server_composition": "sandbox",
    "html": "<div dir=\"ltr\">Webhookのテスト用メールです。<div>...</div></div>",
    "text": "Webhookのテスト用メールです。\r\n\r\n--\r\n...",
    "envelope-from": "info@smtps.jp"
}

Webhookの結果は管理画面で確認

Webhookでデータが送信されたログは管理画面で確認できます。送信時のAPIキー設定など、HTTPヘッダーを編集するといった機能も用意されていますので、運用に応じて細かなカスタマイズが可能です。

f:id:moongift:20200227212832p:plain
Webhookログ

まとめ

メールと連携したシステムはよくあります。通常、メールサーバを立てて、その中で処理することが多いのですが、メールサーバが落ちてしまうとシステムが稼働しなくなったり、メール文面の解析が煩雑でした。Customers Mail Cloudを使えばそうした手間なくJSONで処理できて便利です。

受信サーバ | Customers Mail Cloud