LAUDE.mdの更新が完了しました。変更内容:

最終更新日 → 2026-02-25
プロジェクト構造 → mail/ と settings/password/ ページを追加
データモデル概要 → MailSender, MailEmail, MailNotificationToken を追加
実装状況 → メールフィルタリング機能を本番稼働済みに更新、パスワード変更機能を追加
マスタードキュメントリンク → document/11_マスタードキュメント_メール通知関連編.md を追加
トラブルシューティング → 本番デプロイコマンド(--env-file .env.production 必須)を冒頭に追加
更新履歴 → 今回の変更を記録
This commit is contained in:
Akira
2026-02-25 10:06:22 +09:00
parent 407d915b35
commit b386ee4380
2 changed files with 646 additions and 7 deletions

View File

@@ -0,0 +1,599 @@
# マスタードキュメント - メール通知関連編
> **最終更新**: 2026-02-25
> **対象バージョン**: Phase 1 完了時点(本番稼働中)
> **目的**: このドキュメントだけでメール通知機能の全容を把握できること
---
## 目次
1. [システム概要・全体構成](#1-システム概要全体構成)
2. [データモデル](#2-データモデル)
3. [API仕様バックエンド](#3-api仕様バックエンド)
4. [Windmill フロー仕様](#4-windmill-フロー仕様)
5. [画面仕様(フロントエンド)](#5-画面仕様フロントエンド)
6. [本番環境の設定値](#6-本番環境の設定値)
7. [設計判断と制約](#7-設計判断と制約)
8. [運用手順](#8-運用手順)
9. [ソースファイル索引](#9-ソースファイル索引)
---
## 1. システム概要・全体構成
### 機能の目的
複数のメールアカウントGmail × 2 + Xserver × 6 = 計8アカウントに届く大量のメールを自動でフィルタリングし、農家にとって重要なメールだけを LINE で通知する。
### システム構成図
```
[メールサーバー群] [Windmill] [KeinaSystem]
Gmail (2口) ─IMAP─▶ mail_filter フロー ──API──▶ Django バックエンド
Xserver (6口) ↓ (DB記録・ルール参照)
[Gemini API] ↓
LLM判定 フロントエンド
↓ ・メール履歴画面
LINE Messaging API ・ルール管理画面
(重要と判定した場合のみ通知) ・フィードバックページ
LINE通知文にフィードバックURL ────────┘
を含め、ユーザーがタップして
フィードバックを送信
```
### 処理フロー1メールあたり
```
1. IMAP 接続 → 前回処理済み UID 以降の新着メールを取得
2. 送信者ルール確認GET /api/mail/sender-rule/
├── never_notify → スキップ(記録しない)
├── always_notify → LLMスキップ、即 LINE 通知
└── ルールなし → 3へ
3. 過去フィードバック集計取得GET /api/mail/sender-context/
4. Gemini API で重要度判定LLM
5. KeinaSystem に記録POST /api/mail/emails/
├── not_important → 記録のみ、通知なし
└── important → フィードバックURLを発行、LINE 通知
6. 処理済み最終 UID を Windmill Variable に保存
```
### 10分ごとの定期実行
Windmill スケジュール `0 */10 * * * *` で自動実行。サーバー上の production Windmill で稼働。
---
## 2. データモデル
### 2.1 MailSender送信者ルール
**テーブル名**: `mail_mailsender`
| フィールド | 型 | 説明 |
|---|---|---|
| `id` | BigAutoField | PK |
| `email` | EmailField (null可) | アドレス指定ルールの場合 |
| `domain` | CharField(255, null可) | ドメイン指定ルールの場合 |
| `rule` | CharField(20) | `never_notify` / `always_notify` |
| `note` | TextField | メモ(任意)|
| `created_at` | DateTimeField | 作成日時 |
| `updated_at` | DateTimeField | 更新日時 |
**制約**: `email``domain` は必ずどちらか一方のみ設定DB CHECK 制約 `mail_sender_email_or_domain`
**ルール判定の優先順位**: アドレスルールが先、次にドメインルール
### 2.2 MailEmail受信メール記録
**テーブル名**: `mail_mailemail`
| フィールド | 型 | 説明 |
|---|---|---|
| `id` | BigAutoField | PK |
| `account` | CharField(20) | `gmail` / `gmail_service` / `xserver` / `hotmail` |
| `message_id` | CharField(500, unique) | メールの Message-ID ヘッダー(重複防止に使用)|
| `sender_email` | EmailField | 送信者メールアドレス |
| `sender_domain` | CharField(255) | 送信者ドメイン |
| `subject` | CharField(500) | 件名 |
| `body_preview` | TextField | 本文冒頭最大500文字|
| `received_at` | DateTimeField | 受信日時 |
| `llm_verdict` | CharField(20) | `important` / `not_important` |
| `notified_at` | DateTimeField (null可) | LINE 通知日時(通知済みの場合のみ)|
| `feedback` | CharField(20, null可) | `important` / `not_important` / `never_notify` / `always_notify` |
| `feedback_at` | DateTimeField (null可) | フィードバック日時 |
**ordering**: `-received_at`(新しい順)
**重複防止**: `message_id` の unique 制約。同じメールが複数アカウントで受信された場合は 2件目以降を「重複メール、スキップ」として処理400エラーを無視
### 2.3 MailNotificationTokenフィードバック用トークン
**テーブル名**: `mail_mailnotificationtoken`
| フィールド | 型 | 説明 |
|---|---|---|
| `id` | BigAutoField | PK |
| `email` | OneToOneField → MailEmail | |
| `token` | UUIDField (unique) | フィードバック URL 用 UUID |
| `created_at` | DateTimeField | |
**用途**: `important` と判定されたメールに対して作成。`/mail/feedback/<token>/` の URL をLINE通知文に含める。有効期限なし。
---
## 3. API仕様バックエンド
ベース URL: `https://main.keinafarm.net/api/mail/`
### 3.1 認証方式
| 認証方式 | 対象エンドポイント | ヘッダー |
|---|---|---|
| APIキー認証Windmill用 | sender-rule, sender-context, POST emails/ | `X-API-Key: <MAIL_API_KEY>` |
| JWT認証フロントエンド用 | GET emails/, stats/, senders/, PATCH emails/<pk>/feedback/ | `Authorization: Bearer <token>` |
| 認証不要 | GET/POST feedback/<token>/ | なし |
**MAIL_API_KEY**: `.env.production``MAIL_API_KEY` と一致している必要がある。Windmill Variable `u/admin/KEINASYSTEM_API_KEY` に設定。
### 3.2 Windmill向けエンドポイント
#### GET /api/mail/sender-rule/
送信者ルールを確認する。
**リクエスト**: クエリパラメータ `email` `domain`
**レスポンス例**:
```json
{"matched": true, "rule": "never_notify", "match_type": "address"}
{"matched": false}
```
**判定順序**: アドレス一致 → ドメイン一致 → マッチなし
---
#### GET /api/mail/sender-context/
過去フィードバックの集計を返すLLMへのコンテキスト用
**リクエスト**: クエリパラメータ `email` `domain`
**レスポンス例**:
```json
{
"total_notified": 8,
"important": 2,
"not_important": 5,
"never_notify": 0,
"no_feedback": 1
}
```
---
#### POST /api/mail/emails/
メールを記録し、`important` の場合はフィードバックURLを発行する。
**リクエストボディ**:
```json
{
"account": "gmail",
"message_id": "<unique-message-id>",
"sender_email": "sender@example.com",
"sender_domain": "example.com",
"subject": "件名",
"body_preview": "本文冒頭...",
"received_at": "2026-02-25T15:46:00+09:00",
"llm_verdict": "important"
}
```
**レスポンス例**:
```json
{"id": 69, "feedback_url": "https://main.keinafarm.net/mail/feedback/<uuid>"}
```
`not_important` の場合: `{"id": 68}`feedback_url なし)
**重複処理**: `message_id` が既存の場合 400 を返す。Windmill 側で「重複メール、スキップ」として処理。
---
### 3.3 フィードバックエンドポイント(認証不要)
#### GET /api/mail/feedback/\<token\>/
フィードバックページ表示用にメール情報を返す。
**レスポンス例**:
```json
{
"id": 69,
"sender_email": "sender@example.com",
"sender_domain": "example.com",
"subject": "件名",
"body_preview": "本文...",
"received_at": "2026-02-25T15:46:00+09:00",
"feedback": null
}
```
---
#### POST /api/mail/feedback/\<token\>/
フィードバックを保存する。
**リクエストボディ**:
```json
{
"feedback": "never_notify",
"scope": "address"
}
```
`feedback``important` / `not_important` / `never_notify` / `always_notify` のいずれか。
`scope``never_notify` / `always_notify` の場合のみ必要(`address` / `domain`)。
`never_notify` / `always_notify` + scope の場合、`MailSender` レコードを自動 upsert。
---
### 3.4 フロントエンド向けエンドポイントJWT認証
#### GET /api/mail/emails/
メール処理履歴を返す最新100件
**クエリパラメータ**: `account`(アカウント絞り込み)、`verdict`LLM判定絞り込み
---
#### PATCH /api/mail/emails/\<pk\>/feedback/
履歴画面から直接フィードバックを更新する。
**リクエストボディ**: `feedback`(必須)、`scope``never_notify`/`always_notify` 時のみ)
---
#### GET /api/mail/stats/
ダッシュボード用統計。
**レスポンス例**:
```json
{
"today_processed": 12,
"today_notified": 3,
"feedback_pending": 1,
"total_rules": 5
}
```
---
#### GET /api/mail/senders/
送信者ルール一覧。
#### POST /api/mail/senders/
送信者ルール追加。`email` または `domain` のどちらか一方を指定。
#### DELETE /api/mail/senders/\<id\>/
送信者ルール削除。
---
## 4. Windmill フロー仕様
### 4.1 基本情報
| 項目 | 値 |
|---|---|
| フローパス | `f/mail/mail_filter` |
| スクリプト言語 | Python 3 |
| スケジュール | `0 */10 * * * *`10分ごと|
| スケジュールパス | `f/mail/mail_filter_schedule` |
| Windmill URL | `https://windmill.keinafarm.net` |
| ワークスペース | `admins` |
### 4.2 処理対象アカウント
| 変数名 | メールアドレス | サーバー |
|---|---|---|
| `XSERVER1` | `akira@keinafarm.com` | `sv579.xserver.jp:993` |
| `XSERVER2` | `service@keinafarm.com` | `sv579.xserver.jp:993` |
| `XSERVER3` | `midori@keinafarm.com` | `sv579.xserver.jp:993` |
| `XSERVER4` | `kouseiren@keinafarm.com` | `sv579.xserver.jp:993` |
| `XSERVER5` | `post@keinafarm.com` | `sv579.xserver.jp:993` |
| `XSERVER6` | `sales@keinafarm.com` | `sv579.xserver.jp:993` |
| `GMAIL` | `akiracraftwork@gmail.com` | `imap.gmail.com:993`、All Mail |
| `GMAIL2` | `akiranoushi@gmail.com` | `imap.gmail.com:993`、All Mail |
Hotmail は定義済みだがコメントアウト(未有効化)。
### 4.3 Windmill Variables 一覧
本番 Windmill (`windmill.keinafarm.net`、ワークスペース `admins`) に設定。
| Variable パス | 内容 | Secret |
|---|---|---|
| `u/admin/GMAIL_IMAP_USER` | Gmail ユーザー | ✓ |
| `u/admin/GMAIL_IMAP_PASSWORD` | Gmail アプリパスワード | ✓ |
| `u/admin/GMAIL2_IMAP_USER` | Gmail2 ユーザー | ✓ |
| `u/admin/GMAIL2_IMAP_PASSWORD` | Gmail2 アプリパスワード | ✓ |
| `u/admin/XSERVER1_IMAP_USER` | `akira@keinafarm.com` | — |
| `u/admin/XSERVER1_IMAP_PASSWORD` | Xserver IMAP パスワード | ✓ |
| `u/admin/XSERVER2_IMAP_USER` | `service@keinafarm.com` | — |
| `u/admin/XSERVER2_IMAP_PASSWORD` | Xserver IMAP パスワード | ✓ |
| `u/admin/XSERVER3_IMAP_USER` | `midori@keinafarm.com` | — |
| `u/admin/XSERVER3_IMAP_PASSWORD` | Xserver IMAP パスワード | ✓ |
| `u/admin/XSERVER4_IMAP_USER` | `kouseiren@keinafarm.com` | — |
| `u/admin/XSERVER4_IMAP_PASSWORD` | Xserver IMAP パスワード | ✓ |
| `u/admin/XSERVER5_IMAP_USER` | `post@keinafarm.com` | — |
| `u/admin/XSERVER5_IMAP_PASSWORD` | Xserver IMAP パスワード | ✓ |
| `u/admin/XSERVER6_IMAP_USER` | `sales@keinafarm.com` | — |
| `u/admin/XSERVER6_IMAP_PASSWORD` | Xserver IMAP パスワード | ✓ |
| `u/admin/GEMINI_API_KEY` | Gemini API キー | ✓ |
| `u/admin/LINE_CHANNEL_ACCESS_TOKEN` | LINE Messaging API トークン | ✓ |
| `u/admin/LINE_TO` | LINE 通知先ユーザー ID | ✓ |
| `u/admin/KEINASYSTEM_API_KEY` | KeinaSystem API キー(`.env.production``MAIL_API_KEY` と同値)| ✓ |
| `u/admin/KEINASYSTEM_API_URL` | `https://main.keinafarm.net` | — |
| `u/admin/MAIL_FILTER_GMAIL_LAST_UID` | Gmail 最終処理済み UID | — |
| `u/admin/MAIL_FILTER_GMAIL2_LAST_UID` | Gmail2 最終処理済み UID | — |
| `u/admin/MAIL_FILTER_XSERVER1_LAST_UID` | Xserver1 最終処理済み UID | — |
| `u/admin/MAIL_FILTER_XSERVER2_LAST_UID` | Xserver2 最終処理済み UID | — |
| `u/admin/MAIL_FILTER_XSERVER3_LAST_UID` | Xserver3 最終処理済み UID | — |
| `u/admin/MAIL_FILTER_XSERVER4_LAST_UID` | Xserver4 最終処理済み UID | — |
| `u/admin/MAIL_FILTER_XSERVER5_LAST_UID` | Xserver5 最終処理済み UID | — |
| `u/admin/MAIL_FILTER_XSERVER6_LAST_UID` | Xserver6 最終処理済み UID | — |
### 4.4 LAST_UID の仕組み
- **初回実行**: `LAST_UID``0` または未設定の場合、現在の最大 UID を記録して終了(既存メールを遡らない)
- **通常実行**: `LAST_UID + 1` 以降の UID を検索して処理
- **エラー時**: 個別メッセージの処理に失敗しても、成功した最大 UID まで更新する
### 4.5 LLM 判定Gemini
モデル: `gemini-2.0-flash``temperature=0`, `maxOutputTokens=10`
プロンプトに渡す情報:
- 送信者アドレス・件名・本文冒頭
- 過去の同一送信者のフィードバック集計(`sender-context` API から取得)
回答: `1`(重要)/ `2`重要でないの1文字。`1` で始まる場合 `important`
### 4.6 LINE 通知文フォーマット
```
📧 重要なメールが届きました
宛先: Gmail (メイン)
差出人: sender@example.com
件名: 件名テキスト
フィードバック:
https://main.keinafarm.net/mail/feedback/<uuid>
```
---
## 5. 画面仕様(フロントエンド)
### 5.1 フィードバックページ(認証不要)
**URL**: `/mail/feedback/[token]`
**ファイル**: `frontend/src/app/mail/feedback/[token]/page.tsx`
LINE 通知のリンクから直接アクセス。JWT 認証不要のため、`api` インスタンスJWT自動付与ではなく素の `fetch` を使用。
**表示内容**:
- 送信者アドレス・件名・受信日時・本文冒頭
- フィードバックボタン: 重要だった / 普通のメール / 今後通知しない / 常に通知してほしい
- `今後通知しない` / `常に通知` 選択時: アドレス / ドメイン の適用範囲を選択
**状態**:
- 既にフィードバック済みの場合は現在の値をハイライト表示(再選択可能)
- 送信完了後「受け付けました」画面に切り替わる
---
### 5.2 メール処理履歴JWT認証
**URL**: `/mail/history`
**Navbar**: 「メール履歴」History アイコン)
**ファイル**: `frontend/src/app/mail/history/page.tsx`
**表示内容**: 処理したメール最新100件
| カラム | 内容 |
|---|---|
| 受信日時 | 日時 + アカウント名 |
| 送信者 | メールアドレス |
| 件名 | テキストtruncate|
| LLM判定 | 重要(赤)/ 通常(灰)バッジ |
| フィードバック | 状態バッジ + 編集ボタン |
**フィルター**: アカウント別・LLM判定別セレクトボックス
**フィードバックモーダル**: 履歴画面からも4択でフィードバックを設定・変更できる。`never_notify` / `always_notify` 選択時はアドレス/ドメインの適用範囲を表示。
---
### 5.3 メール通知ルール管理JWT認証
**URL**: `/mail/rules`
**Navbar**: 「メールルール」Shield アイコン)
**ファイル**: `frontend/src/app/mail/rules/page.tsx`
**追加フォーム**:
- 種別(アドレス / ドメイン)
- ルール(通知しない / 常に通知)
- 値(メールアドレスまたはドメイン名)
- メモ(任意)
**一覧表示**: 種別バッジ・ルールバッジ・値・メモ・設定日・削除ボタン
**ルールが自動追加される場面**: フィードバックで `never_notify` / `always_notify` を選択してスコープを指定すると自動登録される。
---
## 6. 本番環境の設定値
### バックエンド(`.env.production`
```
MAIL_API_KEY=<Windmill の KEINASYSTEM_API_KEY と同じ値>
```
`settings.py` での参照:
```python
MAIL_API_KEY = os.environ.get('MAIL_API_KEY', '')
FRONTEND_URL = os.environ.get('FRONTEND_URL', 'http://localhost:3000')
```
`FRONTEND_URL` はフィードバック URL の生成に使用(本番値: `https://main.keinafarm.net`)。
### デプロイコマンド
```bash
# サーバー上で実行(--env-file を必ず指定)
cd /home/keinasystem/keinasystem_t02
git pull
docker compose -f docker-compose.prod.yml --env-file .env.production build
docker compose -f docker-compose.prod.yml --env-file .env.production up -d
```
**注意**: `--env-file .env.production` を省略すると SECRET_KEY 等が空になりバックエンドが起動しない。
### Windmill フローの更新手順
ローカルの `flows/mail_filter.flow.json` を編集後:
```bash
cd C:/Users/akira/Develop/windmill_workflow
# サーバーに転送
scp flows/mail_filter.flow.json keinafarm-claude:/tmp/mail_filter.flow.json
# サーバー上でデプロイWindmill API 経由)
ssh keinafarm-claude 'TOKEN=qLJ3VPZ61kTDiIwaUPUu1dXszGrsN1Dh; WM=http://172.18.0.15:8000/api/w/admins
curl -s -X DELETE -H "Authorization: Bearer $TOKEN" "$WM/flows/delete/f/mail/mail_filter"
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d @/tmp/mail_filter.flow.json "$WM/flows/create"'
```
---
## 7. 設計判断と制約
### Windmill と KeinaSystem の疎結合
Windmill はスケジューラ・LLM呼び出し担当、KeinaSystem は DB・UI 担当として明確に分離。両者は REST APIAPIキー認証でのみ連携。
→ Windmill の障害が KeinaSystem のメイン機能(作付け計画等)に影響しない。
### APIキー認証の実装
`MailAPIKeyPermission``BasePermission` サブクラス)で `X-API-Key` ヘッダーを検証。`secrets.compare_digest` でタイミング攻撃を防止。
`authentication_classes = []` を明示的に設定したビューとしていないビューがある:
- `SenderRuleView`, `SenderContextView`: `authentication_classes = []` を設定済み → 403
- `MailEmailView` の POST: 設定なし(デフォルトの JWTAuthentication が動く)→ キー不一致時は 401
**統一するなら** `MailEmailView` にも `authentication_classes = []` を追加すべき(現状は動作上問題なし)。
### フィードバック URL のセキュリティ
UUID v4 のランダムトークンのみで認証。有効期限なし。LINE に送信された URL を知っている人なら誰でもフィードバックを送れる(悪用リスクは低いため許容)。
### 重複メール処理
同じメールが複数アカウントで受信される場合(転送設定等)、`message_id` の unique 制約で2件目以降を自動スキップ。最初に処理したアカウントの `account_code` でDBに記録される。
---
## 8. 運用手順
### 新しいメールアカウントを追加する場合
1. `flows/mail_filter.flow.json``ACCOUNTS` リストにエントリを追加
2. 本番 Windmill に Variables を追加USER, PASSWORD, LAST_UID
3. フローを再デプロイ
```bash
# Variable 追加例(サーバー上で実行)
ssh keinafarm-claude 'TOKEN=qLJ3VPZ61kTDiIwaUPUu1dXszGrsN1Dh; WM=http://172.18.0.15:8000/api/w/admins
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d "{\"path\":\"u/admin/XSERVER7_IMAP_USER\",\"value\":\"newuser@keinafarm.com\",\"is_secret\":false,\"description\":\"\"}" \
"$WM/variables/create"'
```
### LINE トークンが期限切れになった場合
LINE Developers Console でトークンを再発行し、本番 Windmill の Variable を更新:
```bash
ssh keinafarm-claude 'TOKEN=qLJ3VPZ61kTDiIwaUPUu1dXszGrsN1Dh; WM=http://172.18.0.15:8000/api/w/admins
# 古いものを削除してから再作成
curl -s -X DELETE -H "Authorization: Bearer $TOKEN" "$WM/variables/delete/u/admin/LINE_CHANNEL_ACCESS_TOKEN"
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d "{\"path\":\"u/admin/LINE_CHANNEL_ACCESS_TOKEN\",\"value\":\"<新トークン>\",\"is_secret\":true,\"description\":\"LINE Messaging APIトークン\"}" \
"$WM/variables/create"'
```
### フローのログを確認する
```bash
# 最近のジョブ一覧
ssh keinafarm-claude 'TOKEN=qLJ3VPZ61kTDiIwaUPUu1dXszGrsN1Dh
curl -s -H "Authorization: Bearer $TOKEN" "http://172.18.0.15:8000/api/w/admins/jobs/completed/list?per_page=10" \
| grep -o "\"id\":\"[^\"]*\"\|\"started_at\":\"[^\"]*\"\|\"script_path\":\"[^\"]*\"" \
| paste - - - | grep mail_filter'
# ステップジョブのログ取得
ssh keinafarm-claude 'TOKEN=qLJ3VPZ61kTDiIwaUPUu1dXszGrsN1Dh; JOB_ID=<ステップジョブID>
curl -s -H "Authorization: Bearer $TOKEN" \
"http://172.18.0.15:8000/api/w/admins/jobs/completed/get/$JOB_ID" \
| grep -o "\"logs\":\"[^\"]*\"" | sed "s/\\\\n/\n/g"'
```
---
## 9. ソースファイル索引
### バックエンド
| ファイル | 内容 |
|---|---|
| `backend/apps/mail/models.py` | MailSender, MailEmail, MailNotificationToken |
| `backend/apps/mail/serializers.py` | MailSenderSerializer, MailEmailCreateSerializer, MailEmailListSerializer, FeedbackDetailSerializer |
| `backend/apps/mail/views.py` | 全ビューSenderRuleView, SenderContextView, MailEmailView, MailStatsView, FeedbackView, MailEmailFeedbackView, MailSenderViewSet+ MailAPIKeyPermission |
| `backend/apps/mail/urls.py` | URL ルーティング |
| `backend/apps/mail/admin.py` | Django 管理画面登録 |
| `backend/apps/mail/migrations/` | マイグレーション |
| `backend/keinasystem/settings.py` | `MAIL_API_KEY`, `FRONTEND_URL` 設定L161-162|
| `backend/keinasystem/urls.py` | `path('api/mail/', include('apps.mail.urls'))` |
### フロントエンド
| ファイル | 内容 |
|---|---|
| `frontend/src/app/mail/feedback/[token]/page.tsx` | フィードバックページ(認証不要)|
| `frontend/src/app/mail/history/page.tsx` | メール処理履歴画面 |
| `frontend/src/app/mail/rules/page.tsx` | 送信者ルール管理画面 |
| `frontend/src/components/Navbar.tsx` | 「メール履歴」「メールルール」リンク追加済み |
### Windmill フロー
| ファイル | 内容 |
|---|---|
| `C:/Users/akira/Develop/windmill_workflow/flows/mail_filter.flow.json` | フロー定義Python スクリプト本体を含む)|
本番 Windmill でのパス: `f/mail/mail_filter`
スケジュール: `f/mail/mail_filter_schedule`