feat: 新增企微已推送新闻 link 去重缓存

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-09 11:33:26 +08:00
parent aff75141cb
commit ba8631c867
3 changed files with 117 additions and 3 deletions

View File

@@ -46,7 +46,7 @@ def _cn_enabled() -> bool:
def _hours_window() -> int:
return max(1, env_int("DAILY_AI_NEWS_HOURS", 72))
return max(1, env_int("DAILY_AI_NEWS_HOURS", 24))
def _per_feed_limit() -> int:
@@ -497,7 +497,7 @@ def _format_news_section(
return lines
def prepare_wecom_news_items(news: dict[str, Any]) -> list[dict[str, Any]]:
def prepare_wecom_news_items(news: dict[str, Any], *, date_str: str | None = None) -> list[dict[str, Any]]:
if not news.get("enabled"):
return []
limit = _wecom_limit()
@@ -528,10 +528,14 @@ def prepare_wecom_news_items(news: dict[str, Any]) -> list[dict[str, Any]]:
"desc_short": _clean_text(item.get("summary", ""), 36),
}
)
if date_str:
from daily.news.pushed_links import filter_unpushed_items
items = filter_unpushed_items(items, date_str=date_str)
return items
def prepare_wecom_cn_news_items(news: dict[str, Any]) -> list[dict[str, Any]]:
def prepare_wecom_cn_news_items(news: dict[str, Any], *, date_str: str | None = None) -> list[dict[str, Any]]:
if not news.get("enabled"):
return []
limit = _wecom_cn_limit()
@@ -577,4 +581,8 @@ def prepare_wecom_cn_news_items(news: dict[str, Any]) -> list[dict[str, Any]]:
"desc_short": _clean_text(item.get("summary", ""), 36),
}
)
if date_str:
from daily.news.pushed_links import filter_unpushed_items
items = filter_unpushed_items(items, date_str=date_str)
return items

View File

@@ -0,0 +1,87 @@
"""已推送企微早报的新闻 link 去重缓存。"""
from __future__ import annotations
import json
from datetime import datetime, timedelta
from pathlib import Path
from typing import Any
from daily.config import CACHE_DIR, news_dedup_days
from daily.news.fetch import _normalize_link
def _cache_path() -> Path:
return CACHE_DIR / "pushed-news-links.json"
def _load_raw() -> dict[str, Any]:
path = _cache_path()
if not path.exists():
return {"dates": {}}
try:
data = json.loads(path.read_text(encoding="utf-8"))
except (OSError, ValueError):
return {"dates": {}}
if not isinstance(data.get("dates"), dict):
return {"dates": {}}
return data
def _save_raw(data: dict[str, Any]) -> None:
path = _cache_path()
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
def _prune(data: dict[str, Any], *, keep_days: int) -> None:
dates: dict[str, list[str]] = data.setdefault("dates", {})
try:
anchor = max(datetime.strptime(d, "%Y-%m-%d") for d in dates)
except ValueError:
return
cutoff = anchor - timedelta(days=keep_days)
for key in list(dates.keys()):
try:
if datetime.strptime(key, "%Y-%m-%d") < cutoff:
dates.pop(key, None)
except ValueError:
dates.pop(key, None)
def load_pushed_link_set() -> set[str]:
data = _load_raw()
out: set[str] = set()
for links in (data.get("dates") or {}).values():
if isinstance(links, list):
out.update(str(x) for x in links if x)
return out
def filter_unpushed_items(
items: list[dict[str, Any]],
*,
date_str: str,
) -> list[dict[str, Any]]:
del date_str # reserved for per-day scoping if needed later
seen = load_pushed_link_set()
out: list[dict[str, Any]] = []
for item in items:
link = _normalize_link(str(item.get("link") or ""))
if not link or link in seen:
continue
out.append(item)
return out
def record_pushed_links(date_str: str, links: list[str]) -> None:
data = _load_raw()
dates: dict[str, list[str]] = data.setdefault("dates", {})
normalized: list[str] = []
for link in links:
clean = _normalize_link(link)
if clean:
normalized.append(clean)
dates[date_str] = sorted(set(normalized))
_prune(data, keep_days=news_dedup_days())
_save_raw(data)