フォーム経由の問い合わせ(add-leads)や Web 収集(collect-web-leads)では拾えない 「メールで直接やり取りした既存の重要関係者」 を、過去メールの全走査で取り込む。 collect-web-leads の姉妹ツールで、 閉域ローカル専用・送信機能なし・本文を保存しないという非交渉条件は同じ。承認分だけを run-newsletter-ops の取込器へ渡す。
0. 非交渉条件
特定電子メール法では、取引・やり取りの過程で相手が通知した営業用アドレスへの送信は一定条件下でオプトイン例外。 その趣旨に沿い、走査器に以下を機械的に強制する(変更禁止):
| 条件 | 実装 |
|---|---|
| 送信経路なし | 走査器にメール送信コードを持たせない。送信は別系統・人手承認後のみ |
| 本文を保存しない | 本文は先頭数KBだけ案件語の一致数を数えて破棄。DB に本文を書かない |
| 自分との実在の関係のみ | 「自分が返信した」「往復がある」を軸に採点。片道の受信だけは低優先 |
| 機械送信を除外 | no-reply / mailer-daemon、メルマガ(List-Unsubscribe / Precedence: bulk)、自動送信(Auto-Submitted)はハード除外 |
| 出所の記録 | 連絡先ごとに往復サマリ(受信/送信数・最終日)を source として保存 |
| 閉域 | DB は _prospect/data/mailbox.db。/_prospect/ は gitignore。commit / deploy しない |
1. 閉域ディレクトリ(collect-web-leads と同居)
_prospect/ を再利用する(未作成なら collect-web-leads の手順1)。 保存は Node 22.5+ 内蔵の node:sqlite、実行は node --experimental-sqlite。web 収集の DB とは 別ファイル mailbox.db に書く(スキーマ層 lib/db.mjs は共有し、openDb(path) の引数で切替)。
2. mbox を入手(Google Takeout)
対象アカウントで takeout.google.com → メールのみ選択 → 「すべての mbox データ」→ エクスポート → 解凍して .mbox を用意。 全期間・全件が確実に取れる唯一の経路(API の OAuth 設定が要らない)。
3. 全走査
scan-mailbox.mjs を実行(--me は自分のアドレス。エイリアスは繰り返し、社内ドメインは --me-domain):
node --experimental-sqlite _prospect/scan-mailbox.mjs \
--me you@your.co --me-domain your.co "All mail Including Spam and Trash.mbox"
mbox を ストリームで1通ずつパースし、ヘッダから差出人/宛先/日付/件名を取り出す (MIME encoded-word はデコード)。方向(自分発 / 相手発)を判定し、相手アドレスごとに 送信数・受信数・最終日・案件語ヒットを集計 → スコア → mailbox.db の企業/メール表に upsert。 まず --dry で件数と上位だけ確認できる。
4. 判定ルール(ニーズ精査の真実源)
config.json の mailboxKeywords(案件語辞書)と mailboxScoring(重み)だけを編集して調整する (ハードコード禁止)。スコアは透明な加算 → 0〜100 にクランプ:
- 自分が返信している(双方向)…
replyBonus(最強シグナル) - 往復回数 min(送信,受信) …
perRoundtrip×回数(上限つき) - 法人ドメイン(フリー/キャリアメール以外)…
corporateDomainBonus - 現役(最終やり取りが
recencyWithinDays以内)…recencyWithinDaysBonus - 案件語(見積・発注・契約・請求・納品・撮影・制作 …)… 語ごとの重み(反復は +1/回・上限つき)
強度は --strict(往復2回以上 or 案件語つき往復)/ 既定=標準(法人=受信実績あり / フリーメール=受信かつ案件語)/ --broad(片道でも双方向実績があれば)。閾値で自動却下はせず、最終判断は人が下す。
5. 承認(localhost 専用 UI)
review.mjs を mailbox モードで起動(web 収集の 4599 とは別ポート 4600・127.0.0.1 のみ):
node --experimental-sqlite _prospect/review.mjs --db mailbox # → http://127.0.0.1:4600
スコア降順に、会社(ドメイン)/ 相手アドレス+人名 / シグナル / 往復サマリを見て 承認 / 却下。 非ループバックは 403。localhost 専用なので外部に露出しない。
6. 書き出す → 取込器へ
node --experimental-sqlite _prospect/export-approved.mjs --db mailbox
# → _prospect/data/mailbox-export.csv (email,name,source_url) ※人名つき・往復サマリを出所に
node scripts/import-inquiries.mjs _prospect/data/mailbox-export.csv --source email --apply
取込器側で PII は AES-256-GCM 暗号化、source=email / 同意根拠 prior_correspondence で記録され、 以後は unsubscribe / suppression の管轄下に入る。
7. 送信は別工程
連絡先化しても、送信は必ず run-newsletter-ops のキャンペーン作成を経る。 初回は既存取引の文脈を明記し、cold ではなく既知の関係者として、送信者表示+配信解除リンクを必須にする。
検証(acid test)
- 合成 mbox(本命クライアントの往復 / 法人・受信のみ / フリーメール+案件語 / 私信 / メルマガ / no-reply / cold 発信)を
1本作り、scan-mailbox.mjs --dry で メルマガ・no-reply が除外され、往復のある法人が高スコア、 私信フリーメールが除外されることを確認。
--strictで受信のみが落ち、--broadで片道 cold が拾われる(強度が効く)。review.mjs --db mailboxが 127.0.0.1:4600 で起動し承認が round-trip、非ループバックが 403。export-approved.mjs --db mailboxが人名つき CSV を書き、import-inquiries.mjs --source email(dry-run)が
PII をマスク表示。git check-ignore _prospect/ が効き git status に一切現れないこと。