#!/usr/bin/env python3 """Render a mobile-friendly HTML digest and a Discord-friendly markdown digest.""" from __future__ import annotations import argparse import json from collections import defaultdict from pathlib import Path from typing import Any, Dict, List from fetch_arxiv import DOMAIN_CONFIGS from utils import SKILL_DIR, format_authors, html_escape, normalize_space, now_local, read_json, write_text DOMAIN_ORDER = ["embodied", "representation", "reinforcement"] TEMPLATE_PATH = SKILL_DIR / "assets" / "mobile_digest_template.html" def render_tag(tag: str) -> str: return f'#{html_escape(tag)}' def render_link(label: str, url: str) -> str: if not url: return "" safe_label = html_escape(label) safe_url = html_escape(url) return f'{safe_label}' def render_paper_card(paper: Dict[str, Any]) -> str: domain_label = DOMAIN_CONFIGS[paper["domain"]]["label_zh"] tags_html = "".join(render_tag(tag) for tag in paper.get("tags", [])) links_html = "".join( item for item in [ render_link("打开 DOI", paper.get("doi_url", "")), render_link("打开 arXiv", paper.get("abs_url", "")), render_link("打开 PDF", paper.get("pdf_url", "")), ] if item ) authors = html_escape(format_authors(paper.get("authors", []), limit=4)) return f""" {html_escape(domain_label)} 综合分 {paper.get('score_total', 0):.1f} {html_escape(paper.get('published_local', '')[:10])} {html_escape(paper.get('title', ''))} {html_escape(paper.get('brief_explanation_zh', ''))} {tags_html} 作者:{authors} arXiv:{html_escape(paper.get('arxiv_id', ''))} 入选原因:{html_escape(paper.get('selection_reason', ''))} 中文摘要 {html_escape(paper.get('translated_abstract_zh', ''))} 原文摘要 {html_escape(paper.get('summary', ''))} {links_html} """.strip() def render_sections(papers: List[Dict[str, Any]]) -> Dict[str, str]: grouped: Dict[str, List[Dict[str, Any]]] = defaultdict(list) for paper in papers: grouped[paper["domain"]].append(paper) nav_parts: List[str] = [] section_parts: List[str] = [] for domain in DOMAIN_ORDER: domain_papers = grouped.get(domain, []) if not domain_papers: continue label = DOMAIN_CONFIGS[domain]["label_zh"] nav_parts.append(f'{html_escape(label)} · {len(domain_papers)} 篇') cards_html = "\n".join(render_paper_card(paper) for paper in domain_papers) section_parts.append( f""" {html_escape(label)} {len(domain_papers)} 篇 {cards_html} """.strip() ) return {"nav": "".join(nav_parts), "sections": "\n".join(section_parts)} def render_html(payload: Dict[str, Any]) -> str: template = TEMPLATE_PATH.read_text(encoding="utf-8") papers = payload.get("papers", []) rendered = render_sections(papers) intro = f"{now_local().strftime('%Y-%m-%d')} · 具身智能 / 表征学习 / 强化学习 · 每个方向 2-3 篇偏应用候选。点开卡片即可看中文摘要、原文摘要与 DOI 链接。" replacements = { "{{date}}": now_local().strftime("%Y-%m-%d"), "{{intro}}": html_escape(intro), "{{nav}}": rendered["nav"], "{{sections}}": rendered["sections"], "{{generated_at}}": html_escape(now_local().strftime("%Y-%m-%d %H:%M %Z")), } html = template for placeholder, value in replacements.items(): html = html.replace(placeholder, value) return html def render_markdown(payload: Dict[str, Any]) -> str: lines: List[str] = [] lines.append(f"# RobotDaily | {now_local().strftime('%Y-%m-%d')}") lines.append("") lines.append("具身智能 / 表征学习 / 强化学习,每个方向 2-3 篇偏应用候选。") lines.append("") for domain in DOMAIN_ORDER: papers = [paper for paper in payload.get("papers", []) if paper.get("domain") == domain] if not papers: continue lines.append(f"## {DOMAIN_CONFIGS[domain]['label_zh']}({len(papers)} 篇)") lines.append("") for idx, paper in enumerate(papers, start=1): tags = " ".join(f"`{tag}`" for tag in paper.get("tags", [])) lines.extend( [ f"### {idx}. {paper.get('title', '')}", f"- 作者:{format_authors(paper.get('authors', []), limit=4)}", f"- 亮点:{paper.get('brief_explanation_zh', '')}", f"- 标签:{tags}", f"- DOI:{paper.get('doi_url', '')}", f"- arXiv:{paper.get('abs_url', '')}", f"- PDF:{paper.get('pdf_url', '')}", "", ] ) return "\n".join(lines).strip() + "\n" def main() -> None: parser = argparse.ArgumentParser(description="Render RobotDaily digest HTML/markdown") parser.add_argument("--input", required=True) parser.add_argument("--html-output", default="") parser.add_argument("--md-output", default="") args = parser.parse_args() payload = read_json(args.input, default={}) or {} html = render_html(payload) markdown = render_markdown(payload) if args.html_output: write_text(args.html_output, html) else: print(html) if args.md_output: write_text(args.md_output, markdown) if __name__ == "__main__": main()
{html_escape(paper.get('brief_explanation_zh', ''))}
{html_escape(paper.get('translated_abstract_zh', ''))}
{html_escape(paper.get('summary', ''))}