← back to Rentv Adintel

scripts/research.js

135 lines

#!/usr/bin/env node
'use strict';
/**
 * CLI stub: node scripts/research.js <california|arizona|conferences>
 *
 * Wires to src/search/provider + src/adapters/manual-review IF present
 * (guarded with try/require). When modules are unavailable, prints the
 * manual search URLs that a human researcher should open instead.
 *
 * This is intentionally conservative — no live fetching from this stub
 * (spec §6: no bypassing auth, robots, rate limits, or anti-bot controls).
 *
 * @module scripts/research
 */

require('../lib/env');

const MARKET = (process.argv[2] || '').toLowerCase();

if (!['california', 'arizona', 'conferences'].includes(MARKET)) {
  console.error('Usage: node scripts/research.js <california|arizona|conferences>');
  console.error('Valid markets: california, arizona, conferences');
  process.exit(1);
}

// Try to load the real search provider module (may not exist yet)
let provider = null;
try {
  provider = require('../src/search/provider');
} catch (_) {
  // Not yet built — fall through to manual guidance
}

// Try to load the manual-review adapter
let manualReview = null;
try {
  manualReview = require('../src/adapters/manual-review');
} catch (_) {
  // Not yet built — fall through
}

// ---------------------------------------------------------------------------
// Manual research URL generators
// ---------------------------------------------------------------------------

/**
 * Build the list of recommended manual search URLs for a given market.
 * @param {'california'|'arizona'|'conferences'} market
 * @returns {string[]}
 */
function manualSearchUrls(market) {
  const rentv = 'https://rentv.com';
  const rentvReview = 'https://rentvreview.com';

  if (market === 'california') {
    return [
      `${rentv}/sponsors — RENTV CA sponsor pages`,
      `${rentvReview}/advertisers — RENTV Review CA advertisers`,
      'https://www.naiop.org/chapters/socal/events — NAIOP SoCal events & sponsors',
      'https://www.boma.org/BOMA/Chapters/Greater_Los_Angeles/Events.aspx — BOMA LA sponsors',
      'https://sccai.org/events — SCCAI OC/IE events',
      'https://www.sfbart.com — SF Bay Area CRE events',
      'Manual: search Google CSE for site:rentv.com "sponsor" OR "advertiser" california',
    ];
  }

  if (market === 'arizona') {
    return [
      `${rentv}/arizona — RENTV AZ market page`,
      'https://www.naiop.org/chapters/az/events — NAIOP AZ events & sponsors',
      'https://www.boma.org/BOMA/Chapters/Phoenix/Events.aspx — BOMA Phoenix sponsors',
      'https://www.sior.com/events — SIOR Phoenix chapter',
      'Manual: search Google CSE for site:rentv.com "sponsor" OR "advertiser" arizona',
    ];
  }

  // conferences
  return [
    'https://rentv.com/cre-talk — RENTV CRE Talk sponsors',
    'https://www.naiop.org/events — NAIOP national conferences',
    'https://www.bisnow.com/los-angeles/events — Bisnow LA sponsors',
    'https://www.globest.com/events — GlobeSt CRE conference list',
    'https://cretech.com/events — CREtech tech conference sponsors',
    'https://www.corecnet.org/events — CoreNet Global chapters',
    'Manual: search for "[event name] sponsors [year]" on Google',
  ];
}

// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------

(async () => {
  console.log(`\n[research] Market: ${MARKET}`);
  console.log('[research] Spec §6 compliance: no automated scraping of prohibited hosts.\n');

  if (provider && typeof provider.runResearch === 'function') {
    // Real provider is wired — delegate to it
    console.log('[research] Using installed search provider:', provider.name || 'unknown');
    try {
      const result = await provider.runResearch({ market: MARKET });
      console.log('[research] Results:', JSON.stringify(result, null, 2));
    } catch (err) {
      console.error('[research] Provider error:', err.message);
      process.exit(1);
    }
    return;
  }

  if (manualReview && typeof manualReview.queueManualSearch === 'function') {
    // Manual review adapter is wired — queue the search
    console.log('[research] Manual review adapter found — queueing search items.');
    try {
      const queued = await manualReview.queueManualSearch({ market: MARKET });
      console.log('[research] Queued', queued.count, 'manual review items.');
    } catch (err) {
      console.error('[research] Manual review adapter error:', err.message);
    }
  }

  // Guidance output (always printed as a fallback / supplemental)
  console.log('[research] Research modules not yet fully available.');
  console.log('[research] Open these URLs manually in your browser to find advertiser evidence:\n');
  const urls = manualSearchUrls(MARKET);
  urls.forEach((u, i) => console.log(`  ${i + 1}. ${u}`));

  console.log('\n[research] After reviewing each page:');
  console.log('  - Use the admin UI at /advertisers/new to add organizations.');
  console.log('  - Use /ads/new to record ad sightings with source URLs.');
  console.log('  - Use /events to record conference sponsor information.');
  console.log('[research] All entries require a source_page_url and observed_at date.');

  process.exit(0);
})();