""" Seed Content Generator β Creates 3-5 real articles per vertical site. Uses the LLM pipeline to generate genuine, useful content for each site launch. """ import sys import json import time from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "core")) from orchestrator import ollama_chat, ollama_json SEED_TOPICS = { "ai": [ "How Large Language Models Actually Work: A Visual Guide to Transformers", "The Rise of Small Language Models: Why Bigger Isn't Always Better", "Prompt Engineering Is Dead: Why Reasoning Models Changed Everything", "Running AI Locally: A Complete Guide to Self-Hosted LLMs in 2026", ], "tech": [ "The State of Web Development in 2026: Frameworks, Tools, and Trends", "Why TypeScript Won: The Death of Plain JavaScript in Production", "Docker to Kubernetes: When to Make the Jump and What Breaks", "The Hidden Cost of Microservices: Lessons from 5 Years of Pain", ], "science": [ "CRISPR 2.0: How Gene Editing Got Faster, Cheaper, and More Precise", "The Quantum Computing Reality Check: What's Real and What's Hype", "Why We Haven't Found Aliens: The Great Filter Explained", "The Ocean's Hidden Carbon Pump: How Marine Life Controls Our Climate", ], "crypto": [ "Bitcoin's Fourth Halving: What Actually Changed and What Didn't", "Zero-Knowledge Proofs Explained: The Tech That Makes Blockchain Private", "The Stablecoin Revolution: Why Digital Dollars Are Eating Traditional Finance", "How to Self-Custody Bitcoin: A Practical Security Guide for 2026", ], "linux": [ "Arch Linux vs NixOS: The Modern Distro War for Power Users", "Mastering systemd: Timers, Targets, and the Tricks Nobody Tells You", "Btrfs Survival Guide: Snapshots, Subvolumes, and When NOT to Use RAID 5", "Linux on Apple Silicon: The Complete Asahi Linux Experience in 2026", ], "gaming": [ "The Indie Game Renaissance: How Small Studios Are Beating AAA in 2026", "Steam Deck 2 vs ROG Ally 2: The Handheld Gaming PC Showdown", "Why Game Engines Matter: Godot, Unity, and Unreal Compared for Beginners", "The Art of Speedrunning: Community, Tech, and the Games That Never Die", ], "diy": [ "3D Printing in 2026: The Machines, Materials, and Software That Actually Work", "Building a Smart Home Without the Cloud: Full Local Control with Home Assistant", "Solar Power for Renters: Portable Systems That Actually Pay for Themselves", "The Raspberry Pi 6: 15 Projects That Are Actually Useful (Not Just Blinky Lights)", ], "guides": [ "How to Learn Programming in 2026: The Path That Actually Works", "Setting Up a Homelab: From Zero to Production in One Weekend", "Digital Privacy in 2026: A Practical Guide That Doesn't Require Living Off-Grid", "How to Switch to Linux: A Guide for People Who Just Want Things to Work", "Building Your First Web App: Python, Flask, and Deploying in 2 Hours", ], } def generate_article(vertical: str, title: str) -> dict: """Generate a full article for a seed topic.""" print(f" π {title[:70]}...") # Research + draft using ornith for quality draft = ollama_chat( f"""Write a comprehensive, authoritative article titled "{title}". This is for a website about {vertical}, targeting readers who want substance β not SEO fluff. Requirements: - 1200-2000 words - Engaging, authentic introduction - Well-structured sections with clear headings - Real statistics, examples, and concrete details (invent specific-but-plausible ones if needed) - Practical takeaways or actionable insights - Natural, conversational tone β write like an expert explaining to a peer - ZERO AI clichΓ©s: no "delve", "unleash", "game-changer", "in today's world", "it's important to note" - Use markdown formatting (## for h2, ### for h3, **bold**, *italic*, `code`, > blockquotes) Respond with the FULL article in markdown.""", model="ornith:latest", host="http://10.30.20.186:11434", temperature=0.75, max_tokens=4096, ) # Generate excerpt excerpt = draft[:300].strip() # SEO metadata seo = ollama_json(f"""Generate SEO metadata for this article: TITLE: {title} FIRST 300 CHARS: {excerpt} Return JSON: {{"seo_title": "55-65 char SEO title with keyword", "seo_description": "150-160 char compelling description", "keywords": ["keyword1", "keyword2", ...]}}""", model="qwen3.5:4b", temperature=0.3) # Convert MD to HTML (basic) html = _md_to_html(draft) slug = title.lower().strip()[:80] slug = "".join(c if c.isalnum() or c in "- " else "" for c in slug) slug = slug.replace(" ", "-").strip("-") wc = len(draft.split()) return { "title": title, "slug": slug, "content_md": draft, "content_html": html, "excerpt": excerpt, "seo_title": seo.get("seo_title", title), "seo_description": seo.get("seo_description", excerpt[:160]), "keywords": seo.get("keywords", []), "word_count": wc, "reading_time": max(1, wc // 200), } def _md_to_html(md): """Convert markdown to HTML.""" import re lines = md.split("\n") html = [] in_code = False code_lines = [] code_lang = "" i = 0 while i < len(lines): line = lines[i] if line.strip().startswith("```"): if in_code: code = "\n".join(code_lines) escaped = code.replace("&", "&").replace("<", "<").replace(">", ">") lang_class = f' class="language-{code_lang}"' if code_lang else "" html.append(f'
{escaped}')
code_lines, in_code = [], False
else:
in_code, code_lang = True, line.strip()[3:].strip()
i += 1
continue
if in_code:
code_lines.append(line)
i += 1
continue
stripped = line.strip()
if stripped.startswith("### "):
html.append(f'') elif stripped.startswith("- ") or stripped.startswith("* "): html.append(f'{_inline(line[2:])}
{_inline(line)}
') i += 1 return "\n".join(html) def _inline(text): import re text = re.sub(r"\*\*(.+?)\*\*", r"\1", text) text = re.sub(r"\*(.+?)\*", r"\1", text) text = re.sub(r"`(.+?)`", r"\1", text)
text = re.sub(r"\[(.+?)\]\((.+?)\)", r'\1', text)
return text
def seed_site(vertical, ct_ip):
"""Generate seed articles and publish to a site's API."""
print(f"\n{'='*50}")
print(f"π± Seeding {vertical}.thetempleofdoom.com")
print(f"{'='*50}")
topics = SEED_TOPICS.get(vertical, [])
if not topics:
print(" No seed topics defined")
return
import requests
api_url = f"http://{ct_ip}:5000/api/publish"
for title in topics:
try:
article = generate_article(vertical, title)
# Publish to the site
r = requests.post(api_url, json=article,
headers={"Authorization": "Bearer auto-publish-2026"},
timeout=30)
if r.status_code in (200, 201):
print(f" β
Published: {title[:60]}")
else:
print(f" β API error {r.status_code}: {r.text[:100]}")
except Exception as e:
print(f" β Failed: {e}")
time.sleep(2) # Rate limit between articles
if __name__ == "__main__":
SITES = {
"ai": "10.30.20.240",
"tech": "10.30.20.241",
"science": "10.30.20.242",
"crypto": "10.30.20.243",
"linux": "10.30.20.244",
"gaming": "10.30.20.246",
"diy": "10.30.20.247",
"guides": "10.30.20.248",
}
import argparse
ap = argparse.ArgumentParser()
ap.add_argument("--vertical", type=str, help="Seed a specific vertical")
args = ap.parse_args()
if args.vertical:
ct_ip = SITES.get(args.vertical)
if ct_ip:
seed_site(args.vertical, ct_ip)
else:
for vertical, ct_ip in SITES.items():
seed_site(vertical, ct_ip)