メルカリの発送忘れを完全防止!n8nでGmailからiPhoneリマインダーへタスクを自動登録する方法(DAViCal連携)

メルカリで商品が売れたときに届く「発送をお願いします」という通知メール。すぐに対応できない時、他のメールに埋もれてうっかり発送を忘れてしまいそうになった経験はありませんか?

今回は、オープンソースの自動化ツール「n8n」を使って、この通知メールを受信した瞬間に、自分のiPhoneのリマインダーへ自動的にタスクとして登録する仕組みを構築しました。
※本記事は、すでに自前でDAViCalなどのCalDAVサーバーを構築・運用しており、iPhoneとの同期設定が完了している方向けの少しマニアックな手順となります。

目次

なぜ「.icsのURL購読」や「IFTTT」ではなく「n8n + CalDAV」なのか?

今回の仕組みを作るにあたり、「他の方法でもできるのでは?」と思う方もいるかもしれません。ここでは、あえてこの構成を採用している理由を説明します。

理由1:iPhoneリマインダーでバッジ表示させるための「CalDAV」

カレンダー連携と聞いて「.icsのURLを発行して、iPhoneで購読(サブスクライブ)すれば簡単なのでは?」と思う方もいるかもしれません。しかし、iPhoneの仕様上、.icsのURL購読で追加できるのは標準の「カレンダー」アプリのみです。
「リマインダー」アプリでタスクとして管理し、完了チェックを行ったり、アプリアイコンに未完了の赤いバッジを表示させたりするためには、CalDAVサーバー経由での登録がどうしても必要になります。
(…ともっともらしい理由を書いていますが、実を言うと、昔趣味で立ち上げたDAViCalのカレンダーサーバーがそのまま自宅インフラとして稼働し続けており、その流れで使い続けているというのが一番の理由だったりします!)

理由2:IFTTTにはない「データ抽出の柔軟性」と「プライバシー」

iOSリマインダーへの自動登録ツールとして、「IFTTT」を思い浮かべる方も多いでしょう。IFTTTには専用のiOS Reminders連携機能があり、非エンジニアでも直感的に設定できる素晴らしいメリットがあります。
しかし、今回の要件においてIFTTTではなく「自己ホスト型n8n」を採用したのには明確なメリットがあります。

  • 複雑なデータ抽出が可能: IFTTTは用意されたトリガーとアクションを繋ぐシンプルな設計である反面、今回のように「メール本文の中から正規表現を使って特定の商品IDや価格だけをピンポイントで抜き出す」といった複雑なテキスト処理やループ処理には不向きです。n8nであれば、JavaScriptを用いた高度なデータ抽出・加工が可能です。
  • プライバシーと運用コスト: IFTTTはクラウドサービスであるため、個人の購買情報や取引相手の情報が含まれるメール内容を一度外部のサーバーに渡して処理することになります。自己ホスト型のn8nとDAViCalを組み合わせれば、パーソナルなデータを外部サービスに送信することなく、完全に手元の環境(オンプレミス)でセキュアに処理を完結できます。また、実行回数に応じた従量課金や機能制限を気にする必要もありません。

使用したツール

  • n8n: ワークフロー自動化ツール
  • Gmail: メルカリからの通知受け取り用
  • DAViCal: オープンソースのCalDAVサーバー(iPhoneのリマインダーアプリと同期済み)

ワークフローの作り方

メルカリリマインダフロー

ステップ1:Gmail TriggerとIFノードで特定のメールを検知

まずはn8nの「Gmail Trigger」ノードを設定します。Triggerのイベントを「Message Received」にし、毎分(Every Minute)ポーリングして新着メールを取得するように設定します。この時、後続の処理でメールの本文全体を解析するため、ノード設定の「Simplify」オプションは必ずオフ(無効)にしておいてください。

次に「IF」ノード(またはFilterノード)を配置し、取得したすべてのメールの中から「対象のメールだけ」を分岐させます。条件設定にて、送信元(from)が no-reply@mercari.jp であり、かつ件名(subject)に の発送をお願いします が含まれるものをTrueとして後続の処理へ流すようにします。この設計にすることで、ワークフロー上で「メルカリのメール」と「それ以外のメール」の処理経路が視覚的にわかりやすくなり、今後の拡張も容易になります。

ステップ2:Codeノードでデータ抽出とVTODO(タスク)データの作成

分岐のTrueルートに「Code」ノードを配置し、JavaScriptの正規表現を使ってメール本文から商品IDや商品名などを抽出します。さらに、メールの受信日時(internalDate)を取得し、カレンダーデータの標準規格(RFC 5545)で定められた YYYYMMDDThhmmssZ 形式に変換します。

const items = $input.all();
const results = [];

for (const item of items) {
    // プレーンテキスト本文の取得(フィールド名が存在しない場合のフォールバックを含む)
    const emailBody = item.json.text || item.json.snippet || item.json.html || "";
    
    // 正規表現によるグループ抽出
    const idMatch = emailBody.match(/商品ID\s*:\s*([a-zA-Z0-9]+)/);
    const nameMatch = emailBody.match(/商品名\s*:\s*(.+)/);
    const priceMatch = emailBody.match(/商品価格\s*:\s*(.+)/);
    
    const productId = idMatch ? idMatch[1].trim() : `unknown-${Date.now()}`;
    const productName = nameMatch ? nameMatch[1].trim() : "商品名不明";
    const productPrice = priceMatch ? priceMatch[1].trim() : "価格不明";

    // メールの受信日時(internalDate)を取得し、Dateオブジェクトに変換
    // 取得できない場合はフェイルセーフとして現在時刻を使用
    const internalDateMs = item.json.internalDate ? parseInt(item.json.internalDate, 10) : Date.now();
    const receiveDate = new Date(internalDateMs);

    // iCalendar規格のUTCフォーマット (YYYYMMDDThhmmssZ) へ変換する関数
    const formatIcalDate = (dateObj) => {
        return dateObj.toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z';
    };

    const icalDateStr = formatIcalDate(receiveDate);
    
    // VTODOデータの構築
    // DUE プロパティに受信日時を設定し、期限として認識させる
    const vtodoStr = 
`BEGIN:VCALENDAR\r
VERSION:2.0\r
PRODID:-//n8n Workflow//Mercari Dispatch//JA\r
BEGIN:VTODO\r
UID:mercari-${productId}\r
DTSTAMP:${icalDateStr}\r
DUE:${icalDateStr}\r
SUMMARY:【発送】${productName}\r
DESCRIPTION:メルカリ商品の発送手続きをお願いします。\\n\\n商品ID: ${productId}\\n価格: ${productPrice}\\n\\nアプリ起動: https://mercari.com/jp/launch/\r
STATUS:NEEDS-ACTION\r
PRIORITY:1\r
END:VTODO\r
END:VCALENDAR`;

    // 抽出された変数と生成されたペイロードを次のノードへ渡す
    results.push({
        json: {
            productId: productId,
            productName: productName,
            productPrice: productPrice,
            icalData: vtodoStr
        }
    });
}

return results;

【バッジ表示のための重要ポイント】

iPhoneのリマインダーアプリで「今日が期限の項目を含める」設定をオンにした際、アイコンに未完了バッジを表示させるには、タスクに「期限(Due)」が設定されている必要があります。そのため、生成するタスク(VTODO)データの DUE プロパティに、先ほど変換したメール受信日時をセットするコードを記述します。

ステップ3:HTTP RequestノードでDAViCalへタスクをPUT

最後に作成したVTODOデータを、DAViCalサーバーへ登録します。
n8n標準の「HTTP Request」ノードを使用し、HTTPメソッドは PUT を指定します。

ここでつまずきやすいのが送信先のURLです。親フォルダ(コレクション)宛てにPUTリクエストを送ると 405 Method Not Allowed エラーになってしまいます。必ず https://[ホスト]/caldav.php/[ユーザー]/tasks/mercari-[商品ID].ics のように、末尾に一意のファイル名(リソースURI)を指定してリクエストを送信してください。
また、データはJSONではなくプレーンテキストとして送信するため、Body Content Typeを Raw に設定し、ヘッダーに text/calendar; charset=utf-8 を指定します。

おわりに

この自動化ワークフローを導入してから、メルカリで商品が売れると自動的にiPhoneに通知とバッジがつくようになり、発送タスクの管理が劇的にラクになりました。
DAViCalサーバー環境がある方にとっては、ECサイトの通知や仕事の依頼メールなど、さまざまな用途に応用できる強力な仕組みですので、ぜひ試してみてください!