メール取込
方針
- 受信・保存はプログラム(IMAP)
- 種別判定・金額抽出は Gemini(Skills 相当は Gemini の仕組みで代替)
- 金額検証・重複排除はプログラム側のガードレール
- 「全部 Gemini 丸投げ」にはしない
想定されるメール種別(楽天など)
厳密な件名ルールは最初から全部書かない。Gemini に分類させる。
| kind | イメージ | 扱い |
|---|---|---|
flash |
利用速報(使った直後) | リアルタイム支出候補。Discord 即時向き |
statement |
明細・請求・確定系 | 補正・取りこぼし検知。即時通知は抑制可 |
ignore |
キャンペーン、案内、セキュリティ通知など | 支出にしない。raw だけ残す |
同じ支払いが flash と後続メールで二重に見えることがあるので、重複排除が必要。
処理フロー
IMAP 定期取得(1〜5分間隔)
→ 未処理メールを取得
→ raw(件名 / From / 受信日時 / 本文テキスト)を PostgreSQL に保存
→ Gemini 分類+抽出(structured JSON)
→ プログラムで検証
- amount が本文に存在するか
- kind / is_expense の整合
- dedupe_key で既存支出と突合
→ OK なら支出イベント化
→ flash かつ is_expense なら Discord へ厳し目通知
Gemini にやらせること
kind: flash / statement / ignoreamount,merchant,paid_atis_expense: 家計の「支出」として数えるかdedupe_hint: 同一取引っぽさを示す短い鍵(店名+金額+日付など)confidence/notes(任意)
やらせないこと:
- メール受信そのもの
- 生データの永続化
- 最終的な会計確定(検証失敗時は保留)
プロンプトの渡し方は Gemini。
プログラム側ガードレール
- 生データ必須保存 … 再処理・監査用
- 金額検証 … 抽出
amountが本文中に現れるか(表記ゆれは正規化して比較) - ignore はイベント化しない
- 重複排除
- 同一 Message-ID は再処理しない
dedupe_hint+ 金額 + 日付近傍で flash ↔ statement を結びつける- 既に flash で計上済みなら statement は「確認」扱い(二重計上しない)
- 失敗時 …
needs_reviewにして Discord に「読めなかった」と短く報告(任意)
IMAP 準備
- 利用通知が届くメールアドレスを決める(専用でも可)
- IMAP が使えること(Gmail ならアプリパスワード等)
- 可能なら楽天の送信元 / 件名でざっくり事前フィルタ(最終判定は Gemini)
環境変数(メール関連)
IMAP_HOST=
IMAP_USER=
IMAP_PASSWORD=
IMAP_FOLDER=INBOX
# 任意: 送信元ざっくりフィルタ
# IMAP_FROM_FILTER=rakuten.co.jp
出力 JSON イメージ(Gemini → アプリ)
{
"kind": "flash",
"is_expense": true,
"amount": 1280,
"currency": "JPY",
"merchant": "コンビニXYZ",
"paid_at": "2026-08-08T12:34:00+09:00",
"dedupe_hint": "コンビニXYZ|1280|2026-08-08",
"confidence": 0.86,
"notes": "利用速報と判定"
}