code icon Code

Fetch Published Letter

Retrieve content from a published letter URL

Source Code

const [url] = process.argv.slice(2);

if (!url) {
  console.error("Error: url is required");
  process.exit(1);
}

console.log(`Fetching published letter from: ${url}`);

try {
  const response = await fetch(url);
  
  if (!response.ok) {
    if (response.status === 404) {
      console.error("Letter not found. It may not have deployed yet (GitHub Pages can take 1-2 minutes).");
    } else {
      console.error(`Failed to fetch: ${response.status} ${response.statusText}`);
    }
    process.exit(1);
  }

  const html = await response.text();

  // Extract title from <title> tag
  const titleMatch = html.match(/<title>([^<]+)<\/title>/);
  const title = titleMatch ? titleMatch[1] : "";

  // Extract content from .content div
  const contentMatch = html.match(/<div class="content">([\s\S]*?)<\/div>\s*<\/article>/);
  const contentHtml = contentMatch ? contentMatch[1].trim() : "";

  // Extract author from meta tag
  const authorMatch = html.match(/<meta name="author" content="([^"]+)">/);
  const author = authorMatch ? authorMatch[1] : "";

  // Extract date from .meta span (second span if author exists)
  const metaMatch = html.match(/<div class="meta">([\s\S]*?)<\/div>/);
  let date = "";
  if (metaMatch) {
    const spans = metaMatch[1].match(/<span>([^<]+)<\/span>/g);
    if (spans) {
      // If author exists, date is second span; otherwise first
      date = author && spans.length > 1 
        ? spans[1].replace(/<\/?span>/g, "")
        : spans[0].replace(/<\/?span>/g, "");
    }
  }

  // Detect style from CSS variables or colors
  let style = "modern"; // default
  if (html.includes("#faf8f5") || html.includes("Georgia, 'Times New Roman'")) {
    style = "classic";
  } else if (html.includes("#fcfcfa") || html.includes("Source Serif Pro")) {
    style = "serif";
  }

  // Extract header image if present
  const imageMatch = html.match(/<img src="([^"]+)" alt="" class="header-image">/);
  const imageUrl = imageMatch ? imageMatch[1] : "";

  console.log(`  Title: ${title}`);
  console.log(`  Style: ${style}`);
  console.log(`  Author: ${author || "(none)"}`);
  console.log(`  Date: ${date || "(none)"}`);
  console.log(`  Has image: ${imageUrl ? "yes" : "no"}`);
  console.log(`  Content length: ${contentHtml.length} chars`);

  console.log("\n--- RESULT ---");
  console.log(JSON.stringify({
    success: true,
    title,
    contentHtml,
    style,
    author,
    date,
    imageUrl,
    url,
  }, null, 2));

} catch (error) {
  console.error(`Failed to fetch letter: ${error.message}`);
  process.exit(1);
}