メール通知スパム問題をAIと一緒に3時間で解決した話
メール通知スパム問題をAIと一緒に3時間で解決した話
「めっちゃ通知来る」
深夜22時、Telegramに大量のメール再通知が届き始めた。
1分間に10件、20件、30件…。過去に処理済みのメールが次々と再通知されている。完全にスパム状態だった。
慌てて「Gmail Watcher」のcronを無効化した。これ以上の通知を止めるために。
でも、問題の原因はわかっていた。数時間前、メールフィルターを調整したときに、何かがおかしくなったんだ。
問題の経緯
この日の夜、私は「Gmail Watcher V2」という自作のメール監視システムを改善していた。
重要なメールを逃さないための再通知機能を実装したのだが、「通知が来なくなった」という問題が発生していた。
原因は明確だった:
– メールフィルターが強すぎて、全メールを「IGNORE(無視)」判定していた
– 重要な送信元(岩野圭佑さん、久保さん、松本さんなど)からのメールも見逃していた
フィルターを緩めた。除外キーワードを35個から8個に減らし、ブラックリストも13個から4個に削減した。
結果、再通知スパムが発生した。
何が起きたのか
問題の本質は「既存メールの再処理」だった。
フィルター変更後、過去に「IGNORE」判定されたメールが再分類され、一気に「HIGH(重要)」判定に変わった。それらすべてが再通知対象になってしまった。
コードを確認すると、原因は is_already_processed() 関数にあった:
def is_already_processed(self, message_id):
# メールが既に存在 AND 通知済み
c.execute('SELECT notification_status FROM emails WHERE message_id = ?', (message_id,))
result = c.fetchone()
if result:
# 既に通知済みならスキップ
if result[0] in ['notified', 'sent', 'skipped']:
return True
# レコードはあるが未通知なら再処理
return False
return False
notification_status = 'pending' のメールは「未通知」扱いで再処理されていた。
IGNOREメールは 'pending' のまま保存されていたため、フィルター変更後に再処理 → HIGH判定 → 大量再通知、という流れになっていた。
AIと一緒に解決した手順
深夜22時、私はAIアシスタント(OpenClaw)にメッセージを送った。
「Gmail Watcherが再通知スパム状態。緊急停止した。原因は既存メールの再処理。修正してくれ」
AIは即座に状況を理解し、修正方針を提示してきた:
修正1: is_already_processed() の変更
def is_already_processed(self, message_id):
# 既存メールは無条件でスキップ(再処理防止)
c.execute('SELECT id, notification_status FROM emails WHERE message_id = ?', (message_id,))
result = c.fetchone()
if result:
# 既存メールは無条件でスキップ
return True
return False
シンプルな変更だが、効果は絶大だった。「既存メールは二度と処理しない」という鉄則を徹底した。
修正2: save_email_v2() の改善
def save_email_v2(self, email_data, classification_result):
classification = classification_result['classification']
# notification_status を判定結果に応じて設定
if classification == 'ignore':
notification_status = 'ignore'
handled_status = 'ignored'
elif classification == 'digest_only':
notification_status = 'digest'
handled_status = 'unhandled'
else:
notification_status = 'pending'
handled_status = 'unhandled'
IGNOREメールは notification_status = 'ignore' で保存し、再処理対象から完全に除外する。
テストと再開
修正完了後、テスト実行した:
python3 gmail_watcher.py --check
結果:
📧 crosslink: 新着メールなし
📧 programming_school: 新着メールなし
✅ チェック完了: 新規 0件 / 重要 0件
再通知スパムなし。完璧に動いた。
cronを再開した。5分ごとのメール監視が復活した。
学んだこと
1. 「既存データの再処理」は危険
フィルターやロジックを変更するとき、既存データにどう影響するかを必ず考える必要がある。
今回の場合、フィルター変更が「過去のIGNOREメールを再処理」という予期しない動作を引き起こした。
教訓:「既存メールは二度と処理しない」というルールを徹底する。
2. ステータス管理は明確に
notification_status = 'pending' が「未通知」を意味するのか「処理中」を意味するのか、曖昧だった。
修正後:
– 'ignore' → 完全無視
– 'digest' → ダイジェスト対象
– 'pending' → 通知予定
– 'sent' → 通知済み
各ステータスの意味を明確にすることで、バグを防げる。
3. AIと一緒に解決する速さ
今回、問題発生から修正完了まで約3時間だった。
その内訳:
– 問題発生・緊急停止:5分
– 原因特定:10分
– 修正方針検討:5分
– 実装・テスト:2時間30分
– 再開・動作確認:10分
AIに修正を任せたことで、自分は「方針を決める」ことに集中できた。コード書きはすべてAIが担当した。
深夜のデバッグは焦らない
深夜22時、大量の通知が来たとき、最初は焦った。
でも、冷静に考えれば対処法は明確だった:
1. まず止める(cron無効化)
2. 原因を特定する
3. 修正する
4. テストする
5. 再開する
AIがいることで、「自分が全部やらなきゃ」というプレッシャーがなかった。「AIに任せて、自分は判断だけする」というスタンスで進められた。
まとめ
今回の教訓:
- フィルター変更は既存データに影響する → 再処理防止ロジックを徹底
- ステータス管理は明確に → 曖昧な状態を残さない
- AIと一緒なら、深夜のデバッグも怖くない
「めっちゃ通知来る」状態から、3時間で完全復旧した。
AIを使いこなせば、トラブルシューティングも楽になる。
関連記事:
– 岩盤浴で寝落ちしてる間にAIが全自動でシステム構築した話
– Gmail Watcher V2 実装の全記録
– 個人開発でハマったデバッグの話