← back to AbramsOS

scripts/backfill-mode-claims.js

47 lines

#!/usr/bin/env node
// Parse the Mode newsletter corpus → distinct, deduped, ranked settlement list.
// DB-free: reads data/mode-emails-raw.js, writes data/mode-claims-backfill.json.
// Usage: node scripts/backfill-mode-claims.js [YYYY-MM-DD as "today"]
const fs = require('fs');
const path = require('path');
const { parseModeEmail } = require('../lib/mode-claims');

const emails = require('../data/mode-emails-raw.js');
const TODAY = process.argv[2] || new Date().toISOString().slice(0, 10);

const bySlug = new Map();
for (const e of emails) {
  for (const s of parseModeEmail(e.body, { emailId: e.id, emailDate: e.date })) {
    const prev = bySlug.get(s.slug);
    // keep the earliest-seen instance but prefer one with a deadline + richer payout
    if (!prev || (!prev.deadline && s.deadline) || (s.payout_max_cents || 0) > (prev.payout_max_cents || 0)) {
      bySlug.set(s.slug, { ...prev, ...s, seen_count: (prev?.seen_count || 0) + 1 });
    } else {
      prev.seen_count = (prev.seen_count || 0) + 1;
    }
  }
}

const all = [...bySlug.values()];
const open = all.filter(s => !s.deadline || s.deadline >= TODAY);
const expired = all.filter(s => s.deadline && s.deadline < TODAY);

// rank: no-claim-required (free money) first, then by payout ceiling desc, then soonest deadline
const rank = (a, b) =>
  (b.no_claim_required - a.no_claim_required) ||
  ((b.payout_max_cents || 0) - (a.payout_max_cents || 0)) ||
  String(a.deadline || '9999').localeCompare(String(b.deadline || '9999'));
open.sort(rank);

const out = { generated_at: new Date().toISOString(), today: TODAY, corpus_emails: emails.length,
  distinct_settlements: all.length, open: open.length, expired: expired.length, settlements: open };
fs.writeFileSync(path.join(__dirname, '..', 'data', 'mode-claims-backfill.json'), JSON.stringify(out, null, 2));

const money = c => c == null ? '—' : '$' + (c / 100).toLocaleString('en-US');
console.log(`Corpus ${emails.length} emails → ${all.length} distinct settlements  |  OPEN ${open.length}  EXPIRED ${expired.length}\n`);
console.log('OPEN (deadline ≥ ' + TODAY + '), ranked:');
for (const s of open) {
  const tag = s.no_claim_required ? 'AUTO ' : (s.proof_required === false ? 'NOPROOF ' : (s.proof_required ? 'PROOF ' : ''));
  console.log(`  [${(s.deadline||'—')}] ${money(s.payout_max_cents).padStart(9)}  ${tag}${s.name}`);
}