feat: 早报系统重构与功能增强
- 新增常驻调度器 daily/scheduler.py + run-scheduler.ps1(定时生成/推送) - 新增 daily/bridge_manager.py:Windows 兼容的 Cursor SDK 桥接 - 新增 skills/daily-featured-pick 首推 Skill 与叙事轴/去重逻辑 - 新闻抓取窗口、GitHub 搜索、企微 delta 模式等多项改进 - 补充设计文档与 superpowers 计划/规范 - 新增对应测试(scheduler、featured_pick、github_search、news_fetch_window 等) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -229,6 +229,192 @@ class DeltaFormatTests(unittest.TestCase):
|
||||
self.assertIn("Skills Trending Top 10", text)
|
||||
self.assertNotIn("Skills Trending 变化", text)
|
||||
|
||||
def test_delta_pad_keeps_large_clusters_merged_and_fills_limit(self):
|
||||
"""补榜按 source 合并态取条,大 cluster 不得撑爆 flat 预算导致短榜。"""
|
||||
from daily.format_wecom import build_skills_delta_sections
|
||||
|
||||
def cluster(source: str, n: int, installs: int) -> dict:
|
||||
titles = [f"t{i}" for i in range(n)]
|
||||
return {
|
||||
"id": f"{source}/{titles[0]}",
|
||||
"title": titles[0],
|
||||
"source": source,
|
||||
"installs": installs,
|
||||
"installs_fmt": str(installs),
|
||||
"cluster": True,
|
||||
"cluster_count": n,
|
||||
"cluster_skills": titles,
|
||||
"cluster_titles": ", ".join(titles[:4]) + "…",
|
||||
"link": f"https://skills.sh/{source}/{titles[0]}",
|
||||
"description": f"{source} cluster",
|
||||
}
|
||||
|
||||
full = [cluster(f"big{i}/pkg", 20, 1000 - i) for i in range(1, 5)] + [
|
||||
{
|
||||
"id": f"other{n}/pkg/skill",
|
||||
"title": "skill",
|
||||
"source": f"other{n}/pkg",
|
||||
"installs": 50 - n,
|
||||
"installs_fmt": str(50 - n),
|
||||
"link": f"https://skills.sh/other{n}/pkg/skill",
|
||||
"description": f"other {n}",
|
||||
}
|
||||
for n in range(1, 12)
|
||||
]
|
||||
with patch("daily.format_wecom.localize_brief_descriptions", return_value={}):
|
||||
with patch("daily.format_wecom.needs_chinese", return_value=False):
|
||||
text = build_skills_delta_sections(
|
||||
[],
|
||||
[],
|
||||
trending_full=full,
|
||||
hot_full=[],
|
||||
trending_limit=10,
|
||||
pad=True,
|
||||
)
|
||||
self.assertIn("Skills Trending Top 10", text)
|
||||
self.assertIn("20 skills", text)
|
||||
self.assertIn("other6/pkg", text)
|
||||
|
||||
def test_delta_pad_recent_blocks_same_source_not_just_primary_id(self):
|
||||
"""周去重按 source:换同仓另一个 skill id 不得再上榜。"""
|
||||
from daily.format_wecom import build_skills_delta_sections
|
||||
|
||||
titles = [f"t{i}" for i in range(20)]
|
||||
full = [
|
||||
{
|
||||
"id": f"big/pkg/{titles[0]}",
|
||||
"title": titles[0],
|
||||
"source": "big/pkg",
|
||||
"installs": 999,
|
||||
"installs_fmt": "999",
|
||||
"cluster": True,
|
||||
"cluster_count": 20,
|
||||
"cluster_skills": titles,
|
||||
"cluster_titles": ", ".join(titles[:4]) + "…",
|
||||
"link": f"https://skills.sh/big/pkg/{titles[0]}",
|
||||
"description": "big cluster",
|
||||
},
|
||||
*[
|
||||
{
|
||||
"id": f"other{n}/pkg/skill",
|
||||
"title": "skill",
|
||||
"source": f"other{n}/pkg",
|
||||
"installs": 50 - n,
|
||||
"installs_fmt": str(50 - n),
|
||||
"link": f"https://skills.sh/other{n}/pkg/skill",
|
||||
"description": f"other {n}",
|
||||
}
|
||||
for n in range(1, 12)
|
||||
],
|
||||
]
|
||||
# 昨日展示的是同 source 另一 skill id(非今日 primary)
|
||||
recent = {f"big/pkg/{titles[5]}"}
|
||||
with patch("daily.format_wecom.localize_brief_descriptions", return_value={}):
|
||||
with patch("daily.format_wecom.needs_chinese", return_value=False):
|
||||
text = build_skills_delta_sections(
|
||||
[],
|
||||
[],
|
||||
trending_full=full,
|
||||
hot_full=[],
|
||||
trending_limit=10,
|
||||
pad=True,
|
||||
recent_trending=recent,
|
||||
)
|
||||
self.assertIn("Skills Trending Top 10", text)
|
||||
self.assertNotIn("big/pkg", text)
|
||||
self.assertIn("other1/pkg", text)
|
||||
|
||||
def test_delta_pad_hot_recent_unions_trending_history_by_source(self):
|
||||
"""Skills Hot 周去重合并 Trending 历史:隔日换榜也不能同 source 再出现。"""
|
||||
from daily.format_wecom import build_skills_delta_sections
|
||||
|
||||
hot_full = [
|
||||
{
|
||||
"id": "101-skills/skills/ai-music",
|
||||
"title": "ai-music",
|
||||
"source": "101-skills/skills",
|
||||
"installs": 200,
|
||||
"installs_fmt": "200",
|
||||
"link": "https://skills.sh/101-skills/skills/ai-music",
|
||||
"description": "hot candidate",
|
||||
},
|
||||
{
|
||||
"id": "fresh/src/skill",
|
||||
"title": "skill",
|
||||
"source": "fresh/src",
|
||||
"installs": 100,
|
||||
"installs_fmt": "100",
|
||||
"link": "https://skills.sh/fresh/src/skill",
|
||||
"description": "fresh",
|
||||
},
|
||||
]
|
||||
with patch("daily.format_wecom.localize_brief_descriptions", return_value={}):
|
||||
with patch("daily.format_wecom.needs_chinese", return_value=False):
|
||||
text = build_skills_delta_sections(
|
||||
[],
|
||||
[],
|
||||
trending_full=[],
|
||||
hot_full=hot_full,
|
||||
trending_limit=10,
|
||||
hot_limit=10,
|
||||
pad=True,
|
||||
recent_trending={"101-skills/skills/ai-video-generation"},
|
||||
recent_hot=set(),
|
||||
)
|
||||
self.assertIn("Skills Hot Top 1", text)
|
||||
self.assertIn("fresh/src", text)
|
||||
self.assertNotIn("101-skills", text)
|
||||
|
||||
def test_delta_pad_hot_excludes_trending_by_source(self):
|
||||
"""同日 Hot 补榜按 source 避开 Trending,而非展开全部 cluster skill id。"""
|
||||
from daily.format_wecom import build_skills_delta_sections
|
||||
|
||||
trending_full = [
|
||||
{
|
||||
"id": "same/src/a",
|
||||
"title": "a",
|
||||
"source": "same/src",
|
||||
"installs": 100,
|
||||
"installs_fmt": "100",
|
||||
"link": "https://skills.sh/same/src/a",
|
||||
"description": "trending item",
|
||||
}
|
||||
]
|
||||
hot_full = [
|
||||
{
|
||||
"id": "same/src/b",
|
||||
"title": "b",
|
||||
"source": "same/src",
|
||||
"installs": 90,
|
||||
"installs_fmt": "90",
|
||||
"link": "https://skills.sh/same/src/b",
|
||||
"description": "hot twin",
|
||||
},
|
||||
{
|
||||
"id": "fresh/src/skill",
|
||||
"title": "skill",
|
||||
"source": "fresh/src",
|
||||
"installs": 80,
|
||||
"installs_fmt": "80",
|
||||
"link": "https://skills.sh/fresh/src/skill",
|
||||
"description": "fresh hot",
|
||||
},
|
||||
]
|
||||
with patch("daily.format_wecom.localize_brief_descriptions", return_value={}):
|
||||
with patch("daily.format_wecom.needs_chinese", return_value=False):
|
||||
text = build_skills_delta_sections(
|
||||
[],
|
||||
[],
|
||||
trending_full=trending_full,
|
||||
hot_full=hot_full,
|
||||
trending_limit=10,
|
||||
hot_limit=10,
|
||||
pad=True,
|
||||
)
|
||||
self.assertIn("Skills Hot Top 1", text)
|
||||
self.assertIn("fresh/src", text)
|
||||
self.assertNotIn("same/src/b", text)
|
||||
|
||||
def test_delta_pad_uses_large_pool_when_recent_excludes_top(self):
|
||||
from daily.format_wecom import build_skills_delta_sections
|
||||
|
||||
@@ -306,6 +492,46 @@ class DeltaFormatTests(unittest.TestCase):
|
||||
self.assertIn("GitHub Trending Top 10", text)
|
||||
self.assertNotIn("GitHub Trending 变化", text)
|
||||
|
||||
def test_delta_pad_github_unions_recent_across_boards(self):
|
||||
"""GitHub 三榜共用周去重:Trending 出过的 repo,新兴/Topic 不得再出。"""
|
||||
from daily.format_wecom import build_github_delta_sections
|
||||
|
||||
movement = {"github_trending_moves": [], "github_emerging_moves": [], "github_topic_moves": []}
|
||||
shared = {
|
||||
"repo": "seen/repo",
|
||||
"url": "https://github.com/seen/repo",
|
||||
"language": "Go",
|
||||
"stars_today_fmt": "100",
|
||||
"total_stars_fmt": "1K",
|
||||
"created_at": "2026-07-01",
|
||||
"description": "already shown",
|
||||
"desc_short": "already shown",
|
||||
}
|
||||
fresh = {
|
||||
"repo": "fresh/repo",
|
||||
"url": "https://github.com/fresh/repo",
|
||||
"language": "Go",
|
||||
"stars_today_fmt": "90",
|
||||
"total_stars_fmt": "900",
|
||||
"created_at": "2026-07-02",
|
||||
"description": "fresh",
|
||||
"desc_short": "fresh",
|
||||
}
|
||||
with patch("daily.format_wecom.localize_brief_descriptions", return_value={}):
|
||||
text = build_github_delta_sections(
|
||||
movement,
|
||||
topic_name="llm",
|
||||
github_trending=[],
|
||||
github_emerging=[shared, fresh],
|
||||
github_topic=[shared],
|
||||
emerging_limit=5,
|
||||
topic_limit=5,
|
||||
pad=True,
|
||||
recent_board_keys={"github_trending": {"seen/repo"}},
|
||||
)
|
||||
self.assertIn("fresh/repo", text)
|
||||
self.assertNotIn("seen/repo", text)
|
||||
|
||||
def test_delta_pad_skips_recent_skills(self):
|
||||
from daily.format_wecom import build_skills_delta_sections
|
||||
|
||||
@@ -320,6 +546,16 @@ class DeltaFormatTests(unittest.TestCase):
|
||||
"description": f"skill {i}",
|
||||
}
|
||||
for i in range(4, 6)
|
||||
] + [
|
||||
{
|
||||
"id": "fresh/src/skill",
|
||||
"title": "skill",
|
||||
"source": "fresh/src",
|
||||
"installs": 50,
|
||||
"installs_fmt": "50",
|
||||
"link": "https://skills.sh/fresh/src/skill",
|
||||
"description": "fresh skill",
|
||||
}
|
||||
]
|
||||
with patch("daily.format_wecom.localize_brief_descriptions", return_value={}):
|
||||
with patch("daily.format_wecom.needs_chinese", return_value=False):
|
||||
@@ -330,10 +566,12 @@ class DeltaFormatTests(unittest.TestCase):
|
||||
hot_full=[],
|
||||
trending_limit=10,
|
||||
pad=True,
|
||||
# 同仓历史 skill id → 整仓 source 去重;仅保留其它 source
|
||||
recent_trending={f"x/y/s{i}" for i in range(1, 4)},
|
||||
)
|
||||
self.assertIn("Skills Trending Top 1", text)
|
||||
self.assertIn("2 skills", text)
|
||||
self.assertIn("fresh/src", text)
|
||||
self.assertNotIn("x/y", text)
|
||||
|
||||
def test_delta_pad_skips_recent_github(self):
|
||||
from daily.format_wecom import build_github_delta_sections
|
||||
@@ -363,6 +601,45 @@ class DeltaFormatTests(unittest.TestCase):
|
||||
self.assertIn("org/r4", text)
|
||||
self.assertNotIn("org/r1", text)
|
||||
|
||||
def test_delta_pad_skips_recent_github_moves(self):
|
||||
"""异动新入榜若昨日企微已展示,pad 时仍应排除(不只滤补榜)。"""
|
||||
from daily.format_wecom import build_github_delta_sections
|
||||
|
||||
movement = {
|
||||
"github_trending_moves": [
|
||||
{
|
||||
"repo": "vinta/awesome-python",
|
||||
"url": "https://github.com/vinta/awesome-python",
|
||||
"language": "Python",
|
||||
"total_stars_fmt": "308K",
|
||||
"description": "list",
|
||||
}
|
||||
],
|
||||
"github_emerging_moves": [],
|
||||
"github_topic_moves": [],
|
||||
}
|
||||
full = [
|
||||
{
|
||||
"repo": "fresh/repo",
|
||||
"url": "https://github.com/fresh/repo",
|
||||
"language": "Go",
|
||||
"total_stars_fmt": "1K",
|
||||
"description": "fresh",
|
||||
"desc_short": "fresh",
|
||||
}
|
||||
]
|
||||
with patch("daily.format_wecom.localize_brief_descriptions", return_value={}):
|
||||
text = build_github_delta_sections(
|
||||
movement,
|
||||
topic_name="llm",
|
||||
github_trending=full,
|
||||
trending_limit=10,
|
||||
pad=True,
|
||||
recent_board_keys={"github_trending": {"vinta/awesome-python"}},
|
||||
)
|
||||
self.assertIn("fresh/repo", text)
|
||||
self.assertNotIn("vinta/awesome-python", text)
|
||||
|
||||
def test_load_recent_board_keys_from_data_json(self):
|
||||
import json
|
||||
import tempfile
|
||||
|
||||
Reference in New Issue
Block a user