diff --git a/daily/generate.py b/daily/generate.py index 0bdc6ca..dc3ecf6 100644 --- a/daily/generate.py +++ b/daily/generate.py @@ -489,7 +489,11 @@ def _format_skill_section(items: list[dict[str, Any]], *, hot: bool = False) -> return lines -def generate_report() -> tuple[str, str, Path, Path]: +def _collect(date_str: str) -> dict[str, Any]: + """抓取 skills/github/news 数据并归一化,计算 wecom 限额与周去重 recent keys。 + + 无副作用(不写快照/不记已推)。返回供 _select/_render 消费的 bundle。 + """ # Hot/Trending 前排同 source 极密,需更深抓取才能凑够展示用的唯一 source trending_n = env_int("DAILY_TRENDING_LIMIT", 400) hot_n = max(env_int("DAILY_HOT_LIMIT", 400), compare_depth()) @@ -570,11 +574,79 @@ def generate_report() -> tuple[str, str, Path, Path]: | recent_shown["github_emerging"] | recent_shown["github_topic"] ) + return { + "trending_n": trending_n, + "hot_n": hot_n, + "github_limit": github_limit, + "emerging_limit": emerging_limit, + "topic_limit": topic_limit, + "wecom_trending": wecom_trending, + "wecom_hot": wecom_hot, + "wecom_github": wecom_github, + "wecom_emerging": wecom_emerging, + "wecom_topic": wecom_topic, + "wecom_limits": wecom_limits, + "pool": pool, + "pad_pool": pad_pool, + "feed": feed, + "prev_ids": prev_ids, + "now": now, + "date_str": date_str, + "time_str": time_str, + "updated": updated, + "trending": trending, + "hot": hot, + "github_trending": github_trending, + "github_emerging": github_emerging, + "github_topic": github_topic, + "topic_name": topic_name, + "news_merged": news_merged, + "ai_news": ai_news, + "cn_ai_news": cn_ai_news, + "ai_news_research": ai_news_research, + "wecom_news": wecom_news, + "wecom_tech_news": wecom_tech_news, + "recent_shown": recent_shown, + "skill_recent": skill_recent, + "github_recent": github_recent, + } + + +def _select(c: dict[str, Any]) -> dict[str, Any]: + """全部「选择 + 数据突变」:9 处 board_select + featured_pick + LLM 步骤 + pad。 + + 时序约束内聚本段:featured pick 依赖 deep-pool;_localize 在 featured 后、 + pad 前执行;写回 shown keys/llm_input 也在此。_render 不再做任何选择。 + 副作用: apply_featured_pick 写回 shown keys(保持原时序)。 + """ + date_str = c["date_str"] + feed = c["feed"] + prev_ids = c["prev_ids"] + updated = c["updated"] + time_str = c["time_str"] + trending = c["trending"] + hot = c["hot"] + github_trending = c["github_trending"] + github_emerging = c["github_emerging"] + github_topic = c["github_topic"] + topic_name = c["topic_name"] + news_merged = c["news_merged"] + ai_news = c["ai_news"] + cn_ai_news = c["cn_ai_news"] + wecom_news = c["wecom_news"] + wecom_tech_news = c["wecom_tech_news"] + wecom_limits = c["wecom_limits"] + pool = c["pool"] + pad_pool = c["pad_pool"] + recent_shown = c["recent_shown"] + skill_recent = c["skill_recent"] + github_recent = c["github_recent"] + selected_trending = board_select( board="skills_trending", items=trending, recent_keys=skill_recent, - limit=wecom_trending, + limit=c["wecom_trending"], pool_size=pool, kind="skill", ) @@ -582,7 +654,7 @@ def generate_report() -> tuple[str, str, Path, Path]: board="skills_hot", items=hot, recent_keys=skill_recent, - limit=wecom_hot, + limit=c["wecom_hot"], pool_size=pool, kind="skill", ) @@ -590,7 +662,7 @@ def generate_report() -> tuple[str, str, Path, Path]: board="github_trending", items=github_trending, recent_keys=github_recent, - limit=wecom_github, + limit=c["wecom_github"], pool_size=pool, kind="github", ) @@ -599,7 +671,7 @@ def generate_report() -> tuple[str, str, Path, Path]: board="github_emerging", items=github_emerging, recent_keys=github_recent, - limit=wecom_emerging, + limit=c["wecom_emerging"], pool_size=pool, kind="github", ) @@ -608,7 +680,7 @@ def generate_report() -> tuple[str, str, Path, Path]: board="github_topic", items=github_topic, recent_keys=github_recent, - limit=wecom_topic, + limit=c["wecom_topic"], pool_size=pool, kind="github", ) @@ -741,6 +813,178 @@ def generate_report() -> tuple[str, str, Path, Path]: themes = theme_clusters(feed, theme_rules=THEME_RULES, skill_id_fn=_skill_id) + pick_src = trending[0].get("source", "") if trending else "" + pick_name = trending[0].get("title", "") if trending else "" + pick_command = pick_command_from_featured(featured) or ( + f"npx skills add {pick_src}/{pick_name}" + if pick_src and pick_name + else "npx skills add vercel-labs/skills/find-skills" + ) + pick_why = pick_why_from_featured(featured) or "" + pick_title = str((featured or {}).get("title") or pick_name or "").strip() + pick_url = str((featured or {}).get("url") or "").strip() + + gt = selected_trending + gh = selected_hot + gt_pad = board_select( + board="skills_trending", + items=trending, + recent_keys=skill_recent, + limit=pad_pool, + pool_size=pool, + kind="skill", + ) + gh_pad = board_select( + board="skills_hot", + items=hot, + recent_keys=skill_recent, + limit=pad_pool, + pool_size=pool, + kind="skill", + ) + wecom_github_items = [_prepare_github_item(item) for item in selected_github] + wecom_emerging_items = [_prepare_github_item(item) for item in selected_emerging] + wecom_topic_items = [_prepare_github_item(item) for item in selected_topic] + github_pad_recent = ( + recent_shown["github_trending"] + | recent_shown["github_emerging"] + | recent_shown["github_topic"] + ) + wecom_github_pad = [ + _prepare_github_item(item) + for item in board_select( + board="github_trending", + items=github_trending, + recent_keys=github_pad_recent, + limit=pad_pool, + pool_size=pool, + kind="github", + ) + ] + wecom_emerging_pad = [ + _prepare_github_item(item) + for item in board_select( + board="github_emerging", + items=github_emerging, + recent_keys=github_pad_recent, + limit=pad_pool, + pool_size=pool, + kind="github", + ) + ] + wecom_topic_pad = [ + _prepare_github_item(item) + for item in board_select( + board="github_topic", + items=github_topic, + recent_keys=github_pad_recent, + limit=pad_pool, + pool_size=pool, + kind="github", + ) + ] + delta_pad = eff_mode == "delta" and wecom_delta_pad() + board_kwargs = { + "mode": eff_mode, + "movement": movement, + "trending": gt, + "hot": gh, + "topic_name": topic_name, + "github_trending": wecom_github_items, + "github_emerging": wecom_emerging_items, + "github_topic": wecom_topic_items, + "wecom_trending": c["wecom_trending"], + "wecom_hot": c["wecom_hot"], + "wecom_github": c["wecom_github"], + "wecom_emerging": c["wecom_emerging"], + "wecom_topic": c["wecom_topic"], + "pad": delta_pad, + "date_str": date_str, + "trending_pad": gt_pad, + "hot_pad": gh_pad, + "github_trending_pad": wecom_github_pad, + "github_emerging_pad": wecom_emerging_pad, + "github_topic_pad": wecom_topic_pad, + } + return { + "selected_trending": selected_trending, + "selected_hot": selected_hot, + "boards_for_wecom": boards_for_wecom, + "llm_input": llm_input, + "featured": featured, + "movement": movement, + "eff_mode": eff_mode, + "push_gate": push_gate, + "wecom_ai": wecom_ai, + "wecom_cn": wecom_cn, + "agent_wecom": agent_wecom, + "editorial_theme": editorial_theme, + "editorial_highlights": editorial_highlights, + "themes": themes, + "pick_command": pick_command, + "pick_why": pick_why, + "pick_title": pick_title, + "pick_url": pick_url, + "board_kwargs": board_kwargs, + "prev_ids": prev_ids, + "gt": gt, + "gh": gh, + "wecom_github_items": wecom_github_items, + "wecom_emerging_items": wecom_emerging_items, + "wecom_topic_items": wecom_topic_items, + } + + +def _render(c: dict[str, Any], s: dict[str, Any]) -> tuple[str, str, Path, Path]: + """纯拼装: 拼完整版 markdown + 企微 wecom_md + 写盘。 + + 零选择逻辑。副作用: record_pushed_links + _save_snapshot + save_json + 写文件, + 全部保持原时序(在拼装完成后执行)。 + """ + feed = c["feed"] + now = c["now"] + date_str = c["date_str"] + time_str = c["time_str"] + updated = c["updated"] + trending = c["trending"] + hot = c["hot"] + github_trending = c["github_trending"] + github_emerging = c["github_emerging"] + github_topic = c["github_topic"] + topic_name = c["topic_name"] + news_merged = c["news_merged"] + ai_news = c["ai_news"] + cn_ai_news = c["cn_ai_news"] + ai_news_research = c["ai_news_research"] + wecom_news = c["wecom_news"] + wecom_tech_news = c["wecom_tech_news"] + trending_n = c["trending_n"] + hot_n = c["hot_n"] + github_limit = c["github_limit"] + emerging_limit = c["emerging_limit"] + topic_limit = c["topic_limit"] + prev_ids = s["prev_ids"] + llm_input = s["llm_input"] + featured = s["featured"] + eff_mode = s["eff_mode"] + push_gate = s["push_gate"] + wecom_ai = s["wecom_ai"] + wecom_cn = s["wecom_cn"] + agent_wecom = s["agent_wecom"] + editorial_theme = s["editorial_theme"] + editorial_highlights = s["editorial_highlights"] + themes = s["themes"] + pick_command = s["pick_command"] + pick_why = s["pick_why"] + pick_title = s["pick_title"] + pick_url = s["pick_url"] + board_kwargs = s["board_kwargs"] + gt = s["gt"] + gh = s["gh"] + wecom_github_items = s["wecom_github_items"] + wecom_emerging_items = s["wecom_emerging_items"] + wecom_topic_items = s["wecom_topic_items"] + lines = [ f"# 早报 · {date_str}", "", @@ -814,17 +1058,6 @@ def generate_report() -> tuple[str, str, Path, Path]: lines.append(f"- {ex}") lines.append("") - pick_src = trending[0].get("source", "") if trending else "" - pick_name = trending[0].get("title", "") if trending else "" - pick_command = pick_command_from_featured(featured) or ( - f"npx skills add {pick_src}/{pick_name}" - if pick_src and pick_name - else "npx skills add vercel-labs/skills/find-skills" - ) - pick_why = pick_why_from_featured(featured) or "" - pick_title = str((featured or {}).get("title") or pick_name or "").strip() - pick_url = str((featured or {}).get("url") or "").strip() - lines.extend(["---", "", "## 安装示例", "", "```bash"]) for item in trending[:4]: src, name = item.get("source", ""), item.get("title", "") @@ -833,88 +1066,7 @@ def generate_report() -> tuple[str, str, Path, Path]: lines.extend(["```", "", f"*企微短版见 `output/{date_str}.wecom.md`*"]) markdown = "\n".join(lines) - gt = selected_trending - gh = selected_hot - gt_pad = board_select( - board="skills_trending", - items=trending, - recent_keys=skill_recent, - limit=pad_pool, - pool_size=pool, - kind="skill", - ) - gh_pad = board_select( - board="skills_hot", - items=hot, - recent_keys=skill_recent, - limit=pad_pool, - pool_size=pool, - kind="skill", - ) - wecom_github_items = [_prepare_github_item(item) for item in selected_github] - wecom_emerging_items = [_prepare_github_item(item) for item in selected_emerging] - wecom_topic_items = [_prepare_github_item(item) for item in selected_topic] - github_pad_recent = ( - recent_shown["github_trending"] - | recent_shown["github_emerging"] - | recent_shown["github_topic"] - ) - wecom_github_pad = [ - _prepare_github_item(item) - for item in board_select( - board="github_trending", - items=github_trending, - recent_keys=github_pad_recent, - limit=pad_pool, - pool_size=pool, - kind="github", - ) - ] - wecom_emerging_pad = [ - _prepare_github_item(item) - for item in board_select( - board="github_emerging", - items=github_emerging, - recent_keys=github_pad_recent, - limit=pad_pool, - pool_size=pool, - kind="github", - ) - ] - wecom_topic_pad = [ - _prepare_github_item(item) - for item in board_select( - board="github_topic", - items=github_topic, - recent_keys=github_pad_recent, - limit=pad_pool, - pool_size=pool, - kind="github", - ) - ] - delta_pad = eff_mode == "delta" and wecom_delta_pad() - board_kwargs = { - "mode": eff_mode, - "movement": movement, - "trending": gt, - "hot": gh, - "topic_name": topic_name, - "github_trending": wecom_github_items, - "github_emerging": wecom_emerging_items, - "github_topic": wecom_topic_items, - "wecom_trending": wecom_trending, - "wecom_hot": wecom_hot, - "wecom_github": wecom_github, - "wecom_emerging": wecom_emerging, - "wecom_topic": wecom_topic, - "pad": delta_pad, - "date_str": date_str, - "trending_pad": gt_pad, - "hot_pad": gh_pad, - "github_trending_pad": wecom_github_pad, - "github_emerging_pad": wecom_emerging_pad, - "github_topic_pad": wecom_topic_pad, - } + if agent_wecom: wecom_md = replace_wecom_skill_sections(agent_wecom, **board_kwargs) else: @@ -1000,6 +1152,13 @@ def generate_report() -> tuple[str, str, Path, Path]: return markdown, wecom_md, out_md, out_wecom +def generate_report() -> tuple[str, str, Path, Path]: + """编排三段: 抓取(_collect) → 选择(_select) → 拼装(_render)。""" + collected = _collect(_now_cst().strftime("%Y-%m-%d")) + selected = _select(collected) + return _render(collected, selected) + + def main() -> int: LOG_DIR.mkdir(parents=True, exist_ok=True) log_file = LOG_DIR / f"{_now_cst():%Y-%m-%d}.log"