from __future__ import annotations import html import ipaddress import re import socket import time import xml.etree.ElementTree as ET from typing import Any from urllib.parse import urlparse import httpx _FEED_CACHE: dict[str, Any] = {"timestamp": 0.0, "items": []} CACHE_TTL_SECONDS = 600 RSS_SOURCES = [ { "name": "Ars Technica", "url": "https://feeds.arstechnica.com/arstechnica/technology-lab", "default_category": "Systems & Cloud", }, { "name": "Hacker News", "url": "https://hnrss.org/frontpage?points=120&count=12", "default_category": "DevTools & Engineering", }, { "name": "The Verge Tech", "url": "https://www.theverge.com/rss/tech/index.xml", "default_category": "Product & Startups", }, ] CURATED_TECH_STORIES: list[dict[str, Any]] = [ { "id": "curated-ai-agent-evals", "headline": "Why Engineering Teams Are Shifting From Prompt Tweaking to Deterministic Agent Evals", "summary": "As coding and workflow agents move into production pipelines, teams are discovering that reliability comes from sandboxes, tool guardrails, and automated regression suites rather than longer system prompts.", "source": "Tech Presenter Briefing", "category": "AI & Models", "published_label": "Featured Briefing", "url": "", "talking_points": [ "Single-turn chat demos hide compounding multi-step tool errors.", "Production teams treat agent trajectories like CI pipelines with pass/fail assertions.", "Fast feedback loops and sandboxing beat brittle prompt engineering.", ], }, { "id": "curated-sqlite-edge-renaissance", "headline": "The Modern SQLite & Single-Node Renaissance in High-Throughput Web Apps", "summary": "With NVMe storage, WAL concurrency, and read-replica streaming tools, single-node and embedded databases now handle workloads that teams previously assumed required distributed clusters.", "source": "Systems Architecture Desk", "category": "Systems & Cloud", "published_label": "Featured Briefing", "url": "", "talking_points": [ "Eliminating network hops between app and data slashes tail latency to microseconds.", "Operational simplicity means fewer 3 a.m. distributed consensus failures.", "Know the exact write-contention threshold where horizontal Postgres sharding still wins.", ], }, { "id": "curated-on-device-speech-vision", "headline": "Browser-Native ML: Running Vision & Audio Analytics Locally With WebAssembly and WebGPU", "summary": "Modern browsers now run real-time face landmarking, audio signal analysis, and compact speech models directly on user devices—preserving privacy while cutting cloud inference bills to zero.", "source": "Web Platform Digest", "category": "DevTools & Engineering", "published_label": "Featured Briefing", "url": "", "talking_points": [ "Zero network round-trip enables 30–60 FPS interactive coaching feedback.", "Sensitive camera and microphone streams stay private by default.", "Hybrid architectures combine instant browser cues with optional deeper server models.", ], }, { "id": "curated-passkeys-security", "headline": "Passkeys & Hardware-Bound Credentials Are Finally Replacing Phishable Passwords at Scale", "summary": "Major consumer and developer platforms report dramatic drops in account takeovers and sign-in friction after adopting WebAuthn passkeys synced across desktop and mobile devices.", "source": "Security Engineering Brief", "category": "Security & Privacy", "published_label": "Featured Briefing", "url": "", "talking_points": [ "Cryptographic origin binding makes lookalike phishing domains useless.", "Biometric sign-in cuts login time by more than half on mobile browsers.", "Cross-ecosystem recovery UX remains the critical detail product teams must nail.", ], }, { "id": "curated-small-teams-leverage", "headline": "How Tiny Product-Minded Engineering Teams Ship Like 50-Person Organizations", "summary": "Armed with typed monorepos, automated preview environments, and AI-assisted testing, lean 4-to-8 engineer teams are shipping polished full-stack products at unprecedented velocity.", "source": "Builder & Startup Desk", "category": "Product & Startups", "published_label": "Featured Briefing", "url": "", "talking_points": [ "Communication overhead scales quadratically; small senior teams keep context unified.", "Shared TypeScript/Python contracts eliminate integration drift.", "Clear product taste and storytelling have become the true competitive moat.", ], }, { "id": "curated-inference-economics", "headline": "The New Unit Economics of AI Features: Latency Budgets, Caching, and Model Routing", "summary": "Smart AI products no longer route every user click to the largest frontier model; instead, they combine semantic caching, compact specialist models, and frontier escalation only when needed.", "source": "AI Infrastructure Brief", "category": "AI & Models", "published_label": "Featured Briefing", "url": "", "talking_points": [ "Users abandon interactive UI features when latency exceeds 2 seconds.", "Routing 80% of routine tasks to compact models cuts cost by 5–10x.", "Designing transparent progressive UI states turns wait time into perceived intelligence.", ], }, ] STORY_FRAMEWORKS: dict[str, dict[str, Any]] = { "briefing_60s": { "id": "briefing_60s", "label": "60s Breaking Tech Briefing", "duration_sec": 60, "mode": "Storytelling", "goal": "Improve pace", "description": "Punchy creator/anchor update: Headline Hook → What Changed → Why Builders Care → Sharp Closing Take.", }, "explainer_2m": { "id": "explainer_2m", "label": "2-Min Deep-Dive Explainer", "duration_sec": 120, "mode": "Storytelling", "goal": "Stronger eye contact", "description": "Educational breakdown: Relatable Problem → Under-the-Hood Mechanism → Visual Analogy → Practical Impact.", }, "contrarian_take": { "id": "contrarian_take", "label": "90s Contrarian Tech Take", "duration_sec": 90, "mode": "Debate", "goal": "Reduce fillers", "description": "Opinionated commentary: Conventional Hype → Where Reality Differs → Concrete Evidence → Nuanced Prediction.", }, "war_story": { "id": "war_story", "label": "2-Min Builder Lesson & Story", "duration_sec": 120, "mode": "Pitch", "goal": "Stronger eye contact", "description": "Narrative arc connecting news to real engineering: The Stakes → The Tradeoff → How to Apply It Monday Morning.", }, } def _strip_html(raw: str) -> str: no_tags = re.sub(r"<[^>]+>", " ", raw or "") unescaped = html.unescape(no_tags) return re.sub(r"\s+", " ", unescaped).strip() def _infer_category(title: str, summary: str, fallback: str) -> str: blob = f"{title} {summary}".lower() if any(k in blob for k in ("ai", "llm", "model", "gpt", "claude", "gemini", "neural", "inference", "agent")): return "AI & Models" if any(k in blob for k in ("security", "vulnerability", "cve", "passkey", "encryption", "breach", "privacy", "zero-day")): return "Security & Privacy" if any(k in blob for k in ("cloud", "linux", "kernel", "postgres", "sqlite", "kubernetes", "server", "cpu", "gpu", "chip")): return "Systems & Cloud" if any(k in blob for k in ("rust", "typescript", "python", "compiler", "git", "open source", "api", "browser", "ide")): return "DevTools & Engineering" if any(k in blob for k in ("startup", "launch", "funding", "apple", "product", "design", "market")): return "Product & Startups" return fallback def _parse_feed_xml(xml_text: str, source_name: str, default_category: str) -> list[dict[str, Any]]: items: list[dict[str, Any]] = [] try: root = ET.fromstring(xml_text) except ET.ParseError: return items # RSS 2.0 for idx, item in enumerate(root.findall(".//item")[:8]): title = _strip_html(item.findtext("title") or "") desc = _strip_html(item.findtext("description") or "") link = (item.findtext("link") or "").strip() if not title or len(title) < 12: continue if "Comments URL:" in desc: desc = "Trending technical discussion on Hacker News — ideal for practicing concise synthesis and your own engineering point of view." summary = desc[:340] + ("..." if len(desc) > 340 else "") category = _infer_category(title, summary, default_category) items.append({ "id": f"live-{source_name.lower().replace(' ', '-')}-{idx}", "headline": title, "summary": summary or f"Fresh technology story from {source_name}. Frame what happened, why it matters, and your practical builder takeaway.", "source": source_name, "category": category, "published_label": "Live Feed", "url": link, "talking_points": _extract_talking_points(title, summary), }) # Atom ns = {"atom": "http://www.w3.org/2005/Atom"} for idx, entry in enumerate(root.findall(".//atom:entry", ns)[:8]): title = _strip_html(entry.findtext("atom:title", default="", namespaces=ns)) summary_raw = ( entry.findtext("atom:summary", default="", namespaces=ns) or entry.findtext("atom:content", default="", namespaces=ns) ) desc = _strip_html(summary_raw) link_el = entry.find("atom:link", ns) link = (link_el.attrib.get("href", "") if link_el is not None else "").strip() if not title or len(title) < 12: continue summary = desc[:340] + ("..." if len(desc) > 340 else "") category = _infer_category(title, summary, default_category) items.append({ "id": f"live-atom-{source_name.lower().replace(' ', '-')}-{idx}", "headline": title, "summary": summary or f"Latest technology report from {source_name}.", "source": source_name, "category": category, "published_label": "Live Feed", "url": link, "talking_points": _extract_talking_points(title, summary), }) return items def _extract_talking_points(headline: str, summary: str) -> list[str]: clean_summary = summary.strip() sentences = [s.strip() for s in re.split(r"(?<=[.!?])\s+", clean_summary) if len(s.strip()) > 25] points = [f"Core shift: {headline}"] if sentences: points.append(sentences[0][:160]) points.append("Builder lens: How does this change architecture, latency, cost, or user trust?") return points[:3] def fetch_tech_news_feed(category: str | None = None, force_refresh: bool = False) -> dict[str, Any]: now = time.time() live_items: list[dict[str, Any]] = [] if not force_refresh and _FEED_CACHE["items"] and (now - _FEED_CACHE["timestamp"] < CACHE_TTL_SECONDS): live_items = list(_FEED_CACHE["items"]) else: fetched: list[dict[str, Any]] = [] try: with httpx.Client(timeout=3.0, follow_redirects=True, headers={"User-Agent": "SpeechMachine-TechCoach/1.0"}) as client: for src in RSS_SOURCES: try: resp = client.get(src["url"]) if resp.status_code == 200 and resp.text: fetched.extend(_parse_feed_xml(resp.text, src["name"], src["default_category"])) except Exception: continue except Exception: fetched = [] if fetched: # Deduplicate by normalized headline seen: set[str] = set() deduped: list[dict[str, Any]] = [] for entry in fetched: norm = re.sub(r"\W+", "", entry["headline"].lower())[:60] if norm not in seen: seen.add(norm) deduped.append(entry) live_items = deduped[:15] _FEED_CACHE["timestamp"] = now _FEED_CACHE["items"] = live_items combined = live_items + CURATED_TECH_STORIES if category and category.lower() not in {"all", ""}: filtered = [item for item in combined if item["category"].lower() == category.lower()] else: filtered = combined categories = [ "All", "AI & Models", "Systems & Cloud", "DevTools & Engineering", "Security & Privacy", "Product & Startups", ] frameworks_out = [ { **fw, "target_seconds": fw["duration_sec"], "target_words": round((fw["duration_sec"] / 60) * 140), } for fw in STORY_FRAMEWORKS.values() ] stories_out = [ { **st, "published_at": st.get("published_label", "Today"), "is_live": str(st.get("id", "")).startswith("live-"), } for st in filtered[:24] ] return { "live_connected": len(live_items) > 0, "live_count": len(live_items), "categories": categories, "frameworks": frameworks_out, "stories": stories_out, } def _is_safe_public_url(url: str) -> bool: try: parsed = urlparse(url) if parsed.scheme not in {"http", "https"} or not parsed.hostname: return False hostname = parsed.hostname.lower() if hostname in {"localhost", "127.0.0.1", "0.0.0.0", "::1"} or hostname.endswith(".local"): return False infos = socket.getaddrinfo(hostname, None) for info in infos: ip_obj = ipaddress.ip_address(info[4][0]) if ip_obj.is_private or ip_obj.is_loopback or ip_obj.is_link_local or ip_obj.is_reserved: return False return True except Exception: return False def _try_fetch_article_excerpt(url: str) -> tuple[str | None, str | None]: if not url or not _is_safe_public_url(url): return None, None try: with httpx.Client(timeout=3.5, follow_redirects=True, headers={"User-Agent": "SpeechMachine-TechCoach/1.0"}) as client: resp = client.get(url) if resp.status_code != 200: return None, None raw_html = resp.text[:120000] title_match = re.search(r"]*>(.*?)", raw_html, flags=re.IGNORECASE | re.DOTALL) page_title = _strip_html(title_match.group(1)) if title_match else None paragraphs = [ _strip_html(m) for m in re.findall(r"]*>(.*?)

", raw_html, flags=re.IGNORECASE | re.DOTALL) ] substantive = [p for p in paragraphs if len(p) > 60][:4] excerpt = " ".join(substantive)[:700] if substantive else None return page_title, excerpt except Exception: return None, None def _extract_keywords(headline: str, summary: str) -> list[str]: stop = { "the", "and", "for", "with", "that", "this", "from", "into", "your", "are", "why", "how", "what", "when", "where", "over", "more", "than", "their", "they", "have", "has", "been", "will", "just", "new", "modern", "teams", "using", "about", } words = re.findall(r"[a-zA-Z][a-zA-Z0-9\-]{3,}", f"{headline} {summary}".lower()) picked: list[str] = [] for w in words: clean = w.strip("-") if clean not in stop and clean not in picked and len(clean) >= 4: picked.append(clean) if len(picked) >= 4: break return picked + ["because", "however"] def build_storyboard( headline: str, summary: str = "", source: str | None = None, url: str | None = None, category: str | None = "AI & Models", framework: str = "briefing_60s", personal_angle: str | None = None, ) -> dict[str, Any]: effective_headline = headline.strip() effective_summary = summary.strip() if url and not effective_summary: fetched_title, fetched_excerpt = _try_fetch_article_excerpt(url) if fetched_title and len(effective_headline) < 10: effective_headline = fetched_title if fetched_excerpt: effective_summary = fetched_excerpt if not effective_summary: effective_summary = ( f"This story explores {effective_headline} and how it reshapes engineering tradeoffs, " "product velocity, and real-world user outcomes." ) fw = STORY_FRAMEWORKS.get(framework, STORY_FRAMEWORKS["briefing_60s"]) angle_line = ( personal_angle.strip() if personal_angle and personal_angle.strip() else "For builders, the real signal is focusing on measurable reliability, latency, and user value over surface hype." ) keywords = _extract_keywords(effective_headline, effective_summary) if framework == "briefing_60s": sections = [ { "label": "1. Cold-Open Hook (0:00–0:10)", "text": f"Big shift in {category or 'tech'} today: {effective_headline}.", }, { "label": "2. What Just Happened (0:10–0:28)", "text": f"First, here is the core development: {effective_summary}", }, { "label": "3. Why Builders Should Care (0:28–0:46)", "text": "However, look past the headline—this directly changes how teams weigh speed, cost, and architecture decisions.", }, { "label": "4. My Sharp Takeaway (0:46–1:00)", "text": f"My take: {angle_line}", }, ] elif framework == "explainer_2m": sections = [ { "label": "1. Relatable Problem Hook (0:00–0:20)", "text": f"You may have seen the headline '{effective_headline}'—let us unpack what is actually happening under the hood.", }, { "label": "2. Under-the-Hood Mechanism (0:20–0:55)", "text": f"First, the technical context: {effective_summary}", }, { "label": "3. Plain-English Mental Model (0:55–1:30)", "text": "Second, think of this like upgrading from manual city streets to an automated express lane—throughput rises, however edge-case guardrails matter even more.", }, { "label": "4. Practical Builder Takeaway (1:30–2:00)", "text": f"Therefore, here is how I am thinking about applying this: {angle_line}", }, ] elif framework == "contrarian_take": sections = [ { "label": "1. Conventional Consensus (0:00–0:18)", "text": f"Everyone is reacting to '{effective_headline}' as if it changes everything overnight.", }, { "label": "2. Where Reality Differs (0:18–0:45)", "text": f"However, when you inspect the details—{effective_summary}—the real bottleneck is still integration and human workflow.", }, { "label": "3. Concrete Evidence (0:45–1:10)", "text": "First, tools evolve fast, yet production reliability and debugging discipline still determine who ships sustainably.", }, { "label": "4. Nuanced Prediction (1:10–1:30)", "text": f"So my prediction is simple: {angle_line}", }, ] else: # war_story sections = [ { "label": "1. The Stakes (0:00–0:20)", "text": f"Reading '{effective_headline}' reminded me of a critical engineering lesson every builder runs into eventually.", }, { "label": "2. The Core Tension (0:20–0:55)", "text": f"First, look at what happened here: {effective_summary}", }, { "label": "3. The Tradeoff Under Pressure (0:55–1:30)", "text": "However, in real production systems, every optimization introduces a tradeoff between simplicity, latency, and observability.", }, { "label": "4. Monday-Morning Principle (1:30–2:00)", "text": f"Therefore, the principle worth carrying into your own stack is this: {angle_line}", }, ] teleprompter_script = "\n\n".join(sec["text"] for sec in sections) bullet_cues = [f"{sec['label']}: {sec['text'][:115]}..." if len(sec["text"]) > 118 else f"{sec['label']}: {sec['text']}" for sec in sections] delivery_tips = [ "Lock your eyes onto the camera lens for the entire 1st section hook.", "Pause silently for 1 full beat right before 'However' and 'My take / Therefore'.", "Keep speaking cadence between 130–155 WPM so technical nouns land crisply.", ] sections_normalized = [ { "label": sec["label"], "text": sec["text"], "beat": sec["label"].split("(")[0].strip(), "timing": "(" + sec["label"].split("(")[1] if "(" in sec["label"] else "", "guidance": fw["description"], "script": sec["text"], } for sec in sections ] return { "headline": effective_headline, "summary": effective_summary, "source": source or "Custom Tech Story", "url": url or "", "category": category or "AI & Models", "framework": fw["id"], "framework_info": fw, "framework_label": fw["label"], "target_seconds": fw["duration_sec"], "target_words": round((fw["duration_sec"] / 60) * 140), "sections": sections_normalized, "teleprompter_script": teleprompter_script, "bullet_cues": bullet_cues, "target_keywords": keywords[:6], "delivery_tips": delivery_tips, "camera_tips": delivery_tips, }