🦁

AWS Chaliceを使ってFileMaker用のメール受信APIを作る

2022/02/10に公開

(2022年11月25日追記)
私の本が株式会社インプレス R&Dさんより出版されました。この記事の内容も含まれています。イラストは鍋料理さんの作品です。猫のモデルはなんとうちのコです!

https://www.amazon.co.jp/dp/B0BMPZW444/

感想を書いていただけるととても嬉しいです!

(2022年8月3日追記)この記事の内容はこちらの本でも読めます。

https://zenn.dev/sikkim/books/how_to_create_api_sales_service

先日、はじめてPyPIにPythonライブラリを登録した話を書きましたが、そもそも何のためにライブラリを登録したかという話をします。

背景

FileMakerにはメールを送信する機能はありますが、受信する機能はありません。しかし業務用アプリを作っていると、メールの情報をDBに取り込んで処理したい場面は結構出てきます。メール受信機能のように、標準装備されていない機能をFileMakerで実現する方法は、大きく分けてふたつあります。ひとつはプラグインをインストールする方法で、もうひとつはAPIなどを利用して外部システムと連携させる方法です。メールを受信するためのプラグインとしてはMailit360worksあたりが有名です。外部システムとの連携は、検索するといくつか出てきますが、今のところ定番と呼べるようなサービスはなさそうです。

弊社では私が数年前にPythonで開発したメール受信APIを利用してメールをFileMakerに取り込んでいます。それ以前はMailitプラグインを利用していました。ほかにも自社開発したAPIはいくつかあって、中には「これは販売したら売れるんじゃない?」と思えるものもあります。今までは忙しくてAPIの販売に踏み切れませんでしたが、今年前半は比較的余裕があるので、APIを販売してみることにしました。

APIを販売するには、販売するためのプラットフォームが必要です。既存の仕組みだとAWS MarketplaceRakuten RapidAPIがありますが、どちらもいまいち好きになれなかったので、プラットフォームごと自社開発することにしました。弊社はフロントエンドの技術が弱いので、これを機にReactを学びながら作ってみる予定です。そうすればスキルも付きますし、なによりおもしろいですからね。

フロント側はこれから数か月かけて作るとして、バックエンドのAPIは先に作ってテストしておいた方がよさそうだと思い、準備することにしました。社内向けと違って一般販売を視野に入れたAPIを開発するには下記のようにクリアすべき課題がいくつかあります。

  • 認証は必須
  • APIの利用制限は必要
  • 利用者が増えたら自動でスケールするようにしたい
  • 利用がないときにはお金がかからないようにしたい

以上を踏まえると必然的にクラウド上でサーバーレス運用する方針になるかと思います。GCPにするかAWSにするかでかなり悩みましたが、利用制限を簡単にかけられることが決め手となってAWSを利用することにしました。

メール受信APIの仕様

今回実装するメール受信APIの機能はこの2つです。

  • IMAP4サーバーからメールを取得し、JSON形式にして返す
  • 指定された日数より前のメールをサーバーから削除する

リクエストパラメーターとレスポンスはそれぞれ以下の通りです。

メール受信機能のリクエストパラメーター

リクエストパラメーターのキー 説明
host_name IMAP4サーバーのホスト名。
user_id IMAP4アカウントのユーザーID。
password IMAP4アカウントのパスワード。
search_option 省略可。IMAP4の検索オプション。省略時はUNSEENが設定され、未読メッセージのみが取得対象となる。
timezone 省略可。クライアントのタイムゾーン。省略時はAsia/Tokyoが設定される。

メール受信機能のレスポンス

レスポンスのキー 説明
messages メッセージのデータが配列で格納される。
messages[n].subject メッセージのタイトル。
messages[n].body メッセージ本文。
messages[n].from メッセージのFrom。
messages[n].to メッセージのTo。
messages[n].cc メッセージのCC。
messages[n].date メッセージの送信日付。
messages[n].time メッセージの送信時刻。
messages[n].format メッセージのフォーマット(text/plainもしくはtext/html)。
messages[n].msg_id メッセージID。
messages[n].charset 文字コード(utf-8など)。
messages[n].header メッセージのヘッダー。
messages[n].attachments 添付ファイルのデータが配列で格納される。
messages[n].attachments[m].file_name 添付ファイルのファイル名。
messages[n].attachments[m].file_obj 添付ファイルのオブジェクトがbase64エンコードされたテキストデータ。

メール削除機能のリクエストパラメーター

リクエストパラメーターのキー 説明
host_name IMAP4サーバーのホスト名。
user_id IMAP4アカウントのユーザーID。
password IMAP4アカウントのパスワード。
days 省略可。数値型で日数を指定するとIMAP4サーバー上からその日数より前に届いたメッセージが削除される。省略時は90が設定される。

メール削除機能のレスポンス

レスポンスのキー 説明
delete_count 削除したメッセージの件数。

APIの実装

APIはAWSのLambdaとAPI Gatewayを用いて実装します。普通にLambda Functionを書いても良いのですが、Chaliceを使うととても簡単に実装できます。Chaliceの使い方はこちらのハンズオン記事がわかりやすいです。

https://aws.amazon.com/jp/blogs/startup/event-report-chalice-handson/

メール受信APIのソースコードはこちらです。

app.py
import json
from chalice import Chalice
from imap2dict import MailClient


app = Chalice(app_name='fm_mail_backend')

@app.route('/fetch_mail', methods=['POST'], api_key_required=True)
def fetch_mail():
    # リクエストパラメータの解析
    request_params = app.current_request.json_body
    # hostname, user_id, passwordは必須
    host_name = request_params['host_name']
    user_id = request_params['user_id']
    password = request_params['password']
    # search_optionとtimezoneは省略可能
    search_option = request_params['search_option'] if 'search_option' in request_params else 'UNSEEN'
    timezone = request_params['timezone'] if 'timezone' in request_params else 'Asia/Tokyo'

    # メールを受信
    cli = MailClient(host_name, user_id, password)
    messages = cli.fetch_mail(search_option=search_option, timezone=timezone)
    resp = {
        'status':'OK',
        'messages':messages
    }

    # 結果を返す
    return json.dumps(resp, ensure_ascii=False)


@app.route('/delete_mail', methods=['DELETE'], api_key_required=True)
def delete_mail():
    # リクエストパラメータの解析
    request_params = app.current_request.json_body
    # hostname, user_id, passwordは必須
    host_name = request_params['host_name']
    user_id = request_params['user_id']
    password = request_params['password']
    # daysは省略可能
    days = request_params['days'] if 'days' in request_params else 90

    # メールを削除
    cli = MailClient(host_name, user_id, password)
    delete_count = cli.delete_mail(days=days)

    # 結果を返す
    return {"delete_count":delete_count}

面倒くさい部分をすべてimap2dictライブラリに追い出したおかげで、こんなにシンプルに書くことができました。たったこれだけの記述でメール受信と削除の機能を実現できるって、すごくないですか?

これをしたいがために前回の記事でPyPIにライブラリを登録したわけです。ようやく冒頭部分につながりました。

後はchalice deployでデプロイして、API GatewayにAPIキーを設定すればAPI本体は完成です。LambdaとAPI Gatewayの連携やIAMのロールなどはChaliceが自動で良い感じに設定してくれます。バックエンドとしてはAPIキーの自動発行やユーザー管理などを作り込む必要がありますが、そのあたりはおいおい作っていきます。

FileMaker側のサンプルアプリ

APIができたので実際にFileMakerから接続してみます。サンプルアプリはこちらからダウンロードできます。

サンプルアプリ初期画面

右上の歯車アイコンをクリックすると設定画面が開きます。

サンプルアプリ設定画面

「FM Mail」というのはサービスの名前(仮称)です。すべての項目を設定して「新着メール受信」ボタンをクリックすると下図のようにメールと添付ファイルに対応したレコードが作られます。

サンプルアプリのメール受信結果

日本語の添付ファイル名も文字化けせずに読み込めていますね。

サービス公開に向けて

弊社の業務用アプリは数週間前から実際にAWS上のAPIに接続して運用しています。切り替えのときは緊張しましたが、今のところまったく問題なく動いていて、かえって気持ち悪いです。

API販売プラットフォームの実装とサービス公開にはあと数か月はかかると思いますが、進捗状況はその都度記事にして公開していく予定です。

Discussion